Follow up knowledge points of x5 open source library

Catalogue introduction

  • 01. Introduction to basic use catalogue
    • 1.0.1 common basic introduction
    • 1.0.2 Android calls Js
    • 1.0.3 Js calls Android
    • 1.0.4 WebView.loadUrl(url) process
    • 1.0.5 js call timing analysis
    • 1.0.6 how to clear cache data
    • 1.0.7 how to use DeepLink
    • 1.0.8 application opened as a third-party browser
  • 02. Introduction to optimization summary catalog
    • 2.0.1 video full screen playback press return page to be enlarged
    • 2.0.2 speed up loading image resources in webView
    • 2.0.3 custom load exception error status page
    • 2.0.4 page rendering flickers due to WebView hardware acceleration
    • 2.0.5 WebView load certificate error
    • 2.0.6 there is still sound after the destruction of Web audio playback
    • 2.0.7 DNS uses the same domain name as the client API
    • 2.0.8 how to set white list operation
    • 2.0.9 js cannot be released in the background, resulting in heating and power consumption
    • 2.1.0 loading progress bar can be displayed in advance
    • 2.1.1 WebView password plaintext storage vulnerability optimization
  • 03. Introduction to the problem summary catalogue
    • 3.0.0 introduction to the evolution of WebView
    • 3.0.1 necessity to initialize WebView in advance
    • 3.0.2 x5 load office resources
    • 3.0.3 WebView playing video problems
    • 3.0.4 unable to get the correct height of webView
    • 3.0.5 open link risk using scheme protocol
    • 3.0.6 how to handle loading errors
    • 3.0.7 webView to prevent memory leakage
    • 3.0.8 modification of js injection timing
    • 3.0.9 video / picture width exceeds screen
    • 3.1.0 how to ensure js security
    • 3.1.1 how to enable hardware acceleration by code
    • 3.1.2 WebView setting cookies
    • 3.1.4 webView loading web page does not display pictures
    • 3.1.5 bypass certificate verification vulnerability
    • 3.1.6 allowFileAccess vulnerability
    • 3.1.7 WebView nested ScrollView problem
    • 3.1.8 Click to enlarge the picture in WebView
    • 3.1.9 do not render / execute during page sliding
    • 3.2.0 hijacking and injection by operators
    • 3.2.1 solve the problem of slow resource loading
    • 3.2.2 judge whether it has scrolled to the bottom of the page
    • 3.2.3 loading html garbled code with loadData
    • 3.2.4 WebView download progress cannot be monitored
    • 3.2.5 302 / 303 redirection in WebView

x5 package library YCWebView open source project address

  • https://github.com/yangchong211/YCWebView
  • This follow-up knowledge point almost contains most of the problems in the actual development. Learn and consolidate webView again. I hope this article will be useful to you For more content, you can see my open source project. If you think it will bring you some harvest, please star it, which can also increase the power of developers' open source project!

01. Introduction to basic use catalogue

1.0.1 common basic introduction

  • The simplest use in activity
    webview.loadUrl("http://www.baidu.com / "); / / load web resources
    //webView.loadUrl("file:///android_asset/example.html"); / / load local resources
    //A problem is found at this time. After the application is started, the built-in browser of the system is opened automatically. To solve this problem, you need to set WebViewClient for webview and rewrite the method:
    webview.setWebViewClient(new WebViewClient(){
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            //When the return value is true, control to open WebView, and call system browser or third-party browser for false
            return true;
        }
        //You can also override other methods
    });
    
  • What factors affect page loading speed
    • There are many factors that affect the loading speed of the page. It is found in the process of debugging the WebView to load a page
      • There will be more network requests in the process of loading each time, except for the URL request of the web page itself
      • JS, CSS, fonts, pictures, etc. with external references to web pages are independent http requests. These requests are all serial. These requests, together with the browser's parsing and rendering time, will lead to the overall loading time of WebView becoming longer and the consumed traffic corresponding to the real amount.

1.0.2 Android calls Js

  • The first way: native calls the js method, which is:
    • Note that the name must be corresponding, otherwise the call is not successful, and there is another point that the JS call must be called after the onPageFinished function callback, otherwise it will fail.
    //java
    //Call parameterless method
    mWebView.loadUrl("javascript:callByAndroid()");
    //Call a parameterized method
    mWebView.loadUrl("javascript:showData(" + result + ")");
    
    //javascript, here is the corresponding js code
    <script type="text/javascript">
    
    function showData(result){
        alert("result"=result);
        return "success";
    }
    
    function callByAndroid(){
        console.log("callByAndroid")
        showElement("Js:No reference method callByAndroid Be called");
    }
    </script>
    
  • The second way:
    • If there is a demand now, what should we do if we want to get a callback of Native calling the Web? Google added a new method in Android 4.4. This method is more convenient and concise than the loadUrl method, and more efficient than the loadUrl method. Because the execution of loadUrl will cause a page refresh, this method will not, because this method was introduced in version 4.4 You need to add version judgment when using
    if (Build.VERSION.SDK_INT < 18) {
        mWebView.loadUrl(jsStr);
    } else {
        mWebView.evaluateJavascript(jsStr, new ValueCallback<String>() {
            @Override
            public void onReceiveValue(String value) {
                //Here is the result returned by js
            }
        });
    }
    
  • Comparison of two ways
    • Generally, the first method is the most commonly used method, but the first method is troublesome to obtain the returned value, while the second method is limited because it was introduced in version 4.4.
  • Pay attention to problems
    • Remember to add ws.setJavaScriptEnabled(true) code

