大家好,我系苍王。
以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章。
[Android]如何做一个崩溃率少于千分之三噶应用app--章节列表
Android组件化架构热卖中更多深入的开发技巧可以关注我的小专栏《Android进程化开发》,专栏里会提供本篇加强的版本。
大家应该都有或多或少都会接触到Webview的封装和开发,一般都是在默认进程中开启一个Web的Activity来做操作。但是近来越来越多人会偏向于将Webview单独一个进程来做处理。
WebView进程的好处:
1.不占用主进程内存,减少主进程内存压力,减少系统强制回收的可能性。
2.Webview如果存在内存泄露,内存会更加宽裕,只要关闭就了事了,不会对主进程有过多影响。
3.进程隔离,如果出现崩溃不会相互影响,并且可以做一些拉活操作。
Android开启单独进程的方式,在以前的进程化已经介绍过了,WebView通过依赖于Activity来做单独进程,开启Activity单独进程,只要在AndroidMainfest中声明Activity的时候添加android:process=":xxx"属性
哈哈,这些简单的介绍,估计大家都懂,接下来就是展示真正技术的时候了。
JSBridge原理
JsBridge,如果大家有做过Webview和客户端的js调用,相信大家都会接触这个框架,这个框架兼容android和ios端,提供了js对android和ios端的互相调用方案。
因为WebView是单独进程的,其实大家都不喜欢WebView和其他业务过分耦合,那么就需要让WebView和jsbridge做一些封装。
下面是jsbridge原理,jsbridge通过注入js调用代码到html里面,html中通过调用相应函数,然后jsbridge通过shouldOverrideUrlloading来拦截对应拼接的字符串,然后回调到注册触发的webview注册的registerHandler函数完成方法实现。
说一下两点jsbridge使用需要注意的地方。
1.Webview必须注册BridgeWebViewClient。
2.onPageFinished和shouldOverrideUrlLoading覆写时,一定要调用supper的方法,不然注入和回调将无法触发。
JsBridge基本回调框架建立
我们现在要做的controller到webview的封装,这里只是共同进程里面的封装。
- 先建立一个业务类注册的jsbridge管理类
data class JsBean(val functionName: String, val url: String?, val result: (context: Context?, data: String, data2: String, callBack: IJavaScriptCallBack?) -> String?)
object JavaScriptInterface {
val set: MutableSet<JsBean> = HashSet()
//jsbridge接收的scheme名
val SCHEME_NAME = "eating"
var context: Context? = null
fun register(bean: JsBean) {
set.add(bean)
}
fun unregister(bean: JsBean) {
set.remove(bean)
}
}
2.业务初始化时调用JsBridge的注册
JavaScriptInterface.register(JavaScriptInterface.JsBean(
"groupgame",
""
) { context, data, data2, callback ->
……自身逻辑……
data //返回值
})
3.编写jsbridge注册回调
//非跨进程使用,fragment或webview使用
fun inject(context: Context, url: String, webView: NestedScrollWebView) {
//html调用得方法名为sendAction
webView.registerHandler("sendAction") { data, function ->
//解析为uri
val uri = Uri.parse(data)
//比对scheme名
if (uri.scheme == SCHEME_NAME) {
//比对想要触发的方法名
set.filter { uri.authority == it.functionName }
//调用jsbean触发的方法
.mapNotNull { it.result(context, uri.pathSegments[0], "", null) }
//回调给html
.forEach { function.onCallBack(it) }
}
}
}
4.在webViewActivity初始化的时候注册
JavaScriptInterface.inject(context, url, web)
Jsbridge独立进程封装
这里当然是要调用aidl的,之后优化的话封装Binder。
跨进程jsbridge.png
jsbridge到web的部分是共通的,流程图中就不展示了。
1.编写aidl接口
//关闭web页的接口,回调给webActivity的实现
interface IJavaScriptCallBack {
void callbackFinish();
}
//javaScript的分发到业务controller的实现
interface IJavaScriptlInterface {
// void handler(String data);
void handler(String data,IJavaScriptCallBack callback);
}
2.创建一个JavaScriptService
class JavaScriptService : Service() {
companion object {
val TAG = JavaScriptService::class.java.simpleName
}
val mBinder = object : IJavaScriptlInterface.Stub() {
override fun handler(data: String?, callBack: IJavaScriptCallBack?) {
if (data != null)
//jsbridge分发给不同业务
JavaScriptInterface.filterData(data, callBack)
}
}
override fun onBind(intent: Intent?): IBinder {
return mBinder
}
}
3.WebActivity绑定启动
private val serviceConn = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
isServiceConnect = false
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
//获取aidl
jsbController = IJavaScriptlInterface.Stub.asInterface(service)
val url = intent.getStringExtra(ARouterConstants.PARAM.WEB_URL) ?: return
//添加js框架
JavaScriptInterface.inject(this@WebActivity, url, web, jsbController, callback)
isServiceConnect = true
}
}
private fun bindService() {
val intent = Intent(this, JavaScriptService::class.java)
bindService(intent, serviceConn, Context.BIND_AUTO_CREATE)
}
4.Jsbridge绑定函数
//WebActivity跨进程专用回调
fun inject(activity: AppCompatActivity, url: String, webView: NestedScrollWebView, jsbController: IJavaScriptlInterface?, callBack: IJavaScriptCallBack?) {
//web进程内调用
webView.registerHandler("sendAction") { data, function ->
val uri = Uri.parse(data)
if (uri.scheme == SCHEME_NAME && set.size > 0) {
set.filter { uri.authority == it.functionName }
.mapNotNull { it.result(activity, uri.pathSegments[0], "", callBack) }
.forEach { function.onCallBack(it) }
}
//aidl分发到不同业务
jsbController?.handler(data, callBack)
}
}
5.分发的调用触发
//WebActivity跨进程回调触发
fun filterData(data: String, callBack: IJavaScriptCallBack?) {
val uri = Uri.parse(data)
if (uri.scheme == SCHEME_NAME && set.size > 0) {
set.filter { uri.authority == it.functionName }
//这里获取到最顶层的activity,因为aidl无法传递context对象
.mapNotNull { it.result(AmeTopActivity.current(), uri.pathSegments[0], "", callBack) }
}
}
基本的进程回调就到这里了。
Android组件化群1 Android组件化群2
网友评论