Preface overview
Almost all apps use WebView components. It is also an option to use WebView to host business functions. After all, there is no need to wait for the review of the application market to improve the timeliness of business launch and bug repair. However, WebView loading business functions also has great defects, poor experience (mainly reflected in loading and interaction) and memory consumption; The problem of memory consumption here provides a multi process design scheme to let WebView run in a separate process. The advantage of this is to share the memory pressure of the main process. In addition, if the WebView process crashes, it will not affect the normal operation of the main process.
WebView cross process communication is the core of this chapter. In addition, it also carries out secondary encapsulation based on Tencent X5 service. First, briefly summarize the characteristics of this library:
- High reliability, cross process communication design scheme, reduce the memory pressure of the main process and reduce the probability of OOM; Even if WebView runs out of order, it will not affect the main process. If the multi process scheme is not used, WebView will not be free of memory leakage.
- Extensible, simplified cumbersome configuration, flexible on-demand setting, interactive communication between Js and native, meeting the needs of activity and fragment, and convenient for adding functions.
- Preload and initialize the WebView instance in advance.
Knowledge points involved in this chapter:
- Binder communication and AIDL use between Android processes.
- Design mode - singleton mode, Builder mode, command mode
- The annotation processor AutoService library is provided by Google and is also an option for communication between components.
- A little knowledge of Js
Multiple processes are used in many App applications. Let's see what we are most familiar with wechat
adb shell "ps |grep keyword"
keyword: is the package name of the app
com.tencent.mm:appbrand: applet process. Every time wechat starts a applet, an independent process appbrand[x] will be established, with a maximum of 5 processes.
Wechat puts the applet in an independent process, which is actually to apply to the system for another piece of independent memory to use, effectively breaking through the memory limit by default. At the same time, it runs in an independent process. Even if the applet is cashed, it will not affect the normal operation of the main process. After all, the applet is open, and the code quality written by each developer is uneven; It is necessary to put it in an independent process.
Similarly, WebView is the same. Different versions of kernels may be different, and mobile phone manufacturers may modify and customize their own WebView. For various compatibility problems, everyone writes different WebView code quality and has different understanding of memory management. If the Crash of WebView affects the experience, the gain is not worth the loss, Therefore, it is also necessary to put WebView in a separate process.
Basic use
It is the same as starting a normal Activity, as follows
Intent intent = new Intent(this, WebViewActivity.class); intent.putExtra(Constants.URL, url); intent.putExtra(Constants.TITLE, title); intent.putExtra(Constants.JS_OBJECT_NAME,"xxwebview"); startActivity(intent);
If you need to interact with Js, you must carry constants Js_ OBJECT_ Parameter for name. If necessary, you can inherit WebViewActivity and WebViewFragment for modification.
If you do not want to use the default WebViewActivity, you can directly use the encapsulated WebView, as follows:
WebViewManager.with(mActivity) .setViewContainer(webViewContainer) .setJsObjectName(mJsName) .setWebUrl(mUrl) .load()
Where setViewContainer() is the parent container of the incoming WebView. In addition, if multiple processes are required at this time, you must declare the process attribute in your customized Activity, as follows:
<activity android:name=".ui.WebViewActivity" android:process=":webview" />
The function implementation of Js and native interaction is described below.
Architecture process
Flow chart of the whole architecture:
Core class
- WebViewCommandDispatcher: after receiving the Command from Js, it is responsible for distributing the Command to the main process for processing, and implements the ServiceConnection interface.
- MainCommandManager: manages all Command commands and receives Command commands distributed by the Web process in the Main process; This class is the Binder proxy object on the Main process.
- Command: responsible for realizing specific functions.
- ICallbackFromMainToWebInterface: the Binder proxy object on the Web process, which passes the results of the Main process to the Web process.
If the native function is called after sending a Command in Js, the corresponding function needs to be implemented and the Command interface can be implemented, as follows:
@AutoService(Command::class) class CommandShowToast : Command { override fun commandName(): String { return "showToast" } override fun executeCommand(parameters: Map<*, *>, callback: ICallbackFromMainToWebInterface) { LogUtils.d("executeCommand curr thread "+ Thread.currentThread().name) Toast.makeText(App.INSTANCE, parameters["message"].toString(), Toast.LENGTH_SHORT).show() } }
In the implementation class, the class annotation @ AutoService(Command::class) needs to be added to ensure that MainCommandManager can find and register.
The function is implemented in the executeCommand() method, in which the commandName() method defines the command name, which needs to be consistent with that sent by Js. Otherwise, the executeCommand() method cannot be triggered, that is, Js will fail to call the native function. as follows
We need to coordinate with the front end here
function callAppToast(){ console.log("callAppToast."); window.global.takeNativeAction("showToast", {message: "come from html News of"}); } global.takeNativeAction = function(commandName, parameters){ console.log("global takeNativeAction") var request = {}; request.name = commandName; request.param = parameters; window.xxwebview.takeNativeAction(JSON.stringify(request)); }
The parameters carried by Js can be retrieved from the parameters in the executeCommand() method.
Command mode
In fact, the whole process is implemented based on command mode, which is the core of this scheme. Developers only need to pay attention to the implementation of their own business, and the intermediate process does not need to be concerned.
For standard command modes, refer to > Standard command mode
Pre initialization
For the performance of WebView, the most intuitive thing is that the opening speed is slower than native.
Yes, when we open a WebView page, the page often loads slowly for a long time, and the page you need to see appears after a few seconds.
For an ordinary user, opening a WebView usually goes through the following stages:
- Open a new page, the initialization phase (WebView), without any interaction
- Download Js script and CSS style, and then Html rendering; Then execute the Js script. In this stage, the screen is blank and is always in the loading state.
- When the Js script is executed successfully, the data is obtained from the background and finally displayed.
If viewed from the program, the WebView startup process can be divided into the following stages:
The client can handle more in the first stage, WebView initialization stage. The preloading here is to complete the initialization of WebView in advance. WebViewPool
public X5WebView getX5WebView(Context context) { if (sX5WebViewStackCached.isEmpty()) { X5WebView webView = createX5WebView(context); sX5WebViewStackCached.push(webView); return webView; } // Use stack top X5WebView webView = sX5WebViewStackCached.pop(); // If the WebView is not empty, start using the pre created WebView and replace the Context MutableContextWrapper contextWrapper = (MutableContextWrapper) webView.getContext(); contextWrapper.setBaseContext(context.getApplicationContext()); return webView; }
In the offline package scheme, you can silently pre download H5 resources locally when you are free, and then load H5 locally when you need it. This scheme can shorten the white screen time and is very helpful for optimization, but it is a little difficult to implement. It needs the cooperation of the front and rear ends and App ends at the same time.
reference resources https://github.com/al-liu/OCat-MobilePlatform