Synchronization and asynchronous access mechanisms for Android and WebView

Source: Internet
Author: User
Tags custom name

As we all know, through webview, we can develop our application in the way of web development on the Android client.

If an application is simply a webview, all logic needs to be interactive on the web, then we just need to interact with the server via HTML and JavaScript.

However, in many cases, our application is not just a webview, it is possible to apply to the Android itself, such as taking pictures, you need to call the Android camera and so on, to generate vibration, in the need to apply to the characteristics of the phone in some scenarios, There is a need for such a mechanism to communicate with each other between JavaScript and Android, both synchronously and asynchronously, and this mechanism is what I want to introduce in this article.

Step by step, let's start with the simplest place:

1) need a webview to show our page, first define a layout, very simple, is a webview, as follows:

<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 our page, all the logic on the page that needs to interact with the Android native environment is transmitted through it.

2) in the corresponding activity, some initialization of the WebView

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, the main thing is to initialize some of the webview, but the most important of the code is the following sentence:

2.1) websettings.setjavascriptenabled (TRUE);

Tell WebView to allow it to execute JavaScript statements. There is no way to ignore JavaScript on an interactive Web page.

2.2) Mwebview.setwebchromeclient (new Webserverchromeclient ()); 2.3) Mwebview.setwebviewclient (new Webserverviewclient ());
Webchromeclient and Webviewclient are the two most important classes in the WebView application.

With these two classes, WebView is able to capture all the actions of URL loading, JavaScript execution, and so on in an HTML page, so that the events from the Web page can be judged, parsed, and then returned to the HTML page in the native environment of Android.

These two classes are the basis of the HTML page and Android native environment interaction, and all the actions that interact with the background via HTML pages are implemented in these two classes, which we will explain in detail later.

2.4) Mwebview.addjavascriptinterface (new Appjavascriptinterface (), "nintf");

This javascriptinterface is another window for Android native environment and JavaScript interaction.

With our custom Appjavascriptinterface class, call Mwebview's Addjavascriptinterface method to pass this object to the Nintf property of the Window object in Mwebview ("nintf" After the property name is customized), the

You can call this Java object's method directly in JavaScript.


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

We look at the sequence of events, so there is a succession of concepts that will be easier to understand.

In this set of mechanisms, there are two ways to access the Android native environment, one synchronous and one asynchronous.

The concept of synchronization is that when I communicate with you, if I have not received your reply, I can not communicate with other people, I have to wait there, have been waiting for you.

The concept of asynchrony is that when I communicate with you, if you haven't replied to me, I can still communicate with other people, and when I receive your reply, then go to see your reply, what you should do.

3.1) Simultaneous access

In JavaScript, we define a method that looks like this:

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 + "E Rror: "+ message);}       }

For this method, the typical invocation is as follows:

EXEC ("Toast", "Maketextshort", json.stringify (text));

The toast and maketextshort are the services and parameters to invoke the Android native environment, which are managed in PlugInManager and are mentioned in the next article.

Here, we call the prompt method, in this way, the webchromeclient defined in the webview will intercept such a 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 Webchromeclient Onjsprompt method, when this method returns true, it means that webchromeclient has handled the prompt event and does not need to continue distributing it;

When False is returned, this event continues to be passed to WebView, which is handled by WebView.

Since we are here to use this prompt method to implement synchronous access between JavaScript and the native Android environment, we are here to intercept this event for processing.

Here, through the message and DefaultValue, we can get the values of the two parameters of the prompt method in JavaScript, where they are JSON data, which are parsed here and processed by PlugInManager. Finally, the results are returned to the Jspromptresult confirm method.

This result is the return value of prompt in JavaScript.

In addition to jsprompt, there are similar to JavaScript in the alert method, we know that the browser pop-up alert window and our mobile phone app in the window style is very different, and as an application, style must have a uniform standard, so generally, We also intercept the alert window in WebView, which is also handled here, as follows:

@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 ("Informational Tip"). Setmessage (Message)                     . Setpositivebutton ("OK", new Dialoginterface.onclicklistener () {public                            void OnClick (dialoginterface dialog, int which) {                                  Dialog.dismiss () ;                                  Result.confirm ();                           }                     }). Create (). Show ();        return true;}


All described above is how to access the Android native environment synchronously, so what is the asynchronous way to access it?

3.2) Asynchronous access

Similarly, we will define one of the following methods 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'll call ANDROIDHTML5 's callnative, which has four parameters:

A) JSON: is the invocation of the service and operation

b) args: The corresponding number of parameters

c) Success: the callback when successful

d) Fail: callback On Failure

The typical invocation is as follows:

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

Here, ANDROIDHTML5 is an object defined in JavaScript that provides a way to access the Android native environment, as well as a callback queue function. It is defined as follows:
var AndroidHtml5 = {idcounter:0,//Parameter sequence counter output_results: {},//Result of output Callback_success: {},//The result of the output succeeds when the method called Callback_fail: {},//The output result fails when the method is called Callnative:func                           tion (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) {};                }//The following defines a iframe,iframe that will load our custom URLs to androidhtml: start                       var iframe = document.createelement ("iframe");              Iframe.setattribute ("src", "androidhtml://ready?id=" + key);              Document.documentElement.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 places we need to be aware of.
A) Do you remember the appjavascriptinterface we set in WebView initialization? The custom name was "nintf" at the time, and in JavaScript we were able to use all of this object's methods directly.
Window.nintf.setCmds (cmd, key); Window.nintf.setArgs (args, key);

We also 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 and not simple, by invoking the Set method in JavaScript, the corresponding cmd and args parameters are saved, in order to save the commands and operations in the asynchronous request, and then take them out in the native Android environment.

b) The second and most important step is to create an IFRAME, declare a URL in the IFRAME, and start with androidhtml:
As we mentioned above, WebView will set a webviewclient when initializing, and the main function of this class is that we can intercept the loading event, handle it, and rewrite the Load event when the URL load occurs in the HTML page.
And we happen to take advantage of this, using an IFRAME to trigger a URL intercept event.
Let's take a look at how the implementation of this 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 &AMP;&A mp                    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; }//Another thread processing request try {AsynservicehandlEr 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, it will continue to be processed by WebView.              View.loadurl (URL);       return true; }       }
As we can see, in this method, only URLs that start with androidhtml are intercepted, and the other URLs are handled by WebView.

And through Appjavascriptinterface, we will be in JavaScript saved Cmds and args and other data are taken out, and by Asynservicehandler City a thread to deal with.

Let's take a look at how Asynservicehandlerimpl is achieved,

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, the Callbackjs method of ANDROIDHTML5 is executed through the Loadurl method of WebView after the call to the PlugInManager operation finishes the corresponding command and data.

With the key value, we can retrieve the corresponding callback method in the Callbackjs method in ANDROIDHTML5, and handle it.

So, with the build of an IFRAME, loading URLs starting with androidhtml, and then using WebView's Webviewclient interface object, we can interact asynchronously with the Android native environment in an HTML page.

In this article, we talked about the PlugInManager class, which is a class that manages HTML and Android native environment interaction interfaces.

Because if all the logic is put in webviewclient or webchromeclient both to deal with, it is unreasonable, chaotic, complex, can not understand.

So we need to separate the logic implementation from the interaction, the mechanism is beautiful, practical and easy to operate.


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.