Synchronous and asynchronous access mechanism between Android and WebView, androidwebview

Source: Internet
Author: User
Tags custom name

Synchronous and asynchronous access mechanism between Android and WebView, androidwebview

We all know that through WebView, we can develop our applications on the Android client using Web development.

If an application is simply a WebView, and all logic only needs to interact on the webpage, then we only need to interact with the server through html and javascript.

However, in many cases, our application is not simply a WebView, but may need to be applied to Android applications. For example, to take a photo, you need to call the Android camera, in some scenarios where mobile phone features need to be applied, such a mechanism is required to communicate with each other between javascript and Android, including synchronous and asynchronous methods, this mechanism is what I want to introduce in this article.

Step by step, let's start from the simplest point:

1) We need a WebView to display our page. First, we need to define a layout, which is very simple. It is a WebView, as shown below:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width= "match_parent"    android:layout_height= "match_parent"    android:orientation= "vertical">    <WebView        android:id="@+id/html5_webview"        android:layout_width="match_parent"        android:layout_height="match_parent" /></LinearLayout>

This WebView is the most basic control that carries the display of our pages. All the logic on the page and the logical data that needs to interact with the Android native environment are transmitted through it.

2) initialize the WebView in the corresponding Activity.

mWebView = (WebView) findViewById(R.id. html5_webview );WebSettings webSettings = mWebView.getSettings();webSettings.setJavaScriptCanOpenWindowsAutomatically( true );webSettings.setJavaScriptEnabled( true );webSettings.setLayoutAlgorithm(LayoutAlgorithm. NORMAL );mWebView.setWebChromeClient( new WebServerChromeClient());mWebView.setWebViewClient( new WebServerViewClient());mWebView.setVerticalScrollBarEnabled( false );mWebView.requestFocusFromTouch();mWebView.addJavascriptInterface( new AppJavascriptInterface(), "nintf" );


In the above Code, we mainly initialize WebView, but the most important code is the following:

2.1)webSettings.setJavaScriptEnabled( true );

Tell WebView to enable it to execute JavaScript statements. On an interactive webpage, javascript cannot be ignored.

2.2)mWebView.setWebChromeClient( new WebServerChromeClient());2.3)mWebView.setWebViewClient( new WebServerViewClient());
WebChromeClient and WebViewClient are two of the most important classes in WebView applications.

Through these two classes, WebView can capture all operations such as url loading and javascript execution on Html pages, in this way, the Android native environment can determine, parse, and return the corresponding processing results to the html webpage.

These two classes are the basis for interaction between html pages and the Android native environment. All operations that use html pages to interact with the background are implemented in these two classes, we will describe it in detail later.

2.4)mWebView.addJavascriptInterface( new AppJavascriptInterface(), "nintf" );

This JavascriptInterface is another window for interaction between the Android native environment and javascript.

Call the addJavascriptInterface method of mWebView to customize the nintf attribute of the Window object in mWebView) after,

You can directly call the Java object method in javascript.


3) Next, let's take a look at how javascript in Html interacts with the Android native environment.

According to the sequence mechanism of events, this concept is easy to understand.

This mechanism provides two methods to access the Android native environment: synchronous and asynchronous.

The concept of synchronization is that when I talk to you, if I haven't received your reply, I cannot communicate with others, and I have to wait there and keep waiting for you.

The concept of Asynchronization is that when I talk to you, if you haven't replied to me, I can also communicate with others, and when I receive your reply, let's see what you should do with your reply.

3.1) Synchronous access

In Javascript, we define a method as follows:

var exec = function (service, action, args) {        var json = {               "service" : service,               "action" : action       };        var result_str = prompt(JSON.stringify(json), args);        var result;        try {              result = JSON.parse(result_str);       } catch (e) {              console.error(e.message);       }        var status = result.status;        var message = result.message;        if (status == 0) {               return message;       } else {              console.error( "service:" + service + " action:" + action + " error:" + message);       }}

A typical call to this method is as follows:

exec( "Toast", "makeTextShort" , JSON.stringify(text));

Here, Toast and makeTextShort are the services and parameters for calling the Android native environment, which are managed in PluginManager and will be mentioned in the next article.

Here, we call the prompt method. Through this method, the WebChromeClient defined in WebView intercepts this method. The specific code is as follows:

