混合开发,h5调用android原生模块 Action

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

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;

/**
 * Created by cgx on 2017-6-15.
 */

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;

/**
 * Created by cgx on 2017-6-15.
 */

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();

    /**
     * 日后可以在Type里面添加新的action,首先要定义好action对应的字段(第一个参数)和对应的handler
     */
    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;
    }

    /**
     * 检测传递的url,如果以SCHEMA开头就可以执行action的操作
     * @param url
     * @return
     */
    private static boolean canParse(String url) {
        if (TextUtils.isEmpty(url)) {
            return false;
        }
        return url.startsWith(SCHEMA);
    }

    /**
     * 执行handler的方法,首先查找TYPE_MAP,通过action来查找到对应的handler,如果有找得到就执行handler的handle方法
     * @param nip
     * @param webView
     * @return
     */
    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;
    }

    /**
     * 解析action链接头部的类,重点的变量是info,action返回的信息体,通常是一个json字符串
     * 变量action,对应具体的业务
     */
    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文件夹下面,或者放在服务器上


 

 



 

转载于:https://my.oschina.net/carbenson/blog/968183

你可能感兴趣的:(混合开发,h5调用android原生模块 Action)