1.0.3 Js calls Android

  • The first way: add object mapping through the addJavascriptInterface method
    • This is the most commonly used method. First, we need to set a property:
    mWebView.getSettings().setJavaScriptEnabled(true);
    
    • There will be a warning in this function, because there will be a very dangerous vulnerability under a specific version. After setting this property, Native needs to define a class:
      • After API version 17, the @ addJavascriptInterface constraint annotation needs to be added where it is called, because methods without annotation cannot be called
    public class JSObject {
        private Context mContext;
        public JSObject(Context context) {
            mContext = context;
        }
    
        @JavascriptInterface
        public String showToast(String text) {
            Toast.show(mContext, text, Toast.LENGTH_SHORT).show();
            return "success";
        }
    
        /**
         * Front end code embedded js:
         * imageClick Name should be the same as js function method name
         *
         * @param src Links to pictures
         */
        @JavascriptInterface
        public void imageClick(String src) {
            Log.e("imageClick", "----Click on the picture");
        }
    
        /**
         * js used by web page, method has no parameters
         */
        @JavascriptInterface
        public void startFunction() {
            Log.e("startFunction", "----No ginseng");
        }
    }
    
    //Vulnerability in specific version
    mWebView.addJavascriptInterface(new JSObject(this), "yc Teasing ratio");
    
    • JS code call
      • The advantage of this method is that it is easy to use, and the local and JS conventions are also very simple, that is, the object name and method name conventions are good, and the disadvantage is the vulnerability to be mentioned.
    function showToast(){
        var result = myObj.showToast("I am from web Of Toast");
    }
    
    function showToast(){
        myObj.imageClick("picture");
    }
    
    function showToast(){
        myObj.startFunction();
    }
    
  • The second way: intercept the url by using the callback method of WebViewClient interface
    • In fact, the implementation of this method is very simple, and the frequency of use is very high. The WebView client is introduced above. There is a callback interface, shouldoverride url loading (WebView view, string url)), which is to use this interception url, and then analyze the protocol of this url. If it is found that the protocol we agreed in advance starts to parse the parameters and execute the corresponding logic. Note that this method has been abandoned in API24, and needs to be replaced by shouldoverrideurlloading (WebView view, webresourcerequest request request)). The method is very similar. Let's use shouldoverrideurlloading (WebView, string url)) to introduce:
      • The code is very simple. This method can intercept the process of loading url in WebView and get the corresponding url. We can use this method to agree a protocol with the web page. If it matches, we can perform the corresponding operation.
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        //Suppose the incoming URL is "JS: / / openactivity? Arg1 = 111 & arg2 = 222", which means the local page needs to be opened and the corresponding parameters are brought in
        Uri uri = Uri.parse(url);
        String scheme = uri.getScheme();
        //If scheme is js, it represents the pre agreed js protocol
        if (scheme.equals("js")) {
              //If the authority is openActivity, it means that the web needs to open a local page
            if (uri.getAuthority().equals("openActivity")) {
                  //Analyze the related parameters brought by web pages
                HashMap<String, String> params = new HashMap<>();
                Set<String> collection = uri.getQueryParameterNames();
                for (String name : collection) {
                    params.put(name, uri.getQueryParameter(name));
                }
                Intent intent = new Intent(getContext(), MainActivity.class);
                intent.putExtra("params", params);
                getContext().startActivity(intent);
            }
            //Representative application internal processing completed
            return true;
        }
        return super.shouldOverrideUrlLoading(view, url);
    }
    
    • JS code call
    function openActivity(){
        document.location = "js://openActivity?arg1=111&arg2=222";
    }
    
    • Problem: after the code is executed, the local shouldOverrideUrlLoading method will be triggered, and then the parameter will be parsed to call the specified method. This method will not have the vulnerability mentioned in the first one, but it also has a very tedious place: if the web end wants to get the return value of the method, it can only execute JS method through WebView's loadUrl method to pass the return value back. The relevant code is as follows:
    //java
    mWebView.loadUrl("javascript:returnResult(" + result + ")");
    
    //javascript
    function returnResult(result){
        alert("result is" + result);
    }
    
  • The third way: using three methods of the WebChromeClient callback interface to intercept messages
    • The principle of this method is the same as that of the second method. It intercepts related interfaces, but the intercepted interfaces are different:
    @Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
        return super.onJsAlert(view, url, message, result);
    }
    
    @Override
    public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
        return super.onJsConfirm(view, url, message, result);
    }
    
    @Override
    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
        //Suppose the incoming message is "JS: / / openactivity? Arg1 = 111 & arg2 = 222", which means that the local page needs to be opened and the corresponding parameters are brought in
        Uri uri = Uri.parse(message);
        String scheme = uri.getScheme();
        if (scheme.equals("js")) {
            if (uri.getAuthority().equals("openActivity")) {
                HashMap<String, String> params = new HashMap<>();
                Set<String> collection = uri.getQueryParameterNames();
                for (String name : collection) {
                    params.put(name, uri.getQueryParameter(name));
                }
                Intent intent = new Intent(getContext(), MainActivity.class);
                intent.putExtra("params", params);
                getContext().startActivity(intent);
                //Representative application internal processing completed
                result.confirm("success");
            }
            return true;
        }
        return super.onJsPrompt(view, url, message, defaultValue, result);
    }
    
    • As with WebViewClient, the WebChromeClient interface is added this time. It can intercept several prompt methods in JS, that is, dialog boxes of several styles. There are three common dialog methods in JS:
      • The onJsAlert method is to pop up a warning box. In general, it is Toast in Android. You can add a line feed in the text;
      • onJsConfirm pops up the confirmation box, and returns the Boolean value, through which you can judge whether to confirm or cancel when clicking. true means to confirm, false means to cancel;
      • onJsPrompt pops up the input box, click OK to return to the value in the input box, and click Cancel to return to null.
    • But these three kinds of dialog boxes can be intercepted locally, so you can make some changes here, intercept these methods, get their contents, and analyze them. For example, if it's JS protocol, it's internal protocol. The next step is to analyze and then perform relevant operations. The prompt method call is as follows:
    function clickprompt(){
        var result=prompt("js://openActivity?arg1=111&arg2=222");
        alert("open activity " + result);
    }
    
    • It should be noted that the content in prompt is passed through message, not the url of the second parameter, and the return value is passed through the JsPromptResult object. Why to intercept the onJsPrompt method instead of the other two methods is feasible in a sense, but it is not possible if the return value is needed to the web end, because onJsAlert cannot return a value, and onJsConfirm can only return two values, i.e. confirm or cancel. Only the onJsPrompt method can return a value of string type It is the most comprehensive and convenient operation.
  • Summary and comparison of the above three schemes
    • All of the above three schemes are feasible. Let's summarize them here
    • The first way: it is the most common use at present, convenient and concise, but the only disadvantage is that there is vulnerability under 4.2 system;
    • The second way: by intercepting the url and parsing it, if it is an agreed protocol, the corresponding specified operations will be carried out. The disadvantage is that the protocol constraint needs to record a standard document, and it is cumbersome to transfer the value from the Native layer to the Web layer. The advantage is that there will be no leakage. The version under IOS 7 is used in this way.
    • The third way: the idea of the second way is actually similar, but the interception method has changed. Here, three dialog methods in JS are intercepted. The difference between these three dialog methods is the return value problem. The alert dialog box has no return value. The confirm dialog box method has only two return values. The prompt dialog box method can return any type of dialog box The disadvantage of the return value is that the protocol formulation is rather cumbersome, and detailed documents need to be recorded, but the vulnerability of the second method will not exist.

1.0.4 WebView.loadUrl(url) process

  • What does WebView.loadUrl(url) do to load a web page?
    • Loading a web page is a complex process. In this process, we may need to perform some operations, including:
    • Before loading a web page, reset the WebView state and the variable state bound to the business. The WebView state includes the redirection state (mTouchByUser), the front-end control backstep, etc. the business state includes the progress bar, the sharing content of the current page, the display and hiding of the sharing button, etc.
    • Before loading the web page, the parameters of the local client are spliced according to different domains, including the basic model information, version information, login information and the Refer information used in the embedding point. Sometimes, additional configuration is needed for transactions, properties, etc.
    • When the page loading operation is started, WebViewClient.onPageStarted(webview,url,favicon) will be called back. In this method, you can reset the redirection protected variable (mRedirectProtected), or you can reset it before the page is loaded. Due to the legacy code problem, optimization has not been omitted here.
    • During page loading, WebView calls back several methods.
    • After the page is loaded, WebView will call back several methods.
  • What methods are called back during page loading?
    • WebChromeClient.onReceivedTitle(webview, title), which is used to set the title. It should be noted that this method may be called back many times in some Android system versions, and sometimes the title of the callback is a url. The client can handle this situation specially to avoid unnecessary links in the title bar.
    • WebChromeClient.onProgressChanged(webview, progress). According to this callback, you can control the progress of the progress bar (including display and hide). In general, it takes a long time (especially the first load) to achieve 100% progress, and users will feel anxious and affect the experience if they wait for the progress bar to not disappear for a long time. In fact, when progress reaches 80, the loaded page is basically available. In fact, most of the domestic manufacturers will hide the progress bar in advance, so that users think the web page load quickly.
    • WebViewClient.shouldInterceptRequest(webview, request), whether it is a normal page request (using GET/POST), an asynchronous request in the page, or a resource request in the page, will call back this method, giving the development an opportunity to intercept the request. In this method, we can intercept static resources and use cached data instead, or intercept pages and use our own network framework to request data. Including the WebView free flow scheme introduced later, which is also related to this method.
    • WebViewClient.shouldOverrideUrlLoading(webview, request). If you encounter redirection, or click the a tag in the page to implement page Jump, this method will be called back. It can be said that this is one of the most important callbacks in WebView. The interaction between WebView and Native page will explain this method in detail later.
    • WebViewClient.onReceivedError(webview,handler,error). If an error occurs during page loading, this method will be called back. Mainly http errors and ssl errors. In these two callbacks, we can report exceptions, monitor exception pages and overdue pages, and timely feed back to the operation or front-end for modification. When dealing with ssl errors, when encountering untrusted certificates, special processing can be carried out, for example, judging the domain name, and "releasing" the domain name of your company to prevent entering the ugly wrong certificate page. Like Chrome, you can also pop up the ssl certificate query pop-up window to give users the choice.
  • What are the callback methods at the end of loading page
    • It will call back WebViewClient.onPageFinished(webview,url).
    • At this time, you can judge whether to display the close WebView button according to the backout stack. Judge whether it is possible to fallback through mpactivityweb.cangobackorforforward (- 1).

