我正在参与掘金创作者训练营第4期,点击了解活动详情,一起学习吧!
本文将按照 Capacitor Android 执行逻辑来分析,只关注流程,不涉及所有代码。基于 Your First Ionic App: Vue 构建,在阅读之前建议自行搭建环境,创建 photo-gallery 项目,最终使用 ionic cap open android
命令打开 Android 项目。
一、找到入口类
Android 页面信息配置在 AndroidManifest.xml 中,打开后,找到 activity
节点,其中包含 action
为 MAIN
和 category
为 LAUNCHER
的就是入口 activity。photo-gallary 工程只有一个 activity,即 MainActivity。
public class MainActivity extends BridgeActivity {}
MainActivity 继承自 BridgeActivity,不需要额外增加代码,便可完成 capacitor 的初始化。
二、BridgeActivity
2.1 onCreate()
在 onCreate 中加载布局文件 bridge_layout_main.xml,在布局文件中有一个ID为 webview 的控件,用于显示网页信息。
2.2 init()
用于初始化插件,目前被标为 @Deprecated 说明有新的api替代,不是我们分析的重点。
2.3 registerPlugin()
init() 方法标为过时,推荐使用 registerPlugin 来加载插件,photo-gallary 工程中没有用到,暂时放一下,回头再说。
2.4 onStart()
onStart 是 activity 生命周期的第二个方法,在这个方法中对 Bridge 进行初始化。
@Override public void onStart() {
super.onStart();
if (bridge == null) {
PluginManager loader = new PluginManager(getAssets());
bridgeBuilder.addPlugins(loader.loadPluginClasses());
this.load();
}
}
这个方法一共做了两件事,1. 加载系统插件,2. 创建 Bridge 对象。
-
loadPluginClasses()
读取 assets 下的
capacitor.plugins.json
文件,解析出classpath
字段,通过反射获取 Class 对象。 -
load()
使用 Builder 模式构建 Bridge 对象。
小结:将插件配置信息放到 assets 目录下,新增插件只需要同步更新配置文件即可,程序中不需要做任何修改,这是插件与 SDK 的解耦。
三、Bridge
Bridge 是 Capacitor Android 源码的核心,由它延伸出的 Plugin 更是 Capacitor Android 源码的精华所在。我们一步步来分析。
3.1 create()
在 create() 方法中,对 Bridge 进行初始化,这里主要涉及 webView 的获取,我们是从 BridgeActivity 过来的,所以这里是通过 activity.findViewById(R.id.webview)
取到 webView。Bridge 构造方法依次调用了下面三个方法。
3.2 initWebView()
initWebView 主要对 WebView 进行初始化。如:是否可以执行 javascript 脚本,这里肯定要开启的,不然怎么交互呢(执行不到 @JavascriptInterface 注解方法)。还有一个关键点是 setWebContentsDebuggingEnabled ,只有该方法设置为 true 时,才能通过 chrome 调试(chrome://inspect)。
3.3 registerAllPlugins()
遍历 initialPlugins 列表,解析每一个插件,这个在下一节介绍。
3.4 loadWebView()
- 生成 appUrl
- 创建 WebViewLocalServer,主要代理 WebViewClient.shouldInterceptRequest() 方法,用于区分哪些读取本地文件。
- 设置 WebViewClient,请求的拦截,页面加载完毕等回调。
- 加载 url
Capacitor Android 都是以 Bridge 展开的,想要获取插件,从 Bridge 中取,想要获取上下文信息,从 Bridge 中取,真是见字如面啊。
四、插件机制
在 registerPlugin() 方法中,获取插件上的 @CapacitorPlugin 注解,得到插件id,并将它缓存到 plugins 中,map 的 value 为封装的插件类 PluginHandle。
PluginHandle 构造方法中,收集所有被打上 @PluginMethod 注解的方法,并将它缓存到 pluginMethods 中,map 的 value 为封装的插件方法类 PluginMethodHandle。
registerPlugin() 方法最后,通过反射实例化了 Plugin 对象。
这个操作有点类似 Retrofit,都是利用注解规范了方法的实现。区别是 Capacitor 中没有用动态代码。
五、消息处理器
initWebView() 执行完毕,创建了 MessageHandler 对象,这是 hybrid App 的交互中枢,有了它才能实现 JS 与 Native 之间的互相调用。
MessageHandler 构造方法中,将当前类设置为 addJavascriptInterface 方法,并设置名为:androidBridge。
5.1 postMessage
postMessage,唯一被打上 @JavascriptInterface 注解的方法,说明它是一个路由方法,解析出参数,交给 callPluginMethod() 干活。
private void callPluginMethod(String callbackId, String pluginId,
String methodName, JSObject methodData) {
PluginCall call = new PluginCall(this, pluginId, callbackId, methodName, methodData);
bridge.callPluginMethod(pluginId, methodName, call);
}
callPluginMethod 将参数组装成 PluginCall 对象,并传给 bridge 对象。
5.2 callPluginMethod
- MessageHandler 中的 callPluginMethod 方法,将 请求参数封装成 PluginCall 对象,并将该请求传递到 bridge.callPluginMethod()。
- Bridge.callPluginMethod 通过 pluginId 获取指定的 PluginHandle,没找到则使用 call.errorCallback() 反馈给前端。找到的话,执行该插件的 invoke 方法。
- PluginHandle.invoke 先判断 Plugin 实例是否存在,不存在,调用 load 去反射创建对象。存在,则通过方法名去 pluginMethods 中查找 PluginMethodHandle 对象,最终通过反射执行该方法。
到这里,完成了 js 到 native 层的方法执行。
5.3 PluginCall
PluginCall 是一个模型类,它将网页端的方法进行了抽象化:
private final String pluginId;
private final String methodName;
private final JSObject data;
从这几个成员变量可以看出,它将一类方法进行了模块化,每一个模块即是一个插件。操作的结果通过 msgHandler.sendResponseMessage(); 投递到 MessageHandler 中,并将返回的数据封装为 PluginResult 对象。最后执行 webView.evaluateJavascript() 方法,完成 native 到 js 层的回调。
final String runScript = "window.Capacitor.fromNative(" + data.toString() + ")";
final WebView webView = this.webView;
webView.post(() -> webView.evaluateJavascript(runScript, null));
第一行代码表示,在 Capacitor 下有一个 fromNative 方法,将它挂载到 window 对象,这样便可以接收 native 返回的数据了。
小结:postMessage 收到 js 消息后,首先根据 pluginId 去找插件,找到后,再根据 methodName 去找对应的方法,最终通过反射执行该方法。
六、总结
本篇按照插件的加载流程,插件的执行流程展开,并没有涉及所有类。
整个 Capacitor Android 代码清晰易懂,各类业务被封装成了独立的插件,通过 Bridge 对象进行管理。只要使用过 Retrofit,并且有 js 与 native 交互经验的同学,都可以顺利掌握。通读源码后,我们要提高警惕,避免写重复的代码(在本篇指 @JavascriptInterface 不要重复出现),要学会抽象思维,即:如何将分散的逻辑统一成一套可服用的模型。再结合国内生态,想要开发出一套自用的小程序bridge框架不是难事。
Ps: 当我们通过 npm install @capacitor/app
命令安装插件后,刷新 android 工程,会自动将插件的入口信息写入 capacitor.plugins.json
文件。如果是我们自定义的插件,可以通过 registerPlugin() 来加载。