Android与WebView的同步和异步訪问机制
大家都知道。通过WebView,我们能够在Androidclient。用Web开发的方式来开发我们的应用。
假设一个应用就是单纯一个WebView。全部的逻辑都仅仅须要在网页上交互的话,那我们事实上就仅仅须要通过html和javascript来跟server交互就能够了。
可是非常多情况下。我们的应用不是单纯一个WebView就能够了,有可能会须要运用到Android本身的应用,比方拍照,就须要调用Android本身的照像机等,要产生震动,在须要运用到手机特性的一些场景下,肯定须要这么一套机制在javascript和Android之间互相通信,包含同步和异步的方式,而这套机制就是本文中我想要介绍的。
一步一步来。我们先从最简单的地方讲起:
1)须要一个WebView去展现我们的页面,首先定义一个布局。很easy。就是一个WebView,例如以下:
<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>
这个WebView就是承载我们页面展现的一个最主要的控件。全部在页面上的逻辑,须要跟Android原生环境交互的逻辑数据都是通过它来传输的。
2)在相应的Activity中,对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" );
在上面的代码中,主要是对WebView的一些初始化,但当中最重要的几句代码是这么几句:
2.1)webSettings.setJavaScriptEnabled( true );
告诉WebView,让它可以去运行JavaScript语句。在一个交互的网页上,javascript是没办法忽略的。
2.2)mWebView.setWebChromeClient( new WebServerChromeClient());
2.3)mWebView.setWebViewClient( new WebServerViewClient());
WebChromeClient和WebViewClient是WebView应用中的两个最重要的类。
通过这两个类,WebView可以捕获到Html页面中url的载入,javascript的运行等的全部操作,从而可以在Android的原生环境中对这些来自网页上的事件进行推断。解析,然后将相应的处理结果返回给html网页。
这两个类是html页面和Android原生环境交互的基础,全部通过html页面来跟后台交互的操作。都在这两个类里面实现,在后面我们还会具体说明。
2.4)mWebView.addJavascriptInterface( new AppJavascriptInterface(), "nintf" );
这个JavascriptInterface,则是Android原生环境和javascript交互的还有一个窗体。
将我们自己定义的AppJavascriptInterface类,调用mWebView的addJavascriptInterface方法,能够将这个对象传递给mWebView中Window对象的nintf属性("nintf"这个属性名称是自己定义的)之后,
就能够直接在javascript中调用这个Java对象的方法。
3)接下来,我们就先来看看在Html中的javascript是怎样跟Android原生环境来交互的。
我们依照事件发生的顺序机制来看,这样有个先后的概念,理解起来会easy一点。
在这套机制中,提供了两种訪问Android原生环境的方法,一种是同步的,一种是异步的。
同步的概念就是说,我在跟你交流的时候。假设我还没有收到你的回复,我是不能跟其它人交流的,我必须等在那里,一直等着你。
异步的概念就是说。我在跟你交流的时候,假设你还没有回复我。我还可以去跟其它人交流,而当我收到你的回复的时候。再去看看你的回复。应该要干些什么。
3.1)同步訪问
在Javascript中,我们定义了这样一个方法。例如以下:
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);
}
}
而对此方法的,典型的调用例如以下:
exec( "Toast", "makeTextShort" , JSON.stringify(text));
当中Toast和makeTextShort是要调用Android原生环境的服务和參数,这些都是在PluginManager中管理的,在下一篇文章中会提及到。
在这里,我们调用了prompt方法,通过这种方法,在WebView中定义的的WebChromeClient就会拦截到这样一个方法,详细代码例如以下:
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; ... }
}
在这里,我们会重载WebChromeClient的onJsPrompt方法,当此方法返回true的时候,就说明WebChromeClient已经处理了这个prompt事件。不须要再继续分发下去;
而当返回false的时候。则此事件会继续传递给WebView,由WebView来处理。
因为我们这里是要利用这个Prompt方法,来实现Javascript跟Android原生环境之间的同步訪问。所以我们在这里会拦截这个事件进行处理。
在这里,通过message和defaultValue,我们能够拿到javascript中prompt方法两个參数的值,在这里,它们是Json数据,在这里进行解析之后。由PluginManager来进行处理,最后将结果返回给JsPromptResult的confirm方法中。
此结果就是javascript中prompt的返回值了。
而除了JsPrompt。还有类似Javascript中的Alert方法等,我们知道浏览器弹出的Alert窗体跟我们手机应用中窗体风格样式是非常不一样的。而作为一个应用,风格肯定要有一套统一的标准。所以普通情况下,我们也会拦截WebView中的Alert窗体,这个逻辑也相同会是在这里处理。例如以下:
@Override
public 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( "信息提示" ).setMessage(message)
.setPositiveButton( "确定" , new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
result.confirm();
}
}).create().show();
return true ;
}
上面描写叙述的都是同步訪问Android原生环境的方式,那么。异步的訪问方式是怎么样的呢?
3.2)异步訪问
相同的,我们会在Javascript中定义例如以下一个方法:
var exec_asyn = function(service, action, args, success, fail) {
var json = {
"service" : service,
"action" : action
};
var result = AndroidHtml5.callNative(json, args, success, fail);
}
我们会调用AndroidHtml5的callNative,此方法有四个參数:
a)json:是调用的服务和操作
b)args: 相应的參数数
c)success : 成功时的回调方
d)fail:失败时的回调方
典型的调用例如以下:
var success = function(data){};
var fail = functio(data){};
exec_asyn( "Contacts", "openContacts" , '{}', success, fail);
在这里,AndroidHtml5是在Javascript中定义的一个对象,它提供了訪问Android原生环境的方法。以及回调的队列函数。它的定义例如以下:
var AndroidHtml5 = {
idCounter : 0, // 參数序列计数器
OUTPUT_RESULTS : {}, // 输出的结果
CALLBACK_SUCCESS : {}, // 输出的结果成功时调用的方法
CALLBACK_FAIL : {}, // 输出的结果失败时调用的方法 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){};
} //以下会定义一个Iframe,Iframe会去载入我们自己定义的url,以androidhtml:开头
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);
}
}
}
};
在AndroidHtml5中,有几个地方我们须要注意的。
a)大家还记得我们在WebView初始化时设置的AppJavascriptInterface吗?当时自己定义的名称就是"nintf",而在此时,在javascript中,我们就能够直接来运用这个对象全部的方法。
window.nintf.setCmds(cmd, key);
window.nintf.setArgs(args, key);
我们也看一下这个AppJavascriptInterface中的方法。例如以下:
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;
}
}
这个类是简洁而不简单。通过在Javascript中调用类中的set方法。将相应的cmd和args參数给保存起来。目的是为了保存异步请求中多次的命令和操作,然后在Android原生环境中再取出来。
b)第二步呢,也是最重要的一步。会创建一个Iframe。在Iframe中申明一个url,并且是以androidhtml: 开头的。
在上面我们提过。WebView在初始化的时候,会设置一个WebViewClient。这个类的主要作用就是,当在html页面中发生url载入的时候,我们能够拦截这个载入事件。进行处理,重写这次载入事件。
而我们正好是利用了这一点。利用一个Iframe来触发一次Url的拦截事件。
我们来看一下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 ;
}
//另起线程处理请求
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 ;
}
//假设url不是以Androidhtml开头的,则由WebView继续去处理。 view.loadUrl(url);
return true ;
} }
我们能够看到,在这方法中,首先仅仅有以androidhtml开头的url才会被拦截处理,而其它的url则还是由WebView进行处理。
而通过AppJavascriptInterface,我们将在Javascript中保存的cmds和args等数据都拿出来了。并由AsynServiceHandler新启一个线程去处理。
我们再来看看AsynServiceHandlerImpl是怎么实现的,
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();
}
}
能够看到,当调用PluginManager操作完相应的命令和数据之后,会通过WebView的loadUrl方法。去运行AndroidHtml5的callBackJs方法。
通过key值,我们就能够在AndroidHtml5中的callBackJs方法中找回到相应的回调方法,进行处理。
因此,通过一次Iframe的构建,载入以androidhtml开头的url。再利用WebView的WebViewClient接口对象。我们就行在Html页面中和Android原生环境进行异步的交互了。
在这一篇文章中,我们几处地方讲到了PluginManager这个类,这是一个管理HTML和Android原生环境交互接口的类。
由于假设把全部的逻辑都放在WebViewClient或者WebChromeClient这两个都来处理,这是不合理的,乱,复杂。看不懂。
所以我们须要把逻辑实现跟交互给分开来,这个机制才显得美丽。有用。易操作。
Android与WebView的同步和异步訪问机制的更多相关文章
- jQuery的AJax异步訪问
用一个样例用以说明:点击button,将input内用户输入的数据发送给服务端.并将结果返回给页面. 首先是html承载内容: <!DOCTYPE html> <html> & ...
- Android 訪问权限清单
Android权限设置 概述 权限 说明 訪问登记属性 android.permission.ACCESS_CHECKIN_PROPERTIES 读取或写入登记check-in数据库属性表的权限 获取 ...
- Android通过HTTP POST带參訪问asp.net网页
在看了网络上非常多视频关于android通过HTTP POST或者GET方式訪问网页并获取数据的方法. 自己也copy了一份来測试.并通过C#.NET搭建了一个简单的后台,但发现传參时,依照网上的方式 ...
- js eventLoop (使用chunk 同步变异步)
https://www.cnblogs.com/xiaohuochai/p/8527618.html 线程 javascript是单线程的语言,也就是说,同一个时间只能做一件事.而这个单线程的特性,与 ...
- 利用httpclient和多线程刷訪问量代码
缘起于玩唱吧,由于唱吧好友少,訪问量低,又不想加什么亲友团之类的,主要是太麻烦了,于是我就琢磨唱吧的訪问机制,准备用java的httpclient库来进行刷訪问量,想到动态IP反复使用就想到了用多线程 ...
- Android与WebView的插件管理机制
上一篇文章说到,当利用WebViewClient或者WebChromeClient来处理由html页面传过来的请求的时候,都会将相应的服务名称,操作方法和相应的參数数据传给一个叫PluginManag ...
- Android之 -WebView实现离线缓存阅读
前言 本篇博客要实现的是一个离线下载和离线阅读的功能,这是很多阅读类app都常见的一个功能,典型的应用就是网易新闻.什么是离线下载?其实这个概念是比较模糊,是离线之后下载呢,还是下载之后离线,但稍微有 ...
- Android在WebView上构建Web应用程序
原文链接:http://developer.android.com/guide/webapps/webview.html reference:http://developer.android.com/ ...
- Android之webview详解
文章大纲 一.webview基本介绍1.什么是webview2.为什么要使用webview3.webview基本操作 二.webview高级使用1.WebView状态2.资源加载3.WebView加载 ...
随机推荐
- 【BZOJ 3622】3622: 已经没有什么好害怕的了(DP+容斥原理)
3622: 已经没有什么好害怕的了 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 683 Solved: 328 Description Input ...
- 【BZOJ 4662】 4662: Snow (线段树+并查集)
4662: Snow Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 136 Solved: 47 Description 2333年的某一天,临冬突 ...
- JZYZOJ1311 邮局设置问题 dp
易得每两个点之间建立邮局的最好位置为两点最中间的点,两点之间如果没有奇数个数的点则中间两个点都可以...(自己画一下图可以看出随着右边点的增大最佳点的增大非常平滑...强迫症一本满足) w[i][ ...
- 【插头DP】BZOJ3125-city
开学忙成狗,刷题慢如蜗牛…… [题目大意] 给出一个m*n的矩阵里面有一些格子为障碍物,一些格子只能上下通行,一些格子只能左右通行,一些格子上下左右都能通行.问经过所有非障碍格子的哈密顿回路个数. [ ...
- AOP 面向切面 记录请求接口的日志
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点, ...
- Problem E: 深入浅出学算法006-求不定方程的所有解
Description 现有一方程ax+by=c,其中系数a.b.c均为整数,求符合条件的所有正整数解,要求按x由小到大排列,其中a b c 均为不大于1000的正整数 Input 多组测试数据,第一 ...
- [转]Android网格视图(GridView)
GridView的一些属性: 1.android:numColumns=”auto_fit” //GridView的列数设置为自动,也可以设置成2.3.4…… 2.android:columnWi ...
- Codeforces Round #202 (Div. 1) A. Mafia 贪心
A. Mafia Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/348/problem/A D ...
- hdu 5224 Tom and paper 水题
Tom and paper Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/show/6 ...
- hihocoder 1522 : F1 Score
题目链接 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和他的小伙伴们一起写了很多代码.时间一久有些代码究竟是不是自己写的,小Hi也分辨不出来了. 于是他实现 ...