1.0.5 js call timing analysis

  • Inject js code into onPageFinished() or onPageStarted() methods
    • WebView has been developed and needs to interact with js. Most of them think that js is the most appropriate to inject into WebViewClient.onPageFinished() method. At this time, dom tree has been built and the page has been fully displayed. But if you have tested the page loading speed, you will find that the WebViewClient.onPageFinished() method usually takes a long time to call back (the first load usually exceeds 3s), because WebView needs to load the main document and all resources in a page before calling back this method.
    • Can I inject it into WebViewClient.onPageStarted()? The answer is uncertain. After testing, some models are OK, some are not. Injection in WebViewClient.onPageStarted() also has a fatal problem - this method may call back multiple times, causing multiple injections of js code.
    • Since 7.0, there have been some small changes in the way WebView loads js. The official suggestion is to put the timing of js injection after the page starts loading.
  • Inject js code into WebViewClient.onProgressChanged() method
    • WebViewClient.onProgressChanged() is called back many times during the dom tree rendering process. Each time, it will tell us the current loading progress.
      • In this method, you can customize the progress bar for WebView, similar to the progress bar when wechat loads web pages
      • If you inject js code into this method, you need to avoid duplicate injection and enhance the logic. A boolean variable can be defined to control the injection timing
    • Then someone will ask, how many loads do you need to handle js injection logic?
      • For this reason, when the progress of the page is loaded to 80%, the dom tree is almost rendered, indicating that WebView has parsed the < HTML > tag, and the injection must be successful. There are several points to pay attention to when implementing js injection in WebViewClient.onProgressChanged():
      • 1 the multiple injection control mentioned above uses boolean value variable control
      • 2 before reloading a URL, you need to reset the boolean value variable so that the reloaded page can inject js again
      • 3. If the local js, css and other caches have been done, first determine whether the local cache exists. If so, load the local cache. Otherwise, load the network js
      • 4. The progress threshold of injection can be customized freely, theoretically 10% - 100% is reasonable, but 75% - 90% is recommended.

1.0.6 how to clear cache data

  • What are the ways to clear cached data?
    //Clear cache left by web access
    //Because the kernel cache is global, this method is not only for webview but also for the whole application
    Webview.clearCache(true);
    
    //Clear the history of current webview access / / only all records in the history of webview access except the current access record will be cleared
    Webview.clearHistory();
    
    //This api only clears the form data automatically filled in, and does not clear the data stored in the local WebView
    Webview.clearFormData();
    

1.0.7 how to use DeepLink

1.0.8 application opened as a third-party browser

  • For the article page in wechat, you can choose "open in browser". Now many applications are embedded with WebView, can you make your own application open this article as a third-party browser?
  • In the Manifest file, add the < intent Filter > configuration to the Activity you want to receive the jump:
    <activity
        android:name=".X5WebViewActivity"
        android:configChanges="orientation|screenSize"
        android:hardwareAccelerated="true"
        android:launchMode="singleTask"
        android:screenOrientation="portrait"
        android:theme="@style/Theme.AppCompat.Light.NoActionBar">
        <!--You need to add the following intent-filter To configure-->
        <intent-filter tools:ignore="AppLinkUrlError">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <!--Use http,Can only be opened http Start page-->
            <data android:scheme="https" />
        </intent-filter>
    </activity>
    
  • Then get the relevant transfer data in X5WebViewActivity. See the X5WebViewActivity class code in lib for details.
    public class X5WebViewActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_web_view);
            getIntentData();
            initTitle();
            initWebView();
            webView.loadUrl(mUrl);
           // Process the value passed as a three-party browser open
            getDataFromBrowser(getIntent());
        }
    
       /**
         * Only one instance of an Activity using the single task startup mode exists in the system.
         * If the instance already exists, the intent will be passed to the Activity through onNewIntent.
         * Otherwise, a new Activity instance is created.
         */
        @Override
        protected void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
            getDataFromBrowser(intent);
        }
    
        /**
         * Open the passed value as a three-party browser
         * Scheme: https
         * host: www.jianshu.com
         * path: /p/yc
         * url = scheme + "://" + host + path;
         */
        private void getDataFromBrowser(Intent intent) {
            Uri data = intent.getData();
            if (data != null) {
                try {
                    String scheme = data.getScheme();
                    String host = data.getHost();
                    String path = data.getPath();
                    String text = "Scheme: " + scheme + "\n" + "host: " + host + "\n" + "path: " + path;
                    Log.e("data", text);
                    String url = scheme + "://" + host + path;
                    webView.loadUrl(url);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
  • Some highlights
    • Open your own application "through browser" in wechat, and then cut your own application to the background. Repeat the above operation, and the application instance will be created all the time. This is definitely not good. To avoid this situation, we set the startup mode as: launchmode = "single task".

02. Introduction to optimization summary catalog

2.0.1 video full screen playback press to return to the page to be enlarged (some mobile phones appear)

  • As for the reason not found, the solution is as follows
    /**
     * This method is called when the scale changes
     * @param view                              view
     * @param oldScale                          Previous zoom
     * @param newScale                          Now zoom
     */
    @Override
    public void onScaleChanged(WebView view, float oldScale, float newScale) {
        super.onScaleChanged(view, oldScale, newScale);
        //Video full screen playback press to return to the page to be enlarged
        if (newScale - oldScale > 7) {
            //Abnormal amplification, retraction.
            view.setInitialScale((int) (oldScale / newScale * 100));
        }
    }
    

2.0.2 when loading resources in webView, speed up the optimization of loading, mainly for pictures

  • After the html code is downloaded to WebView, webkit starts to analyze each node of the web page. When it finds that there are external style files or external script files, it will asynchronously initiate a network request to download the files. However, if it also resolves to the image node before this, it will also initiate a network request to download the corresponding pictures. In the case of poor network conditions, too many network requests will cause bandwidth tension, affect the time of css or js file loading, and cause page blank loading for too long. The solution is to tell WebView not to automatically load images first, and then start image loading after the page is finish ed.
    //It is set during initialization. The specific code is in the X5WebView class
    if(Build.VERSION.SDK_INT >= KITKAT) {
        //Set the page not to load pictures temporarily when loading
        ws.setLoadsImagesAutomatically(true);
    } else {
        ws.setLoadsImagesAutomatically(false);
    }
    
    /**
     * This method is called when the page is loaded
     * @param view                              view
     * @param url                               url link
     */
    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        //Start image loading after finish ing the page
        if(!webView.getSettings().getLoadsImagesAutomatically()) {
            webView.getSettings().setLoadsImagesAutomatically(true);
        }
    }
    

2.0.3 customize the status page of loading exception error. For example, error may appear in the following methods

  • When WebView fails to load the page (generally 404 NOT FOUND), Android WebView will display an error interface by default. When WebView loads an error, the onReceivedError() in the WebViewClient instance and the onReceivedTitle method receive the error
    /**
     * error in request network
     * @param view                              view
     * @param errorCode                         Error
     * @param description                       description
     * @param failingUrl                        Failed link
     */
    @Override
    public void onReceivedError(WebView view, int errorCode, String description, String
            failingUrl) {
        super.onReceivedError(view, errorCode, description, failingUrl);
        if (errorCode == 404) {
            //Using javascript to hide 404 page information defined by the system
            String data = "Page NO FOUND!";
            view.loadUrl("javascript:document.body.innerHTML=\"" + data + "\"");
        } else {
            if (webListener!=null){
                webListener.showErrorView();
            }
        }
    }
    
    // Report Web resource load errors to the host application. These errors usually indicate that you cannot connect to the server.
    // It is worth noting that the difference is the callback of the outdated version, and the new version will be called any resource (iframe, image, etc.)
    // Not just the home page. Therefore, it is recommended to perform the minimum required work during the callback.
    // After 6
    @Override
    public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
        super.onReceivedError(view, request, error);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            X5WebUtils.log("Server exception"+error.getDescription().toString());
        }
        //ToastUtils.showToast("after server exception 6.0");
        //When a load error occurs, let it load the local error web page file
        //mWebView.loadUrl("file:///android_asset/errorpage/error.html");
        if (webListener!=null){
            webListener.showErrorView();
        }
    }
    
    /**
     * This method is mainly used to monitor the title change operation
     * @param view                              view
     * @param title                             Title
     */
    @Override
    public void onReceivedTitle(WebView view, String title) {
        super.onReceivedTitle(view, title);
        if (title.contains("404") || title.contains("Page cannot be opened")){
            if (webListener!=null){
                webListener.showErrorView();
            }
        } else {
            // Set title
        }
    }
    

