写点什么

Android WebView 独立进程解决方案,手撕面试官

用户头像
Android架构
关注
发布于: 刚刚

Android 多进程的通讯方式有很多种,主要的方式有以下几种:


  1. AIDL

  2. Messenger

  3. ContentProvider

  4. 共享文件

  5. 组件间 Bundle 传递

  6. Socket 传输


考虑到 WebView 主要的通讯方式就是方法调用,所以采用 AIDL 方式。AIDL 本质采用的是 Binder 机制,这里贴一张网上的 Binder 机制原理图,具体的 AIDL 的使用方式这里不赘述, 以下是几个核心 AIDL 文件



image.png


IBinderPool: Webview 进程和主进程的通讯可能涉及到多个 AIDL Binder,从功能上来讲,我们也会把不同功能的接口写成不同的 AIDL Binder,所以 IBinderPool 用于满足调用方根据不同类型获取不同的 Binder。


interface IBinderPool {IBinder queryBinder(int binderCode);


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


//查找特定 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 交互过程是这样的:


  1. Native 打开页面时注册接口:“webView.addJavascriptInterface(jsInterface, "webview");” 其中 jsInterface 是 JsRemoteInterface 类的实例:


public final class JsRemoteInterface {@JavascriptInterfacepublic void post(String cmd, String param) {...}


  1. Web 页面通过“window.webview.post(cmd,JSON.stringify(para))”调用 native;

  2. Native(即 Webview 进程)收到调用之后,通过 IWebAidlInterface 实例传递给主进程执行;

  3. 主进程收到 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) {

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android WebView独立进程解决方案,手撕面试官