class WebServerChromeClient extends WebChromeClient {     @Override    public boolean onJsPrompt(WebView view, String url, String message,              String defaultValue, JsPromptResult result) {            System.out.println( "onJsPrompt:defaultValue:" + defaultValue + "|" + url + "," + message);         JSONObject args = null ;         JSONObject head = null ;         try {              head = new JSONObject(message);                          args = new JSONObject(defaultValue);              String execResult = mPluginManager.exec(head.getString(IPlugin.SERVICE),                        head.getString(IPlugin.ACTION), args);              result.confirm(execResult);              return true;         ...            }         }

Here, we will reload the onJsPrompt method of WebChromeClient. When this method returns true, it means that WebChromeClient has handled this prompt event and does not need to continue distributing it;

When false is returned, the event will be passed to the WebView for processing.

Because we want to use this Prompt method to implement synchronous access between Javascript and Android native environments, We will intercept this event for processing here.

Here, through message and defaultValue, we can get the values of the two parameters of the prompt method in javascript. Here, they are Json data. After parsing them here, PluginManager will process them, finally, return the result to the confirm method of JsPromptResult.

This result is the return value of prompt in javascript.

In addition to JsPrompt, there are also similar Alert methods in Javascript. We know that the style of the Alert window popped up by the browser is very different from that in our mobile app. As an application, the style must have a set of unified standards, so in general, we will also intercept the Alert window in WebView. This logic will also be processed here, as shown below:

@ Overridepublic boolean onJsAlert (WebView view, String url, String message, final JsResult result) {System. out. println ("onJsAlert: url:" + url + "| message:" + message); if (isFinishing () {return true;} CustomAlertDialog. builder customBuilderres = new CustomAlertDialog. builder (DroidHtml5.this); customBuilderres. setTitle ("message prompt "). setMessage (message ). setPositiveButton ("OK", new DialogInterface. onClickListener () {public void onClick (DialogInterface diich, int which) {dialog. dismiss (); result. confirm ();}}). create (). show (); return true ;}


The above describes the synchronous access to the Android native environment. What is the asynchronous access method?

3.2) asynchronous access

Similarly, we will define the following method in Javascript:

var exec_asyn = function(service, action, args, success, fail) {       var json = {               "service" : service,               "action" : action       };                     var result = AndroidHtml5.callNative(json, args, success, fail);  }


We will call callNative of AndroidHtml5. This method has four parameters:

A) json: The called service and operation.

B) args: number of corresponding parameters

C) success: the callback party for success

D) fail: the callback party for failure

Typical calls are as follows:

var success = function(data){};var fail = functio(data){};exec_asyn( "Contacts", "openContacts" , '{}', success, fail);

Here, AndroidHtml5 is an object defined in Javascript. It provides methods for accessing the Android native environment and callback queue functions. It is defined as follows:
Var AndroidHtml5 = {idCounter: 0, // parameter Sequence Counter OUTPUT_RESULTS :{}, // CALLBACK_SUCCESS :{}, // method called when the output result is successful CALLBACK_FAIL: {}, // call method callNative: function (cmd, args, success, fail) {var key = "ID _" + (++ this. idCounter); window. nintf. setCmds (cmd, key); window. nintf. setArgs (args, key); if (typeof success! = 'Undefined') {AndroidHtml5.CALLBACK _ SUCCESS [key] = success;} else {AndroidHtml5.CALLBACK _ SUCCESS [key] = function (result) {};} if (typeof fail! = 'Undefined') {AndroidHtml5.CALLBACK _ FAIL [key] = fail;} else {AndroidHtml5.CALLBACK _ FAIL [key] = function (result ){};} // an Iframe will be defined below, and Iframe will load our custom url, starting with androidhtml: var iframe = document. createElement ("IFRAME"); iframe. setAttribute ("src", "androidhtml: // ready? Id = "+ key); document.doc umentElement. appendChild (iframe); iframe. parentNode. removeChild (iframe); iframe = null; return this. OUTPUT_RESULTS [key] ;}, callBackJs: function (result, key) {this. OUTPUT_RESULTS [key] = result; var obj = JSON. parse (result); var message = obj. message; var status = obj. status; if (status = 0) {if (typeof this. CALLBACK_SUCCESS [key]! = "Undefined") {setTimeout ("AndroidHtml5.CALLBACK _ SUCCESS ['" + key + "'] ('" + message + "')", 0 );}} else {if (typeof this. CALLBACK_FAIL! = "Undefined") {setTimeout ("AndroidHtml5.CALLBACK _ FAIL ['" + key + "'] ('" + message + "')", 0 );}}}};

In AndroidHtml5, there are several points worth attention.
A) Do you still remember the AppJavascriptInterface we set during WebView initialization? At that time, the custom name was "nintf". In javascript, we can directly use all the methods of this object.
window.nintf.setCmds(cmd, key);window.nintf.setArgs(args, key);