2.0.4 page rendering flickers due to WebView hardware acceleration

  • For systems above 4.0, when we turn on hardware acceleration, WebView renders pages faster and drags more smoothly. But there is a side effect: when the WebView view is covered by the whole view, and then suddenly recovers (for example, when using SlideMenu to slide the WebView out of the side), there will be a white block in the transition period and the interface will flash at the same time. The solution to this problem is to speed up the temporary shutdown of WebView hardware before the transition period, and then open it after the transition period
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }
    

2.0.5 WebView load certificate error

  • When webView loads some other people's URLs, certificate authentication errors sometimes occur. At this time, we want to render the page to the user normally. We need to ignore the certificate errors. We need to call the onReceivedSslError method of WebViewClient class, and call handler. Processed() to ignore the certificate errors.
    /**
     * Notify host application of SSL errors when loading resources
     * Function: process https request
     * @param view                              view
     * @param handler                           handler
     * @param error                             error
     */
    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        super.onReceivedSslError(view, handler, error);
        if (error!=null){
            String url = error.getUrl();
            X5WebUtils.log("onReceivedSslError----abnormal url----"+url);
        }
        //https ignore certificate issue
        if (handler!=null){
            //Indicates waiting for certificate response
            handler.proceed();
            // handler.cancel(); / / indicates that the connection is suspended, which is the default method
            // handler.handleMessage(null); / / other processing can be done
        }
    }
    

2.0.6 there is still sound after the destruction of Web audio playback

  • The audio is played on the WebView page. After the Activity exit, the audio is still playing, and it needs to be called in the onDestory() of Activity.
    @Override
    protected void onDestroy() {
        try {
            //Destruction logic of web page with audio playing
            //When the Activity is closed, if Webview's music or video is still playing. You have to destroy Webview
            //But note: when webview calls destory, webview is still bound to Activity
            //This is because the context object of the Activity is passed in when the custom webview is built
            //Therefore, you need to remove webview from the parent container before destroying it:
            if (webView != null) {
                ViewGroup parent = (ViewGroup) webView.getParent();
                if (parent != null) {
                    parent.removeView(webView);
                }
                webView.removeAllViews();
                webView.destroy();
                webView = null;
            }
        } catch (Exception e) {
            Log.e("X5WebViewActivity", e.getMessage());
        }
        super.onDestroy();
    }
    

2.0.7 DNS uses the same domain name as the client API

  • Establish connection / server processing; before the data of page request is returned, there are mainly the following processes that take time.
    DNS
    connection
     Server processing
    
  • DNS uses the same domain name as the client API
    • DNS will cache at the system level. For the WebView address, if the domain name used is the same as the native API, you can directly use the cached DNS instead of sending the request picture.
    • For a simple example, the domain name requested by the client is mainly located at api.yc.com, while the embedded WebView is mainly located at i.yc.com.
    • When we open the App for the first time: the client will request api.yc.com for the first time, and its DNS will be cached by the system. However, when you open WebView, you need to retrieve the IP address of i.yc.com due to different domain names. Static resources are the same. It is better to keep consistent with the resource domain name of the client.

2.0.8 how to set white list operation

  • WebView in the client can be opened through a certain schema of the client, and many URLs of pages to be opened are not written in the client, but can be passed by the parameters in the URL. The risk of using scheme protocol to open links in 4.0.5 above has explained the danger of using scheme. How to avoid this problem? Set the white list for running access. Or give users a strong and obvious prompt before opening an external link. The specific operation is as follows:
    • In the method of onPageStarted to load resources, obtain the host value of the load url, and then compare it with the legal host saved locally. Here, domainList is an array
    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        super.onPageStarted(view, url, favicon);
        String host = Uri.parse(url).getHost();
        LoggerUtils.i("host:" + host);
        if (!BuildConfig.IS_DEBUG) {
            if (Arrays.binarySearch(domainList, host) < 0) {
                //Not in the white list, illegal website, this time gives users strong and obvious tips
            } else {
                //Legitimate URL
            }
        }
    }
    
  • Setting up a white list operation is actually the same as filtering advertisements. Here you can put some legal websites to allow access.

2.0.9 js cannot be released in the background, resulting in heating and power consumption

  • In some mobile phones, if you load html with webView, there are some js that have been executing such things as animation. If webView is hung in the background at the moment, these resources will not be released or perceived by users.
  • As a result, the power consumption of cpu always occupied is very fast, so if this situation is encountered, the processing method is as follows. It means that the onStop method will be called in the background, that is, the js interaction will be turned off at this time, and then the onstream method will be called back in the foreground to turn on the js interaction.
    //In onStop, set JavaScript enabled (false);
    //Set setscriptenabled (true) in onResume.
    @Override
    protected void onResume() {
        super.onResume();
        if (mWebView != null) {
            mWebView.getSettings().setJavaScriptEnabled(true);
        }
    }
    @Override
    protected void onStop() {
        super.onStop();
        if (mWebView != null) {
            mWebView.getSettings().setJavaScriptEnabled(false);
        }
    }
    

