Android WebView开发(四)WebView独立进程解决方案
一、Android WebView开发(一):基础应用
二、Android WebView开发(二):WebView与Native交互
三、Android WebView开发(三):WebView性能优化
四、Android WebView开发(四):WebView独立进程解决方案
五、Android WebView开发(五):自定义WebView工具栏
附GitHub源码:WebViewExplore
一、WebView面临的问题:
1、WebView导致的内存泄漏问题,及后续引起的OOM问题。
2、Android版本不同,采用了不同的内核,兼容性Crash。
3、WebView代码质量,WebView和Native版本不一致,导致Crash。
二、WebView独立进程的实现:
WebView独立进程的实现比较简单,只需要在AndroidManifest中找到对应的WebViewActivity,对其配置"android: process"属性即可。如下:
-
<!--独立进程WebViewActivity-->
-
<activity
-
android:name=".SingleProcessActivity"
-
android:configChanges="orientation|keyboardHidden|screenSize"
-
android:process=":remoteWeb" />
我们可以通过 adb shell ps|grep com.hongri.webview 指令查看验证,添加独立进程前后的对进程的打印情况:
可以看到已经在主进程的基础上,新生成了一个remoteWeb独立进程。
有两个进程就必然会涉及到进程间的通信,所以最终涉及到的交互及通信包括:
前端与Native端、独立进程与主进程间的通信。
后面会通过下面的demo逐个介绍,如下图独立进程中的SingleProcessActivity页面加载了一个本地的html前端页面,页面包含两个按钮,点击后分别最终会调用主进程的showToast方法doCalculate方法,从而实现前端及进程间的交互。
1、前端与Native端的交互:
[可参考:二、WebView与Native的交互]
(1)、注册JS映射接口,并实现:
加载本地的一个remote_web.html文件:
public static final String CONTENT_SCHEME = "file:///android_asset/remote/remote_web.html";
-
//允许js交互
-
webSettings.setJavaScriptEnabled(true);
-
JsRemoteInterface remoteInterface = new JsRemoteInterface();
-
remoteInterface.setListener(this);
-
//注册JS交互接口
-
mWebView.addJavascriptInterface(remoteInterface, "webview");
-
mWebView.loadUrl(CONTENT_SCHEME);
其中 JsRemoteInterface 为对应的前端交互映射类,源码如下:
-
/**
-
* Create by zhongyao on 2021/12/14
-
* Description: 前端交互映射类
-
*/
-
public class JsRemoteInterface {
-
-
private static final String TAG = "JsRemoteInterface";
-
private final Handler mHandler = new Handler();
-
private IRemoteListener listener;
-
-
/**
-
* 前端调用方法
-
* @param cmd
-
* @param param
-
*/
-
-
public void post(final String cmd, final String param) {
-
mHandler.post(new Runnable() {
-
-
public void run() {
-
if (listener != null) {
-
try {
-
listener.post(cmd, param);
-
} catch (RemoteException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
});
-
}
-
-
public void setListener(IRemoteListener remoteListener) {
-
listener = remoteListener;
-
}
-
}
(2)、前端关键代码实现:
- remote_web.html:
-
</script>
-
<div class="item" style="font-size: 18px; color: #ffffff" onclick="callAppToast()">调用: showToast</div>
-
<div class="item" style="font-size: 18px; color: #ffffff" onclick="callCalculate()">调用: appCalculate</div>
-
<script src="remote_web.js" charset="utf-8"></script>
-
<script>
-
function callAppToast() {
-
dj.post("showToast", {message: "this is action from html"});
-
}
-
-
function callCalculate() {
-
dj.postWithCallback("appCalculate", {firstNum: "1", secondNum: "2"}, function(res) {
-
dj.post("showToast", {message: JSON.stringify(res)});
-
});
-
}
-
</script>
- remote_web.js:
-
dj.post = function(cmd,para){
-
if(dj.os.isIOS){
-
var message = {};
-
message.meta = {
-
cmd:cmd
-
};
-
message.para = para || {};
-
window.webview.post(message);
-
}else if(window.dj.os.isAndroid){
-
window.webview.post(cmd,JSON.stringify(para));
-
}
-
};
-
dj.postWithCallback = function(cmd,para,callback,ud){
-
var callbackname = dj.callbackname();
-
dj.addCallback(callbackname,callback,ud);
-
if(dj.os.isIOS){
-
var message = {};
-
message.meta = {
-
cmd:cmd,
-
callback:callbackname
-
};
-
message.para = para;
-
window.webview.post(message);
-
}else if(window.dj.os.isAndroid){
-
para.callback = callbackname;
-
window.webview.post(cmd,JSON.stringify(para));
-
}
-
};
2、独立进程与主进程的交互:
(1)、定义一个AIDL文件 CalculateInterface:
并将暴露给其他进程的方法在该文件中声明,如下图:
需要注意的是,该文件的包名需要跟java文件夹下的源码的主包名一致。
aidl文件源码如下,定义了上面描述的两个方法:
-
// CalculateInterface.aidl
-
package com.hongri.webview;
-
-
// Declare any non-default types here with import statements
-
-
interface CalculateInterface {
-
double doCalculate(double a, double b);
-
void showToast();
-
}
(2)、主进程中定义一个远程服务RemoteService【可看做Server】:
此服务用来监听客户端的连接请求,并实现该AIDL接口,完整代码如下:
-
/**
-
* @author hongri
-
* @description 远程Service【Server】
-
* 【此Service位于主进程中】
-
*/
-
public class RemoteService extends Service {
-
-
private static final String TAG = "RemoteService";
-
-
-
public IBinder onBind(Intent arg0) {
-
return mBinder;
-
}
-
-
-
public boolean onUnbind(Intent intent) {
-
return super.onUnbind(intent);
-
}
-
-
-
public void onCreate() {
-
super.onCreate();
-
}
-
-
-
public int onStartCommand(Intent intent, int flags, int startId) {
-
return super.onStartCommand(intent, flags, startId);
-
}
-
-
-
public void onDestroy() {
-
super.onDestroy();
-
}
-
-
-
private final CalculateInterface.Stub mBinder = new CalculateInterface.Stub() {
-
/**
-
* remoteWeb进程调用主进程的showToast方法,实现进程间的通信。
-
* @throws RemoteException
-
*/
-
-
public void showToast() throws RemoteException {
-
Log.d(TAG, "showToast" " processName:" ProcessUtil.getProcessName(getApplicationContext()) " isMainProcess:" ProcessUtil.isMainProcess(getApplicationContext()));
-
Handler handler = new Handler(Looper.getMainLooper());
-
handler.post(new Runnable() {
-
-
public void run() {
-
Toast.makeText(getApplicationContext(), "remoteWeb进程调用了主进程的showToast方法", Toast.LENGTH_LONG).show();
-
}
-
});
-
}
-
-
/**
-
* remoteWeb进程调用主进程的doCalculate方法,实现进程间通信。
-
* @param a
-
* @param b
-
* @return
-
* @throws RemoteException
-
*/
-
-
public double doCalculate(double a, double b) throws RemoteException {
-
Calculate calculate = new Calculate();
-
final double result = calculate.calculateSum(a, b);
-
Handler handler = new Handler(Looper.getMainLooper());
-
handler.post(new Runnable() {
-
-
public void run() {
-
Toast.makeText(getApplicationContext(), "remoteWeb进程调用了主进程的doCalculate方法, 计算结果为:" result, Toast.LENGTH_LONG).show();
-
}
-
});
-
return result;
-
}
-
};
-
}
(3)、在独立进程SingleProcessActivity中绑定远程服务RemoteService:
-
/**
-
* 绑定远程服务RemoteService
-
*/
-
private void initService() {
-
Intent intent = new Intent();
-
intent.setComponent(new ComponentName("com.hongri.webview", "com.hongri.webview.service.RemoteService"));
-
bindService(intent, mConn, BIND_AUTO_CREATE);
-
}
(4)、绑定成功后,将服务端返回的Binder对象(service)转换成AIDL接口所属的类型(CalculateInterface):
-
private CalculateInterface mRemoteService;
-
-
private final ServiceConnection mConn = new ServiceConnection() {
-
-
public void onServiceConnected(ComponentName name, IBinder service) {
-
Log.d(TAG, "onServiceConnected");
-
//当Service绑定成功时,通过Binder获取到远程服务代理
-
mRemoteService = CalculateInterface.Stub.asInterface(service);
-
}
-
-
-
public void onServiceDisconnected(ComponentName name) {
-
Log.d(TAG, "onServiceDisconnected");
-
mRemoteService = null;
-
}
-
};
(5)、此时就可以根据前端post方法的调用,来根据 mRemoteService 实例针对性的调用主进程的showToast与doCalculate方法:
-
-
public void post(String cmd, String param) throws RemoteException {
-
Log.d(TAG, "当前进程name:" ProcessUtil.getProcessName(this) " 主进程:" ProcessUtil.isMainProcess(this));
-
dealWithPost(cmd, param);
-
}
-
-
/**
-
* 前端调用方法处理
-
*
-
* @param cmd
-
* @param param
-
* @throws RemoteException
-
*/
-
private void dealWithPost(String cmd, String param) throws RemoteException {
-
if (mRemoteService == null) {
-
Log.e(TAG, "remote service proxy is null");
-
return;
-
}
-
switch (cmd) {
-
case "showToast":
-
Log.d(TAG, "showToast");
-
mRemoteService.showToast();
-
break;
-
case "appCalculate":
-
Log.d(TAG, "appCalculate --> " param);
-
try {
-
JSONObject jsonObject = new JSONObject(param);
-
double firstNum = Double.parseDouble(jsonObject.optString("firstNum"));
-
double secondNum = Double.parseDouble(jsonObject.optString("secondNum"));
-
double calculateResult = mRemoteService.doCalculate(firstNum, secondNum);
-
Log.d(TAG, "calculateResult:" calculateResult);
-
} catch (JSONException e) {
-
e.printStackTrace();
-
}
-
break;
-
default:
-
Log.d(TAG, "Native端暂未实现该方法");
-
break;
-
}
-
}
点击前端页面的doCalculate方法,调用结果如下:
表示最终实现了remoteWeb 独立进程与主进程之间的通信。
附GitHub源码:WebViewExplore
参考:
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgfafhb
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01