Let's also take a look at the methods in this AppJavascriptInterface, as follows:
public class AppJavascriptInterface implements java.io.Serializable {                private static Hashtable<String,String> CMDS = new Hashtable<String,String>();        private static Hashtable<String,String> ARGS = new Hashtable<String,String>();                       @JavascriptInterface        public void setCmds(String cmds, String id) {               CMDS .put(id, cmds);       }                   @JavascriptInterface        public void setArgs(String args, String id) {               ARGS .put(id, args);       }            public static String getCmdOnce(String id) {              String result = CMDS .get(id);               CMDS .remove(id);               return result;       }        public static String getArgOnce(String id) {              String result = ARGS .get(id);               ARGS .remove(id);               return result;       }}

This class is concise but not simple. by calling the set Method in the class in Javascript, the corresponding cmd and args parameters are saved, the purpose is to save multiple commands and operations in asynchronous requests and then obtain them in the Android native environment.

B) the second step is also the most important step. An Iframe will be created to declare a url in the Iframe and start with androidhtml.
As mentioned above, a WebViewClient is set during WebView initialization. The main function of this class is to load URLs on html pages, we can intercept this loading event, process it, and rewrite this loading event.
We use this point to trigger a Url interception event using an Iframe.
Let's take a look at how the asynchronous request is implemented in WebViewClient.

Class WebServerViewClient extends WebViewClient {Handler myHandler = new Handler () {...}; @ Override public boolean shouldOverrideUrlLoading (WebView view, String url) {if (url! = Null & url. startsWith ("androidhtml") {String id = url. substring (url. indexOf ("id =") + 3); JSONObject cmd = null; JSONObject arg = null; try {String cmds = AppJavascriptInterface. getCmdOnce (id); String args = AppJavascriptInterface. getArgOnce (id); cmd = new JSONObject (cmds); arg = new JSONObject (args);} catch (JSONException e1) {e1.printStackTrace (); return false ;} // initiate another thread to process the request. try {Asy NServiceHandler asyn = new AsynServiceHandlerImpl (); asyn. setKey (id); asyn. setService (cmd. getString ("service"); asyn. setAction (cmd. getString ("action"); asyn. setArgs (arg); asyn. setWebView (mWebView); asyn. setMessageHandler (myHandler); Thread thread = new Thread (asyn, "asyn _" + (threadIdCounter ++); thread. start ();} catch (Exception e) {e. printStackTrace (); return false;} return true ;} // If the url does not start with Androidhtml, WebView continues to process the url. View. loadUrl (url); return true ;}}
We can see that in this method, only URLs starting with androidhtml will be intercepted and processed by WebView.

Through AppJavascriptInterface, we take out the stored cmds, args, and other data in Javascript, and AsynServiceHandler starts a new thread for processing.

Let's take a look at how AsynServiceHandlerImpl is implemented,

public class AsynServiceHandlerImpl implements AsynServiceHandler {        @Override        public void run() {                        try {              final String responseBody = PluginManager.getInstance().exec(service,  action,args);                                   handler.post( new Runnable() {                      public void run() {                                  webView .loadUrl( "javascript:AndroidHtml5.callBackJs('"+responseBody+ "','" +key +"')" );                      }              });           } catch (PluginNotFoundException e) {               e.printStackTrace();           }       }
As you can see, after calling PluginManager to complete the corresponding commands and data, the callBackJs method of AndroidHtml5 will be executed through the loadUrl method of WebView.

With the key value, we can find the corresponding callback method in the callBackJs method of AndroidHtml5 for processing.

Therefore, by constructing an Iframe, loading a url starting with androidhtml, and then using the WebViewClient interface object of WebView, we can perform Asynchronous interaction with the Android native environment on the Html page.

In this article, we talked about the PluginManager class in several places, which is a class for managing the interaction interfaces between HTML and Android native environments.

Because it is unreasonable to put all the logic in WebViewClient or WebChromeClient for processing.

Therefore, we need to separate the logic implementation and interaction. This mechanism looks beautiful, practical, and easy to operate.



WebView access network problems in android development!

Check whether the INTERNET permission is declared in the configuration file. The permission.

Asynchronous Network Access Mechanism for android

When the asynchronous thread gets data, the main thread continues to go down. When it reaches where you print the data, the asynchronous thread does not get the data, so what you printed is empty. You can use the callback function provided by the asynchronous thread to call back the data, or you can write an API to call back the data.
 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.