2.1.0 loading progress bar can be displayed in advance

  • Displaying progress bar in advance does not improve performance, but it is also very important for the user experience. WebView.loadUrl("url") will not immediately call back onPageStarted or onProgressChanged because during this period, WebView may be initializing the kernel, or establishing a connection with the server. In this period, white screen is easy to appear. The white screen user experience is It's bad, so I suggest
    //Correct
    pb.setVisibility(View.VISIBLE);
    mWebView.loadUrl("https://github.com/yangchong211/LifeHelper");
    
    //Just so so
    @Override
    public void onPageStarted(WebView webView, String s, Bitmap bitmap) {
        super.onPageStarted(webView, s, bitmap);
        //Set the operation of loading start
        pb.setVisibility(View.VISIBLE);
    }
    
    //Here is the logic of monitoring progress bar progress change
    mWebView.getX5WebChromeClient().setWebListener(interWebListener);
    mWebView.getX5WebViewClient().setWebListener(interWebListener);
    private InterWebListener interWebListener = new InterWebListener() {
        @Override
        public void hindProgressBar() {
            pb.setVisibility(View.GONE);
        }
    
        @Override
        public void showErrorView() {
    
        }
    
        @Override
        public void startProgress(int newProgress) {
            pb.setProgress(newProgress);
        }
    
        @Override
        public void showTitle(String title) {
    
        }
    };
    

2.1.1 WebView password plaintext storage vulnerability optimization

  • WebView opens the password saving function mWebView.setSavePassword(true) by default. If the function is not turned off, a prompt box will pop up when the user enters the password, asking whether the user saves the password. If "yes" is selected, the password will be saved to / data/data/com.package.name/databases/webview.db in clear text, so there is a risk of password theft, so you need to use websett Settings.setsavepassword (false) turns off the password saving reminder function.
    • The specific code operation is as follows
    /Set whether to enable the password saving function. It is not recommended to enable it. It has been handled by default. There is a risk of password theft
    mX5WebView.setSavePassword(false);
    

03. Introduction to the problem summary catalogue

3.0.0 introduction to the evolution of WebView

  • The evolutionary history is as follows
    • Starting with Android 4.4, the chrome kernel replaced the Webkit kernel.
    • Since Android 5.0, WebView has been ported as an independent apk, which can exist and update independently without relying on the system.
    • From Android 7.0, if Chrome is installed in the user's mobile phone, chrome is preferred to provide WebView rendering for the application.
    • Starting from Android 8.0 system, WebView multi process mode is enabled by default, that is, WebView runs in an independent sandbox process.

3.0.1 necessity to initialize WebView in advance

  • The first time you open a Web page, it's very slow to use WebView to load the page. The second time you open it, you can obviously feel that the speed has improved. Why?
    • Because the WebView kernel is not initialized when you first load a page, it takes time to initialize the WebView kernel when you first load a page.
    • Initialize the WebView kernel in advance. For example, put it into the Application to initialize it. You can directly use the WebView in the page. This method can effectively reduce the first opening time of WebView in the App. When a user accesses a page, there is no need to initialize the WebView.
    • But there is also a disadvantage in this way, the extra memory consumption. To jump between pages, you need to clear the traces of the previous page, making it easier for memory to leak.

3.0.2 x5 load office resources

  • Notes on loading documents such as word, pdf and xls: Tbs does not support loading network files, so you need to download the files locally first, and then load them out
  • Another point to note is that we call this method mTbsReaderView.onStop() in the onDestroy method, otherwise it will not be able to browse second times. See more FileReaderView class code!

3.0.3 WebView playing video problems

  • 1. WebView is used in this scheme, and there will be video nesting. There will be problems in playing video directly in the default WebView. Moreover, different SDK versions are not the same. The following solutions are searched on the Internet. Record. webView.getSettings.setPluginState(PluginState.ON);webView.setWebChromeClient(new WebChromeClient());
  • 2. Then add: Android: hardwareaccepted = "true" in the Activity configuration of webView
  • 3. The above video can be played normally, but the webview page is finish ed, and the video can still be heard. So I checked again and found that the onResume method of webview can continue playing, and the onPause method can pause playing, but both methods are added in the Added in API level 11, so reflection is needed to complete.
  • 4. Stop playing: use in the onPause method of the page: webView.getClass().getMethod("onPause").invoke(webView, (Object[])null);
  • 5. Continue playing: in the onResume method of the page, use: webView.getClass().getMethod("onResume").invoke(webView,(Object[])null); in this way, you can control the pause and continue playing of the video.

3.0.4 unable to get the correct height of webView

  • Occasionally, unable to get the content height of webView
    • Where htmlString is a string in HTML format.
    webView.loadData(htmlString, "text/html", "utf-8");
    webView.setWebViewClient(new WebViewClient() {
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            Log.d("yc", view.getContentheight() + "");
        }
    });
    
    • This is because the onPageFinished callback refers to the number of bytes that WebView has completed reading from the network. The page that was fired at onPageFinished may not have been parsed.
  • The first solution: provide some delay for onPageFinished()
    webView.setWebViewClient(new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            webView.postDelayed(new Runnable() {
                @Override
                public void run() {
                    int contentHeight = webView.getContentHeight();
                    int viewHeight = webView.getHeight();
                }
            }, 500);
        }
    });
    
  • The second solution: use js to get the content height. See this article for details: https://www.jianshu.com/p/ad22b2649fba

3.0.5 open link risk using scheme protocol

  • The common usage is to regenerate an intent after the APP obtains the data from the web page, and then send it to other components to use the data. For example, use Webview related activities to load a url from a web page. If the url comes from a parameter in the url scheme, such as: yc://ycbjie:8888/from?load_url=http://www.taobao.com.
  • Recommendations for use
    • Any place in APP receiving external input data is a potential attack point. Filter and check the parameters from the web page.
    • Do not transmit sensitive information through web pages. In order to guide users who have already logged in to the APP, some websites will use scripts to dynamically generate parameters of URL Scheme, including sensitive information such as user name, password or login token, so that users can log in directly after opening the APP. Malicious applications can also register the same URL Sechme to intercept these sensitive information. Android system will let users choose which APP to use to open the link, but if users don't pay attention, they will use malicious apps to open, resulting in sensitive information disclosure or other risks.
  • Solution
    • In the embedded WebView, you should restrict the domain name of the allowed open WebView and set the white list for running access. Or give users a strong and obvious prompt before opening an external link. For specific operations, see 5.0.8 how to set the white list operation mode.

