h5原生混合開發已經有些年頭了,說實在的h5調用原生最好用的還是在h5發起新連結的時候攔截下來,然後原生做出反應。
思路是自定義一套原生h5互動協定,使用者在h5頁面點選的時候,h5發起一個跳轉(location=“”),用戶端攔截這個跳轉,如果符合協定的跳轉,就交給用戶端進行操作
否則傳回給浏覽器,進行正常的跳轉
定義:我們把h5發起讓原生處理的請求定義為action
代碼示例:
public class MyWebViewClient extends WebViewClient {
private MyWebView mWebView;
public MyWebViewClient(MyWebView webView) {
super();
mWebView = webView;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (NativeInvokeFilter.filter(url, mWebView)) {
return true;
} else {
return super.shouldOverrideUrlLoading(view, url);
}
}
}
自定義webview,統一使用該webview來處理h5調用原生的需求
package com.example.base.basetemplate.widget.MyWebView;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.webkit.WebSettings;
import android.webkit.WebView;
public class MyWebView extends WebView {
public MyWebView(Context context) {
super(context);
init();
}
public MyWebView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
@SuppressLint("SetJavaScriptEnabled")
private void init() {
setWebViewClient(new MyWebViewClient(this));
WebSettings settings = getSettings();
if (settings != null) {
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setAllowContentAccess(true);
settings.setTextSize(WebSettings.TextSize.NORMAL);
settings.setAllowFileAccess(false);
settings.setSavePassword(false);
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
settings.setBlockNetworkImage(false);
// TODO: SDK修改成21後再設定
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
// settings.setAppCacheEnabled(true);
// settings.setAppCacheMaxSize(1024 * 1024 * 8);
// String appCacheDir = getContext().getDir("cache", Context.MODE_PRIVATE).getPath();
// settings.setAppCachePath(appCacheDir);
}
setHorizontalScrollBarEnabled(false);
}
}
至于自定義webview的調用,就不再啰嗦了
NativeInvokeFilter
處理action的核心類
package com.example.base.basetemplate.nativeinvokefilter;
import android.text.TextUtils;
import com.example.base.basetemplate.nativeinvokefilter.handler.OneActionHandler;
import com.example.base.basetemplate.widget.MyWebView.MyWebView;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class NativeInvokeFilter {
//請求頭部
public static final String SCHEMA = "myappschema://myappschema?";
//此handler和java系統的handler沒有任何關系,易于擴充性展現在新加的action必須實作這個接口,統一放在同一個目錄下
public interface NativeHandler {
void handle(NativeInvokeProtocol nip, MyWebView webView);
}
private static final Map TYPE_MAP = new HashMap();
public enum Type {
UNKNOWN("unknown", null),
ONEACTION("oneaction",new OneActionHandler());
private NativeHandler mHandler;
Type(String key, NativeHandler handler) {
TYPE_MAP.put(key, this);
mHandler = handler;
}
public static Type parse(String key) {
if (TextUtils.isEmpty(key)) {
return UNKNOWN;
}
Type type = TYPE_MAP.get(key);
return type == null ? UNKNOWN : type;
}
public static NativeHandler getHandler(String key) {
return parse(key).mHandler;
}
}
public static boolean filter(String url, MyWebView webView) {
if (canParse(url)) {
NativeInvokeProtocol nip = NativeInvokeProtocol.fromParamString(
url.substring(SCHEMA.length()));
if (nip != null)
return handleInvoke(nip, webView);
}
return false;
}
private static boolean canParse(String url) {
if (TextUtils.isEmpty(url)) {
return false;
}
return url.startsWith(SCHEMA);
}
private static boolean handleInvoke(NativeInvokeProtocol nip, MyWebView webView) {
// 優先使用newAction字段
String realAction = null == nip.newAction || 0 == nip.newAction.length() ? nip.action : nip.newAction;
if (TextUtils.isEmpty(realAction))
return false;
NativeHandler handler = Type.getHandler(realAction);
if (handler != null) {
handler.handle(nip, webView);
return true;
} else
return false;
}
public static class NativeInvokeProtocol {
private static final String ACTION = "action";
private static final String NEW_ACTION = "newAction";
private static final String INFO = "info";
private static final String LEFT_BRACE = "{";
private static final String RIGHT_BRACE = "}";
private static final String EQUAL = "=";
private static final String AND = "&";
private static final String SHARP = "#";
private String action;
private String newAction;
private String url;
public String info;
public String getUrl() {
return url;
}
public String getAction() {
return action;
}
public static NativeInvokeProtocol fromParamString(String paramStr) {
NativeInvokeProtocol nip = new NativeInvokeProtocol();
if (TextUtils.isEmpty(paramStr)) {
return nip;
}
try {
paramStr = URLDecoder.decode(paramStr, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 循環掃描字元串,找出最外層的&(不在{}之間)所在位置,分隔字元串
int len = paramStr.length();
ArrayList outterAnds = new ArrayList();
// {或者}标記,遇到{加1,遇到}減1,等于0即表示目前掃描位置不在{}之間
int braceIndicator = 0;
for (int i = 0; i < len - 1; i++) {
String ch = paramStr.substring(i, i + 1);
if (ch.equals(AND) && braceIndicator == 0) {
outterAnds.add(i);
}
if (ch.equals(LEFT_BRACE)) {
braceIndicator++;
}
if (ch.equals(RIGHT_BRACE)) {
braceIndicator--;
}
}
// 加上字元串首尾位置
outterAnds.add(0, -1);
outterAnds.add(paramStr.length());
HashMap paramMap = new HashMap();
for (int i = 0; i < outterAnds.size() - 1; i++) {
String paramItemStr = paramStr.substring(outterAnds.get(i) + 1, outterAnds.get(i + 1));
int keyIndex = paramItemStr.indexOf(EQUAL);
String key = paramItemStr.substring(0, keyIndex);
String value = paramItemStr.substring(keyIndex + 1);
paramMap.put(key, value);
}
nip.action = paramMap.get(ACTION);
if(null!=nip.action && nip.action.length()>0 && nip.action.contains(SHARP))
nip.action = nip.action.split(SHARP)[0];
nip.info = paramMap.get(INFO);
nip.newAction = paramMap.get(NEW_ACTION);
return nip;
}
}
}
一個非常簡潔的handler
public class OneActionHandler implements NativeHandler {
@Override
public void handle(NativeInvokeProtocol nip, MyWebView webView) {
Toast.makeText(MyEnvironment.activity(),nip.info,Toast.LENGTH_SHORT).show();
}
}
html上的簡單調用
h5調用原生
測試
測試的時候,html可以放在assets檔案夾下面,或者放在伺服器上