Android WebView 独立进程解决方案 (1),flutter 推送通知
image.png
IBinderPool: Webview 进程和主进程的通讯可能涉及到多个 AIDL Binder,从功能上来讲,我们也会把不同功能的接口写成不同的 AIDL Binder,所以 IBinderPool 用于满足调用方根据不同类型获取不同的 Binder。
interface IBinderPool {IBinder queryBinder(int binderCode); //查找特定 Binder 的方法}
IWebAidlInterface: 最核心的 AIDL Binder,这里把 WebView 进程对主进程的每一个调用看做一次 action, 每个 action 都会有唯一的 actionName, 主进程会提前注册好这些 action,action 也有级别 level,每次调用结束通过 IWebAidlCallback 返回结果
interface IWebAidlInterface {
/**
actionName: 不同的 action, jsonParams: 需要根据不同的 action 从 map 中读取并依次转成其他*/void handleWebAction(int level, String actionName, String jsonParams, in IWebAidlCallback callback);
}
IWebAidlCallback: 结果回调
interface IWebAidlCallback {void onResult(int responseCode, String actionName, String response);}
为了维护独立进程和主进程之间的连接,避免每次 aidl 调用时都去重新进行 binder 连接和获取,需要专门提供一个类去维护连接,并根据条件返回 Binder. 这个类就叫做 RemoteWebBinderPool
public class RemoteWebBinderPool {
public static final int BINDER_WEB_AIDL = 1;
private Context mContext;private IBinderPool mBinderPool;private static volatile RemoteWebBinderPool sInstance;private CountDownLatch mConnectBinderPoolCountDownLatch;
private RemoteWebBinderPool(Context context) {mContext = context.getApplicationContext();connectBinderPoolService();}
public static RemoteWebBinderPool getInstance(Context context) {if (sInstance == null) {synchronized (RemoteWebBinderPool.class) {if (sInstance == null) {sInstance = new RemoteWebBinderPool(context);}}}return sInstance;}
private synchronized void connectBinderPoolService() {mConnectBinderPoolCountDownLatch = new CountDownLatch(1);Intent service = new Intent(mContext, MainProHandleRemoteService.class);mContext.bindService(service, mBinderPoolConnection, Context.BIND_AUTO_CREATE);try {mConnectBinderPoolCountDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}}
public IBinder queryBinder(int binderCode) {IBinder binder = null;try {if (mBinderPool != null) {binder = mBinderPool.queryBinder(binderCode);}} catch (RemoteException e) {e.printStackTrace();}return binder;}
private ServiceConnection mBinderPoolConnection = new ServiceConnection() { // 5
@Overridepublic void onServiceDisconnected(ComponentName name) {
}
@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mBinderPool = IBinderPool.Stub.asInterface(service);try {mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);} catch (RemoteException e) {e.printStackTrace();}mConnectBinderPoolCountDownLatch.countDown();}};
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() { // 6@Overridepublic void binderDied() {mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);mBinderPool = null;connectBinderPoolService();}};
public static class BinderPoolImpl extends IBinderPool.Stub {
private Context context;
public BinderPoolImpl(Context context) {this.context = context;}
@Overridepublic IBinder queryBinder(int binderCode) throws RemoteException {IBinder binder = null;switch (binderCode) {case BINDER_WEB_AIDL: {binder = new MainProAidlInterface(context);break;}default:break;}return binder;}}
}
从代码中可以看到这个连接池连接的是主进程 MainProHandleRemoteService.
public class MainProHandleRemoteService extends Service {@Nullable@Overridepublic IBinder onBind(Intent intent) {Binder mBinderPool = new RemoteWebBinderPool.BinderPoolImpl(context);return mBinderPool;}}
Native-Web 交互和接口管理
一次完整的 Web 页面和 Native 交互过程是这样的:
Native 打开页面时注册接口:“webView.addJavascriptInterface(jsInterface, "webview");” 其中 jsInterface 是 JsRemoteInterface 类的实例:
public final class JsRemoteInterface {@JavascriptInterfacepublic void post(String cmd, String param) {...}
Web 页面通过“window.webview.post(cmd,JSON.stringify(para))”调用 native;
Native(即 Webview 进程)收到调用之后,通过 IWebAidlInterface 实例传递给主进程执行;
主进程收到 action 请求之后,根据 actionname 分发处理,执行结束之后通过 IWebAidlCallback 完成进程间回调。
其中,通用的 Action 结构如下:
public interface Command {
String name();
void exec(Context context, Map params, ResultBack resultBack);}
根据不同的 Level 将所有的 command 提前注册好, 以 BaseLevelCommand 为例:
public class BaseLevelCommands {
private HashMap<String, Command> commands;private Context mContext;
public BaseLevelCommands(Context context) {this.mContext = context;registerCommands();}
private void registerCommands() {commands = new HashMap<>();registerCommand(playVideoByNativeCommand);}
private Command playVideoByNativeCommand = new Command() {@Overridepublic String name() {return "videoPlay";}
@Overridepublic void exec(Context context, Map params, ResultBack resultBack) {if (params != null) {String videoUrl = (String) params.get("url");
if (!TextUtils.isEmpty(videoUrl)) {String suffix = videoUrl.substring(videoUrl.lastIndexOf(".") + 1);DJFullScreenActivity.startActivityWithLanscape(context, videoUrl, DJFullScreenActivity.getVideoType(suffix), DJVideoPlayer.class, " ");}}}};}
评论