編輯:關於Android編程
完整項目:https://github.com/snailycy/android_jsbridge
1.1 配置WebView
public void configWebView() { try { WebSettings settings = this.mWebView.getSettings(); settings.setJavaScriptEnabled(true); settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setDatabaseEnabled(true); settings.setBuiltInZoomControls(false); settings.setDomStorageEnabled(true); settings.setAppCacheEnabled(true); //設置localStorage存儲路徑 String localStorageDBPath = this.mWebView.getContext().getFilesDir().getAbsolutePath(); settings.setDatabasePath(localStorageDBPath); this.mWebView.setWebViewClient(new JSWebViewClient(this)); this.mWebView.setWebChromeClient(new JSWebChromeClient(this)); } catch (Exception e) { LogUtils.e(TAG, "configWebView error."); } }
1.2 安卓端攔截js的請求在WebChromeClient類中的onJsAlert方法中處理
注:如果是用addJavascriptInterface的方式接受js請求,那麼在android 4.2系統以下版本有js注入漏洞(在4.2及以上系統時引入@JavascriptInterface可避免)
@Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { if (message.startsWith(JS_REQUEST_PREFIX)) { if (this.jsBridge == null) { result.cancel(); return true; } parseJSProtocol(message); result.cancel(); return true; } return super.onJsAlert(view, url, message, result); }
1.3 自定義JS 請求協議:myjsbridge:///request?class=指定調用的類名&method=指定調用的方法名¶ms=指定的參數&callId=指定的請求ID
解析時按照協議格式分別解析出類名,方法名,參數,callId
/** * 解析JS協議 * * @param message: myjsbridge:///request?class=指定調用的類名&method=指定調用的方法名¶ms=指定的參數&callId=指定的請求ID */ private void parseJSProtocol(String message) { String[] tokens = message.substring(JS_REQUEST_PREFIX.length()).split("&"); String target = null; String method = null; String params = null; long callID = -1; for (String token : tokens) { String[] pair = token.split("="); if (pair.length != 2) { continue; } try { String key = pair[0]; String value = Uri.decode(pair[1]); if (JS_REQUEST_CLASS_KEY.equals(key)) { target = value; } else if (JS_REQUEST_METHOD_KEY.equals(key)) { method = value; } else if (JS_REQUEST_PARAMETERS_KEY.equals(key)) { params = value; } else if (JS_REQUEST_CALL_ID_KEY.equals(key)) { callID = Long.parseLong(value); } } catch (Exception e) { // Ignores. } } if (target != null && method != null && callID >= 0) { this.jsBridge.requestAndroid(target, method, params, callID); } }
1.4 拿到對應的類名,方法名,參數後通過發射調用對應的jsapi
/** * 由JS發起的對android端的請求 * * @param className 類名 * @param methodName 方法名 * @param params 參數 * @param callID 請求ID */ public void requestAndroid(final String className, final String methodName, final String params, final long callID) { this.mWebView.post(new Runnable() { @Override public void run() { try { //拼接全類名: 包名.jsapi.className String fullClassName = mWebView.getContext().getPackageName() + ".jsapi" + "." + className; Class cls = Class.forName(fullClassName); //JSAPI 方法形參為(JSBridge jsbridge,long callId,JSONObject params) Method declaredMethod = cls.getDeclaredMethod(methodName, JSBridge.class, Long.class, JSONObject.class); Object instance = cls.newInstance(); //將請求參數轉換成JSONObject JSONObject requestParams; try { requestParams = new JSONObject(params); } catch (JSONException e) { requestParams = new JSONObject(); } //反射調用JSAPI declaredMethod.invoke(instance, JSBridge.this, callID, requestParams); } catch (Exception e) { reportError(callID); } } }); LogUtils.d(TAG, "requestAndroid : " + className + " , " + methodName + " , " + params); }
1.5 jsapi demo
public class JSUIControl { public void showToast(JSBridge jsBridge, Long callId, JSONObject requestParams) { String content = requestParams.optString("content"); Toast.makeText(jsBridge.getActivity(), content, Toast.LENGTH_LONG).show(); //回調JS jsBridge.reportSuccess(callId); } }
1.6 jsapi處理完邏輯後,將結果回調給js
/** * 回調JS * * @param callID 請求ID (由JS請求android端時帶過來的請求ID) * @param type JSAPI執行成功與否 * @param params 回傳參數 */ private void callbackJS(long callID, JSCallbackType type, String params) { try { if (callID < 0) { return; } //組裝回調js StringBuilder js = new StringBuilder("javascript:"); js.append(MY_JS_BRIDGE); js.append(".callbackFromNative("); js.append(callID); js.append(","); js.append(type.getValue()); if (TextUtils.isEmpty(params)) { js.append(",{});"); } else { js.append(","); js.append(params); js.append(");"); } String callbackJS = js.toString(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //4.4及以上使用evaluateJavascript this.mWebView.evaluateJavascript(callbackJS, null); } else { this.mWebView.loadUrl(callbackJS); } LogUtils.d(TAG, "callbackJS : " + callbackJS); } catch (Exception e) { //ignore } }
1.7 js端使用alert方式調用android接口:
var json = JSON.stringify({"content":"js call native!"}); alert("myjsbridge:///request?class=JSUIControl&method=showToast¶ms="+ encodeURIComponent(json)+"&callId=1");
使用:
//1.實例化JSBridge,配置WebView JSBridge jsBridge = new JSBridge(this, webview); jsBridge.configWebView(); //2.WebView 加載網頁資源 webview.loadUrl("file:///android_asset/demo.html");
然後結合業務,自定義jsapi
完整項目:https://github.com/snailycy/android_jsbridge
首先是運行結果由於通訊錄在手機裡是以數據庫貯存的 所以我們可以通過一個方法 context.getContentResolver().query(Phone.CONTEN
1,使用SharedPrefrences用於簡單少量的數據,數據的格式簡單:都是普通的字符串,標量類型的值等,比如各種配置信息等等SharedPrefrences與Edi
本文實例講述了Android中ImageView使用網絡圖片資源的方法。分享給大家供大家參考。具體如下:很多時候我們不想把東西都放在APK裡面,或者是不能放進去,這時候我
先占個位置,下次翻譯~ :p During normal app use, the foreground activity is sometimes obstruct