3.0.6 how to handle loading errors (Http, SSL, Resource)

  • There are three kinds of error callbacks in WebView loading a web page
    /**
     * This method is called back only if there is an error loading the main page. This is the best way to show how to load the error page.
     * However, if the wrong page is displayed directly, it is likely to be misjudged, causing the illusion that the user often fails to load the page.
     * Because different WebView implementations may be different, we need to exclude several miscalculation examples first:
     *      1.The url that failed to load is not the same as the url in WebView, exclude;
     *      2.errorCode=-1,It indicates that the error is unknown. In order to ensure no misjudgment, it is excluded
     *      3failingUrl=null&errorCode=-12,Exclude because the error url is empty instead of error bad url
     * @param webView                                           webView
     * @param errorCode                                         errorCode
     * @param description                                       description
     * @param failingUrl                                        failingUrl
     */
    @Override
    public void onReceivedError(WebView webView, int errorCode,
                                String description, String failingUrl) {
        super.onReceivedError(webView, errorCode, description, failingUrl);
        // -12 == EventHandle.ERROR_BAD_URL, a hide return code inside android.net.http package
        if ((failingUrl != null && !failingUrl.equals(webView.getUrl())
                && !failingUrl.equals(webView.getOriginalUrl())) /* not subresource error*/
                || (failingUrl == null && errorCode != -12) /*not bad url*/
                || errorCode == -1) { //When errorCode = -1 and the error message is net:: err? Cache? Miss
            return;
        }
        if (!TextUtils.isEmpty(failingUrl)) {
            if (failingUrl.equals(webView.getUrl())) {
                //Do your own error operations, such as customizing the error page
            }
        }
    }
    
    /**
     * This method is called back only if there is an error loading the main page. This is the best way to show how to load the error page.
     * However, if the wrong page is displayed directly, it is likely to be misjudged, causing the illusion that the user often fails to load the page.
     * Because different WebView implementations may be different, we need to exclude several miscalculation examples first:
     *      1.The url that failed to load is not the same as the url in WebView, exclude;
     *      2.errorCode=-1,It indicates that the error is unknown. In order to ensure no misjudgment, it is excluded
     *      3failingUrl=null&errorCode=-12,Exclude because the error url is empty instead of error bad url
     * @param webView                                           webView
     * @param webResourceRequest                                webResourceRequest
     * @param webResourceError                                  webResourceError
     */
    @Override
    public void onReceivedError(WebView webView, WebResourceRequest webResourceRequest,
                                WebResourceError webResourceError) {
        super.onReceivedError(webView, webResourceRequest, webResourceError);
    }
    
    /**
     * Any error generated by HTTP request will call back this method, including html document request of main page, iframe, picture and other resource requests.
     * In this callback, due to a lot of mixed requests, it is not suitable for displaying the page with loading errors, but for monitoring alarms.
     * When a URL or a resource receives a large number of alarms, it indicates that there may be problems with the page or resource. In this case, relevant operations can respond to the modification in a timely manner.
     * @param webView                                           webView
     * @param webResourceRequest                                webResourceRequest
     * @param webResourceResponse                               webResourceResponse
     */
    @Override
    public void onReceivedHttpError(WebView webView, WebResourceRequest webResourceRequest,
                                    WebResourceResponse webResourceResponse) {
        super.onReceivedHttpError(webView, webResourceRequest, webResourceResponse);
    }
    
    /**
     * Any HTTPS request that encounters an SSL error will call back this method.
     * The correct way is to let users choose whether to trust this website. At this time, a trust selection box can pop up for users to choose (most regular browsers do this).
     * Sometimes, for their own website, you can let some specific websites, no matter whether there is a problem with their certificates, let users trust it.
     * Pit: sometimes some mobile phones open the page to report an error. Trick: make all the secondary domains of your website trustworthy.
     * @param webView                                           webView
     * @param sslErrorHandler                                   sslErrorHandler
     * @param sslError                                          sslError
     */
    @Override
    public void onReceivedSslError(WebView webView, SslErrorHandler sslErrorHandler, SslError sslError) {
        super.onReceivedSslError(webView, sslErrorHandler, sslError);
        //Judge whether the website is trustworthy and compare it with its own website host
        if (WebViewUtils.isYCHost(webView.getUrl())) {
            //If it is your own website, continue to use SSL certificate
            sslErrorHandler.proceed();
        } else {
            super.onReceivedSslError(webView, sslErrorHandler, sslError);
        }
    }
    

3.0.7 webView to prevent memory leakage

3.0.9 video / picture width exceeds screen

  • The video playback width or picture width is larger than the width set by webView, exceeding the screen: at this time, ws.setLoadWithOverviewMode(false) can be set;
  • Another way to keep the picture within the scope of the screen is to use css
    <script type="text/javascript">
       var tables = document.getElementsByTagName("img");  //table label found
         for(var i = 0; i<tables.length; i++){  // Change one by one
                tables[i].style.width = "100%";  // Change width to 100%
                 tables[i].style.height = "auto";
         }
    </script>
    
  • Set through the setting property of webView
    // Whether the width of web page content can be greater than the width of WebView control
    ws.setLoadWithOverviewMode(false);
    

3.1.0 how to ensure js security

  • How Android and js communicate
    • In order to achieve dynamic interaction with Web pages, Android applications allow WebView to inject Java objects into Web pages through the webview.addjavascript interface interface interface. Page Javascript scripts can directly reference the object and call its methods.
    • This kind of application generally has the following code:
      webView.addJavascriptInterface(javaObj, "jsObj");
      
    • This code exposes the javaObj object to the js script, which can be referenced by the jsObj object and call the javaObj method. Combined with Java's reflection mechanism, you can execute any Java code through js script, and the relevant codes are as follows:
      • When the affected application executes the above script, it executes the command specified by someCmd.
      <script>
        function execute(cmdArgs) {
            return jsobj.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
        }
      
        execute(someCmd);
      </script>
      
  • addJavascriptInterface any command execution vulnerability
    • Using js to interact with html in webView is a good way. However, in Android 4.2 (16, including 4.2) and below, if addJavascriptInterface is used, there will be a vulnerability injected into js interface; after 4.2, the vulnerability will be solved due to the addition of @ JavascriptInterface in Google.
  • @What does the JavaScript interface annotation do
    • Before, any Public function can be accessed in JS code, and Java object inheritance relationship will lead to many Public functions can be accessed in JS, one of the important functions is getClass(). Then JS can access some other content through reflection. By introducing the @ JavascriptInterface annotation, only the functions of the @ JavascriptInterface annotation can be accessed in JS. This enhances security.

3.1.1 how to enable hardware acceleration by code

  • Turning on hardware and software to speed up the performance improvement is still obvious, but it will consume more memory. Call the code api directly, webView.setOpenLayerType(true);

3.1.2 WebView setting cookies

  • Why should cookie s be set on h5 page? It is mainly to avoid repeated login of webpage. Its function is to record the login information of users. It is not necessary to log in again next time.
  • How to set cookies in the code, as shown below
    /**
     * Synchronous cookie
     *
     * @param url               address
     * @param cookieList        Cookie value to be added in the form of key value pair: key=value
     */
    private void syncCookie (Context context , String url, ArrayList<String> cookieList) {
        //Initialization
        CookieSyncManager.createInstance(context);
        //Get objects
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.setAcceptCookie(true);
        //remove
        cookieManager.removeSessionCookie();
        //Add to
        if (cookieList != null && cookieList.size() > 0) {
            for (String cookie : cookieList) {
                cookieManager.setCookie(url, cookie);
            }
        }
        String cookies = cookieManager.getCookie(url);
        X5LogUtils.d("cookies-------"+cookies);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            cookieManager.flush();
        } else {
            CookieSyncManager.getInstance().sync();
        }
    }
    
  • In android, you can set cookies for WebView by calling this method one sentence before calling webView.loadUrl(url)
    • Note: it must be noted here that the setting Cookie cannot be set after calling, otherwise the setting Cookie is invalid. It needs to be verified. Why???
    webView.getSettings().setBuiltInZoomControls(true);  
    webView.getSettings().setJavaScriptEnabled(true);  
    
  • There are also cross domain issues: domain A: test1.yc.com domain B: test2.yc.com
    • To produce A Cookie that can be accessed by both domain A and domain B in domain A, you need to set the domain of the Cookie to. yc.com;
    • If you want to produce A Cookie in domain A that domain A can't access, you need to set the Cookie to test2.yc.com.
  • Expiration mechanism of cookies
    • You can set the effective time field of the Cookie to be: expires or max age.
      • expires: time point of expiration
      • Max age: effective duration, in seconds.
    • If the max age of the Cookie is set to a negative number, or the expires field is set to the expiration time point, the Cookie will be deleted from the database after the database is updated. If the max age and expires fields of the Cookie are set to the normal expiration date, the data will be deleted when the database is updated after the expiration date.
  • Here are a few useful interfaces:
    • Get all cookies under a url: CookieManager.getInstance().getCookie(url)
    • Judge whether WebView accepts cookies: CookieManager.getInstance().acceptCookie()
    • Clear session cookies: cookiemanager. Getinstance(). Removesessioncookies (valuecallback < Boolean > callback)
    • Clear all cookies: cookiemanager. Getinstance(). Removeallcookies (valuecallback < Boolean > callback)
    • Cookie persistence: CookieManager.getInstance().flush()
    • Set cookie for a host: Cookie manager. Getinstance(). Setcookie (string URL, string value)

