自從有了Webview與JS交互,而我又使用過并且有了一定的理解哈误,真心的體會(huì)到讓開發(fā)者能在web頁相關(guān)開發(fā)為所欲為慰丛。demo地址
比如,在一個(gè)活動(dòng)頁面,需要用戶登陸后的userId簿盅,才能領(lǐng)取活動(dòng)頁面的獎(jiǎng)品挥下,怎么獲取呢?這個(gè)時(shí)候桨醋,你可以通過js跳轉(zhuǎn)到登錄頁棚瘟,登陸成功帶著userId回到活動(dòng)頁,就可以進(jìn)行下一步動(dòng)作了喜最,美滋滋~
上面只是個(gè)簡單的例子偎蘸,本章的主角是DSbridge,不過瞬内,在這之前迷雪,我們先回顧下,在沒封裝的時(shí)候虫蝶,Webview與JS交互的實(shí)現(xiàn)章咧。(我之前一直在用的方式)
/**js調(diào)用android端的方法:**/
//主要看showLog這個(gè)方法
public void initView() {
webView.getSettings().setJavaScriptEnabled(true); webView.addJavascriptInterface(new TestEntity(),"test");
}
public classTestEntity {
@JavascriptInterface
public void showLog(String data) {
Log.i("android",data);
}
}
js調(diào)用showLog()方法:
function handleAndroidMethod() {
test.showLog('hhh');
}
android 調(diào)用js 的方法:
//js里面方法
function jsMethod(data) {
....方法實(shí)現(xiàn)
}
android這邊這樣調(diào)用:
webview.loadUrl('jsMethod('測試')')
很好,傳統(tǒng)的是如上這樣實(shí)現(xiàn)能真,我們回到DSbridge赁严,DSbridge其實(shí)就是在這樣的交互中封裝了一下。那么在分析封裝實(shí)現(xiàn)之前粉铐,我們來看看疼约,DSbridge完成交互中的流程。(還是以分享的例子)
android需要處理的:
public void initView() {
webView= (DWebView) findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.setJavascriptInterface(newJsApiEntity(this));
}
public class JsApiEntity {
private Activity mActivity;
public JsApiEntity(Activity mActivity) {
this.mActivity= mActivity;
}
//for synchronous invocation 同步
@JavascriptInterface
String testSyn(JSONObject jsonObject)throwsJSONException {
return jsonObject.getString("msg") +"[syn call]";
}
//for asynchronous invocation 異步
//分享相關(guān)
@JavascriptInterface
void share(JSONObject jsonObject,CompletionHandler handler)throwsJSONException {
EventBus.getDefault().post(jsonObject,"SHOW_SHARE_DIALOG");
handler.complete("對應(yīng)后臺(tái)里面的flag");//回調(diào)數(shù)據(jù)給H5
}
}
js那邊調(diào)用:
<script>
var title = "我是標(biāo)題";
var content = "我是內(nèi)容";
var url = "落地頁的鏈接";
function share() {
dsBridge.call("share", {title: title,content:content,
imageUrl: "圖片鏈接喲",url:url}, function(flag){alert(flag);})
}
</script>
很對蝙泼,上面就是使用DSbridge之后的使用詳情了程剥。其實(shí)還是挺有意思的,只要調(diào)用dsBridge.call()就可以了踱承,參數(shù)是之前定義的方法名稱(比如 share)倡缠。
DSbridge做的事情,就是在js調(diào)用的原生代碼時(shí)候做了封裝茎活,每次調(diào)用都是用dsBridge.call()來處理昙沦,然后具體的就根據(jù)參數(shù)來區(qū)分了。
我們來看看他的主要源碼部分 DWebView.java:
void init() {
......
super.addJavascriptInterface(new Object() {
int i = 0;
@JavascriptInterface
@Keep
public String call(String methodName, String args) {
String error = "Js bridge method called, but there is not a JavascriptInterface object, please set JavascriptInterface object first!";
if(DWebView.this.jsb == null) {
Log.e("SynWebView", error);
return "";
} else {
Class cls = DWebView.this.jsb.getClass();
try {
boolean asyn = false;
JSONObject arg = new JSONObject(args);
final String callback = "";
Method e;
try {
callback = arg.getString("_dscbstub");
arg.remove("_dscbstub");
e = cls.getDeclaredMethod(methodName, new Class[]{JSONObject.class, CompletionHandler.class});
asyn = true;
} catch (Exception var12) {
e = cls.getDeclaredMethod(methodName, new Class[]{JSONObject.class});
}
if(e == null) {
error = "ERROR! \n Not find method \"" + methodName + "\" implementation! ";
Log.e("SynWebView", error);
DWebView.this.evaluateJavascript(String.format("alert(decodeURIComponent(\"%s\"})", new Object[]{error}));
return "";
}
JavascriptInterface annotation = (JavascriptInterface)e.getAnnotation(JavascriptInterface.class);
if(annotation != null) {
e.setAccessible(true);
Object ret;
if(asyn) {
ret = e.invoke(DWebView.this.jsb, new Object[]{arg, new CompletionHandler() {
public void complete(String retValue) {
this.complete(retValue, true);
}
public void complete() {
this.complete("", true);
}
public void setProgressData(String value) {
this.complete(value, false);
}
private void complete(String retValue, boolean complete) {
try {
if(retValue == null) {
retValue = "";
}
retValue = URLEncoder.encode(retValue, "UTF-8").replaceAll("\\+", "%20");
String e = String.format("%s(decodeURIComponent(\"%s\"));", new Object[]{callback, retValue});
if(complete) {
e = e + "delete window." + callback;
}
DWebView.this.evaluateJavascript(e);
} catch (UnsupportedEncodingException var4) {
var4.printStackTrace();
}
}
}});
} else {
ret = e.invoke(DWebView.this.jsb, new Object[]{arg});
}
if(ret == null) {
ret = "";
}
return ret.toString();
}
error = "Method " + methodName + " is not invoked, since it is not declared with JavascriptInterface annotation! ";
DWebView.this.evaluateJavascript(String.format("alert(\'ERROR \\n%s\')", new Object[]{error}));
Log.e("SynWebView", error);
} catch (Exception var13) {
DWebView.this.evaluateJavascript(String.format("alert(\'ERROR! \\n調(diào)用失斣乩蟆:函數(shù)名或參數(shù)錯(cuò)誤 [%s]\')", new Object[]{var13.getMessage()}));
var13.printStackTrace();
}
return "";
}
}
@JavascriptInterface
@Keep
public void returnValue(int id, String value) {
OnReturnValue handler = (OnReturnValue)DWebView.this.handlerMap.get(Integer.valueOf(id));
if(handler != null) {
handler.onValue(value);
DWebView.this.handlerMap.remove(Integer.valueOf(id));
}
}
@JavascriptInterface
@Keep
public void init() {
DWebView.this.injectJs();
}
}, "_dsbridge");
......
public void setJavascriptInterface(Object object) {
this.jsb = object;
}
}
很好盾饮,我們先看init方法(@Keep,其實(shí)就是為了不被混淆懒熙,不用在意)丘损,從
Class cls = DWebView.this.jsb.getClass()里面一開始就很明顯的說出了意圖,使用反射來處理工扎,通過e = cls.getDeclaredMethod獲取到this.jsb里面的方法徘钥,方法區(qū)分同步和異步,還有這個(gè)call方法是不是很眼熟肢娘?public String call(String methodName, String args) 就是js調(diào)用原生代碼里面用到的呈础,methodName就是需要調(diào)用的方法舆驶。
我們回到這個(gè)jsb ,其實(shí)就是通過setJavascriptInterface方法設(shè)置進(jìn)來的而钞,在上面例子里面其實(shí)就是我們定義的JsApiEntity對象沙廉,現(xiàn)在,通過反射臼节,把需要調(diào)用的Method (e)獲取到了撬陵。接下來肯定是通過注解來調(diào)用方法了,我們接著看:
JavascriptInterface annotation = (JavascriptInterface)e.getAnnotation(JavascriptInterface.class);
if(annotation != null) {'....''}
使用了@JavascriptInterface注解的网缝,才會(huì)進(jìn)入里面巨税,里面其實(shí)就是通過e.invoke來執(zhí)行js調(diào)用的方法了。
本文demo地址:https://github.com/niyige/DSBridgeWebDemo
有什么問題歡迎交流:893007592@qq.com
相關(guān)資料:
https://github.com/wendux/DSBridge-Android