美文网首页Android技术知识Android开发经验谈Android开发
[Android]你不知道的Android进程化(7)--Web

[Android]你不知道的Android进程化(7)--Web

作者: CangWang | 来源:发表于2019-02-01 16:53 被阅读171次

大家好,我系苍王。
以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章。

[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调用原理.png

说一下两点jsbridge使用需要注意的地方。
1.Webview必须注册BridgeWebViewClient。
2.onPageFinished和shouldOverrideUrlLoading覆写时,一定要调用supper的方法,不然注入和回调将无法触发。

JsBridge基本回调框架建立

我们现在要做的controller到webview的封装,这里只是共同进程里面的封装。

  1. 先建立一个业务类注册的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

相关文章

网友评论

    本文标题:[Android]你不知道的Android进程化(7)--Web

    本文链接:https://www.haomeiwen.com/subject/qohdsqtx.html