3.1.4 webView loading web page does not display pictures

  • webView starts from Lollipop(5.0). By default, webView does not allow mixed mode. http resources cannot be loaded in https, while https links may be used in development, but the pictures in the links may be http, so it needs to be set to enable.
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
            mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
    }
    mWebView.getSettings().setBlockNetworkImage(false);
    

3.1.5 bypass certificate verification vulnerability

  • webviewClient has the onReceivedError method. When there is a certificate verification error, we can use handler. Processed() in this method to ignore the certificate verification and continue to load the web page, or use the default handler.cancel() to load the terminal.
    • Because we used handler. Processed (), this "bypass certificate validation vulnerability" was created. If you are sure that all pages can meet the certificate verification, you do not need to use handler. Processed()
    @SuppressLint("NewApi")
    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        //Handler. Processed(); / / accept certificate
        super.onReceivedSslError(view, handler, error);
    }
    

3.1.6 allowFileAccess vulnerability

  • If webView.getSettings().setAllowFileAccess(boolean) is set to true, you will face this problem; the vulnerability is caused by WebView's delayed execution of Javascript and html file replacement.
    • The solution is to prevent WebView page from opening local files, that is: webView.getSettings().setAllowFileAccess(false);
    • Or more directly prohibit the use of JavaScript: WebView. Getsettings(). Setjavascript enabled (false);

3.1.7 WebView nested ScrollView problem

  • Problem description
    • When the WebView is nested in the ScrollView, if the WebView first loads a high height web page, and then loads a low height web page, the height of the WebView will not be adaptive, and a large number of blanks will appear at the bottom.
  • Solution

3.1.8 Click to enlarge the picture in WebView

  • Load js first
    //Mapping js objects to java objects
    webView.addJavascriptInterface(new ImageJavascriptInterface(context), "imagelistener");
    
  • After the html is loaded, add the click js function of the listening image, which can be operated in the onPageFinished method
    @Override
    public void onPageFinished(WebView view, String url) {
        X5LogUtils.i("-------onPageFinished-------"+url);
        //After the html is loaded, add the click js function to monitor the image
        //addImageClickListener();
        addImageArrayClickListener(webView);
    }
    
  • See the implementation method of addImageArrayClickListener for details.
    /**
     * android Interact with js:
     * First we get the tag img of the loaded image in html
     * Then take out its corresponding src attribute
     * Loop through to set the click event of the picture
     * Pass src as a parameter to java code
     * This loop puts the pictures into the array, which is passed in when js calls the local method.
     * Of course, if you use method one to get pictures, the local method does not need to pass in this array
     * Find the code block labeled img through js code, and set the click listening method to connect with the local openImage method
     * @param webView                       webview
     */
    private void addImageArrayClickListener(WebView webView) {
        webView.loadUrl("javascript:(function(){" +
                "var objs = document.getElementsByTagName(\"img\"); " +
                "var array=new Array(); " +
                "for(var j=0;j<objs.length;j++){" +
                "    array[j]=objs[j].src; " +
                "}"+
                "for(var i=0;i<objs.length;i++)  " +
                "{"
                + "    objs[i].onclick=function()  " +
                "    {  "
                + "        window.imagelistener.openImage(this.src,array);  " +
                "    }  " +
                "}" +
                "})()");
    }
    
  • Finally, let's see what js communication interface has done
    public class ImageJavascriptInterface {
    
        private Context context;
        private String[] imageUrls;
    
        public ImageJavascriptInterface(Context context,String[] imageUrls) {
            this.context = context;
            this.imageUrls = imageUrls;
        }
    
        public ImageJavascriptInterface(Context context) {
            this.context = context;
        }
    
        /**
         * How the interface returns
         */
        @android.webkit.JavascriptInterface
        public void openImage(String img , String[] imageUrls) {
            Intent intent = new Intent();
            intent.putExtra("imageUrls", imageUrls);
            intent.putExtra("curImageUrl", img);
    //        intent.setClass(context, PhotoBrowserActivity.class);
            context.startActivity(intent);
            for (int i = 0; i < imageUrls.length; i++) {
                Log.e("Picture address"+i,imageUrls[i].toString());
            }
        }
    }
    

3.1.9 do not render / execute during page sliding

  • In some requirements, there will be some ceiling elements, such as navigation bar, purchase button, etc.; when the page scrolls over the height of the element, the element will be adsorbed on the top of the screen. A problem in WebView: during page scrolling, Scroll Event does not trigger. In addition, WebView has various restrictions during scrolling:
    • setTimeout and setInterval are not triggered.
    • GIF animation does not play.
    • Many callbacks are delayed until the page stops scrolling.
    • Background position: fixed is not supported.
  • These restrictions make it difficult for WebView to have a better experience during scrolling. Most of these restrictions cannot be broken through, but at least some support can be made for the ceiling function. Solutions:
    • On Android, listening to the touchMove event can switch the position of the element during sliding (it is invalid during inertial motion).
  • Refer to the technical articles of meituan

3.2.0 hijacking and injection by operators

  • Because the page code loaded by WebView is obtained dynamically from the server, these codes will be easily stolen or modified by the intermediate link, and the main problems are local operators and some WiFi. Issues monitored include:
    • Ignore communication rules to force caching of pages.
    • The header has been tampered with.
    • The page is injected with ads.
    • The page was redirected.
    • The page is redirected and iframe is re iframe to the new page, and the frame embeds the advertisement.
    • HTTPS request blocked.
    • DNS hijacking.
  • For the behavior of page injection, there are some solutions:
    • 1. Use CSP (Content Security Policy)
    • 2.HTTPS.
      • HTTPS can prevent the page from being hijacked or injected, but its side effect is also obvious. The performance and success rate of network transmission will decline, and HTTPS pages will require that all the resources referenced in the page are also HTTPS, and the migration cost for large websites is not low. One problem of HTTPS is that once the underlying layer wants to tamper with or hijack, the whole link will fail and the page cannot be displayed. This will bring a problem: originally, the page will only be injected with advertisements, and the advertisements will be blocked by CSP. However, after HTTPS is adopted, the whole page cannot be displayed due to hijacking.
      • For static pages with low security requirements, we need to weigh the advantages and disadvantages of HTTPS.
    • 3.App uses Socket proxy to request
      • If the HTTP request is easy to be intercepted, it is also a way to let App convert it to a Socket request and proxy the access of WebView.
      • Generally, illegal operators or WiFi can only intercept HTTP (S) requests, but can't intercept customized package content, so it can basically solve the problem of injection and hijacking.
      • There are also problems with Socket proxy requests:
      • First of all, the HTML request of the page using the client agent will lose the ability of parsing while downloading; according to the previous description, the browser will start parsing as soon as the HTML receives part of the content, load the parsed outer chain, pictures, etc., and execute the inline script At present, WebView does not expose this kind of streaming HTML interface. Only after the client completely downloads the HTML, it can be injected into WebView. So its performance will be affected.
      • Secondly, there are many technical problems, such as jump processing, cache processing, CDN processing, etc If you don't pay attention, you will bury some big holes.
      • In addition, there are some other methods, such as MD5 detection of the page, page static page packaging and downloading, and so on. The specific choice depends on the specific scenario.

3.2.1 solve the problem of slow resource loading

  • In terms of resource preloading, there are actually many ways, as follows:
    • The first way is to use WebView's own caching mechanism: if we visit a page in the APP and visit the page again in a short time, we will feel that the second time we open it is much smoother and the loading speed is shorter than the first time. This is because WebView itself will do some caching, as long as it has opened resources, it will try Cache to the local area. When the second access is needed, it will read directly from the local area. However, this reading is actually unstable. After the cache is turned off, or the cache fails, the system will automatically clear it. We can't control it. Based on this WebView's own cache, there is a resource preloading scheme. When the application starts, we can open a pixel WebView and visit our common resources in advance. If we use these resources again in the subsequent page opening, they can be obtained locally, and the page loading time will be shorter.
    • The second solution is to build and manage the cache by yourself: put these resources that need to be preloaded in the APP, which may be put in advance or downloaded later. The problem is how to cache the front-end pages. The first one is that the front-end can replace the resource URL when H5 is packaged, so that the local resources can be directly accessed The second is that the client can intercept all requests from these pages for replacement.
    • See the technical articles of meituan for details: Meituan public comments on Hybrid construction

3.2.2 judge whether it has scrolled to the bottom of the page

  • The getScrollY() method returns the distance between the top of the current visible area and the top of the entire page, that is, the distance of the current content scrolling
  • getHeight() or getBottom() methods return the height of the current WebView container
  • getContentHeight returns the height of the entire html, but it is not the same as the height of the current entire page. Because WebView has zoom function, the height of the current entire page should actually be the height of the original html multiplied by the zoom ratio. Therefore, the correct judgment method for the corrected result should be:
    if(WebView.getContentHeight*WebView.getScale() == (webview.getHeight()+WebView.getScrollY())){
        //Already at the bottom
    }
    

3.2.3 loading html garbled code with loadData

  • The WebView.loadData(String data, String mimeType, String encoding)) method can be used to load a small section of content of an entire HTML page. The first is the content we need WebView to display. The second is that we tell WebView about the class type of content we display. Generally, the third is bytecode, but when we use it, there will be some holes
    • Mingming has specified the encoding format as UTF-8, but there will be garbled code when loading
    String html = new String("<h3>I am loadData() Title</h3><p>&nbsp&nbsp I am his content</p>");
    webView.loadData(html, "text/html", "UTF-8");
    
  • Use loadData() or loadDataWithBaseURL() to load an HTML snippet
    • Data: it is the data type to be loaded, but English characters: '#', '%', ','? 'cannot appear in the data. If there are four characters, you can use% 23,% 25,% 27,% 3F to replace them. During normal tests, your data contains these characters, but there will be no problem. When there is a problem, you can replace them.
      • %, no page error will be found, the page is full of garbled code. See code for disorderly code style.
      • #, will invalidate your goBack, but can use it. The return button takes effect, but cannot be returned.
      • \When converting with? I will report an error, because it will use \ as an escape character. If you use two-level escape, it will not work. I have nothing to say about it.
    • When we use loadData, it means that we need to convert all illegal characters, which will have a great impact on the running speed, because when we use it, we will use many% numbers in the page stytle. The more data the page has, the slower it will run.
    • In data, some people will encounter the problem of Chinese scrambling. The solution is to pass the parameter to "utf-8", and the page coding format must also be utf-8, so that the coding will be unified without scrambling. I haven't tried any other coding.
  • Solution
    String html = new String("<h3>I am loadData() Title</h3><p>&nbsp&nbsp I am his content</p>");
    webView.loadData(html, "text/html;charset=UTF-8", "null");
    

3.2.4 WebView download progress cannot be monitored

3.2.5 302 / 303 redirection in WebView

  • Professional narration
    • 302 redirection, also known as 302, represents temporary transfer
  • Network interpretation
    • Redirection is a knowledge in web page making. Several examples show you that if you are in a forum login page, you fill in your account number, password, and click login. If your account number is correct, you will automatically jump to the homepage of the forum and return to the login page if it is not correct. Here's the meaning of redirection. In other words, redirection is to set a constraint on the web page, and when the condition is met, it will automatically transfer to other web pages and web addresses. For example, if you enter a website link, you can enter the website directly. If there is an error, you can jump to another page.
  • For instance
    • Describe the situation of this problem, that is, WebView first loads A link, and then clicks A B link on WebView to load. B link will automatically jump to C link. At this time, WebView's goback method will be called to return to load B link, but B link will jump to C link again, resulting in no way to return to A link interface (of course, some friends say, press it twice quickly Return key - that is to say, after two consecutive gobacks are triggered, you can return to A link, but not all users understand this, and the operation is disgusting.) , which is the redirection problem.
  • Implement Sliding Monitoring and gracefully handle Backstack of WebView
    • Can WebView know if a url is 301 / 302? Of course, WebView can get the request information and response information of the url, which can be easily realized according to the code in the header. In fact, it is so easy to give it to WebView to handle the redirection (return false). At this time, press the return key to return to the page before the redirection normally. (PS: it can be seen from the above chapters that WebView is an independent apk after 5.0, which can be upgraded independently. The new version of WebView implementation certainly handles redirection.)
    • However, the business has a demand for url interception, so it is certain that all cases cannot be handled by WebView. In order to solve the problem of url interception, another idea is introduced in this paper, which judges the redirection through the user's touch event. For details, see the ScrollWebView in project lib!

04. About reference

05. About the x5 open source library YCWebView

5.0.1 leading edge description

  • Based on Tencent's x5 sealed source database, to improve the development efficiency of webView, you need to save about 60% of your time cost. This case supports the processing of js interactive logic without coupling, exposing the loading progress of progress bar at the same time, monitoring the abnormal error status, supporting video playback and full frequency, supporting the loading of word, xls, ppt, pdf, txt and other file documents, texting, calling, emailing, opening file operation and uploading pictures, evoking native App and x5 library as the latest version, with powerful functions.

5.0.2 functions and advantages of the library

  • To improve the development efficiency of webView, you need to save about 60% of your time cost, one click initialization operation;
  • It supports the interactive logic of js, which is convenient, fast, uncoupled and easy to operate;
  • Expose the progress bar loading progress, end, and exception status (divided into various states: no network, 404, onReceivedError, sslError exception, etc.) listener to the developer;
  • Support video playing, can switch to full frequency playing video, can rotate the screen, expose video operation monitoring listener to developers;
  • Integrated Tencent x5 WebView, the latest version, powerful;
  • Support the operation of opening files, such as opening album, and then selecting image upload, compatible version (5.0);
  • It supports loading documents such as word, xls, ppt, pdf, txt, etc. the usage method is very simple;
  • Support to set up the progress bar of simulated wechat loading H5 page, which is completely uncoupled, easy to operate and greatly improves the user experience;

5.0.3 project address

Keywords: Mobile Android Javascript network Java

Added by vapokerpro on Mon, 04 Nov 2019 13:51:17 +0200