Android原生与H5交互的实现

随着移动互联网的高速发展,常规的开发速度已经渐渐不能满足市场需求。原生H5混合开发应运而生,目前,市场上许多主流应用都有用到混合开发,例如支付宝、美团等。下面,结合我本人的开发经验,简单谈一下对混合开发的认识以及实现方式。

混合开发的优点

优点显而易见,由前端工程师写一个页面,多个平台都可以运行,省了android和ios工程师不少事,无形中提高了开发效率,节约了开发成本。

缺点

凡是使用过的人都知道,H5的界面显示在手机上,对点击、触摸、滑动等事件的响应并不如原生控件那样流畅,甚至还会出现卡顿。这样也很正常,如果体验跟原生控件一样好的话,也就没android(ios)工程师什么事了。

H5调用原生的方式

方式可能有多种,根据我本人的开发经验,我接触过两种方式。

第一种

1.首先对WebView进行初始化

WebSettings settings = webview.getSettings();
settings.setJavaScriptEnabled(true); //允许在WebView中使用js
  • 1
  • 2

2.创建一个类JavaScriptMetod,专门用来给js提供可调用的方法

3.创建该类的构造方法,提供两个参数,WebView对象和上下文对象

private Context mContext;
    private WebView mWebView;
    public JavaScriptMethod(Context context, WebView webView) {
        mContext = context;
        mWebView = webView;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4.创建一个字符串常量,作为android与js通信的接口,即字符串映射对象

public static final String JAVAINTERFACE = "javaInterface";
  • 1

5.接下来就是创建给js调用的方法,方法的参数接收一个json字符串(注意:在Android4.2之后,为了提高代码安全性,方法必须使用注解@JavascriptInterface,否则无法调用)

@JavascriptInterface
    //andorid4.2(包括android4.2)以上,如果不写该注解,js无法调用android方法
    public void showToast(String json){
        Toast.makeText(context, json, Toast.LENGTH_SHORT).show();
    }
  • 1
  • 2
  • 3
  • 4
  • 5

6.在WebView初始化代码中执行如下代码,

//创建上面创建类的对象
    JavaScriptMetod m = new JavaScriptMetod(this, webview);
    //其实就是告诉js,我提供给哪个对象给你调用,这样js就可以调用对象里面的方法
    //第二个参数就是该类中的字符串常量
    webview.addJavascriptInterface(m, JavaScriptMetod.javaInterface);
  • 1
  • 2
  • 3
  • 4
  • 5

现在,在js中就可以调用JavaScriptMetod中的方法了,调用方式如下

//参数一般为json格式
    var json = {"name":"javascript"};
    //javaInterface是上面所说的字符串映射对象
    window.javaInterface.showToast(JSON.stringify(json));
  • 1
  • 2
  • 3
  • 4

网络上介绍js与android原生交互的文章里,大部分都是上面这种方式,但是这种方式并不适用于ios,也就是说,window.javaInterface.showToast(JSON.stringify(json))这样的js代码并不适用于ios,如果用以上的方法,就得分别为android和ios各写一套js代码。这样很显然是不太合理的,所以在实际开发中,一般都使用接下来的第二种方法。

第二种

这种方法实现的思想是js发出一个url请求,并将所需的参数添加到该url中。android端通过webView.setWebViewClient()拦截url,解析url中携带的参数,并根据参数信息进行相应的操作。

1.与方法一相同,首先都需要对webview进行初始化

    WebSettings settings = webview.getSettings();
    settings.setJavaScriptEnabled(true); //允许在WebView中使用js
  • 1
  • 2

2.首先看js中的代码是怎么写的,

$("#showtoast").click(function () {
        var json = {"data": "I am a toast"};
       window.location.href="protocol://android?code=toast&data="+JSON.stringify(json);
    });
    $("#call").click(function () {
        var json = {"data": "10086"};
       window.location.href="protocol://android?code=call&data="+JSON.stringify(json);
    });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里定义两个点击事件,分别控制android显示吐司和打电话的操作。其中,protocol://android为自定义的H5与android间的通信协议,与http请求进行区分。code规定了要进行的操作,data为传输的数据。

2.android中的代码

webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                /**
                 * 通过判断拦截到的url是否含有pre,来辨别是http请求还是调用android方法的请求
                 */
                String pre = "protocol://android";
                if (!url.contains(pre)) {
                    //该url是http请求,用webview加载url
                    return false;
                }
                //该url是调用android方法的请求,通过解析url中的参数来执行相应方法
                Map map = getParamsMap(url, pre);
                String code = map.get("code");
                String data = map.get("data");
                parseCode(code, data);
                return true;
            }
        });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

其中,getParamsMap()方法从拦截到的url解析出code,data参数,parseCode()方法将根据不同的code进行相应的操作,代码如下:

private Map<String, String> getParamsMap(String url, String pre) {
        ArrayMap<String, String> queryStringMap = new ArrayMap<>();
        if (url.contains(pre)) {
            int index = url.indexOf(pre);
            int end = index + pre.length();
            String queryString = url.substring(end + 1);

            String[] queryStringSplit = queryString.split("&");

            String[] queryStringParam;
            for (String qs : queryStringSplit) {
                if (qs.toLowerCase().startsWith("data=")) {
                    //单独处理data项,避免data内部的&被拆分
                    int dataIndex = queryString.indexOf("data=");
                    String dataValue = queryString.substring(dataIndex + 5);
                    queryStringMap.put("data", dataValue);
                } else {
                    queryStringParam = qs.split("=");

                    String value = "";
                    if (queryStringParam.length > 1) {
                        //避免后台有时候不传值,如“key=”这种
                        value = queryStringParam[1];
                    }
                    queryStringMap.put(queryStringParam[0].toLowerCase(), value);
                }
            }
        }
        return queryStringMap;
    }

    private void parseCode(String code, String data) {
        if(code.equals("call")) {
            try {
                JSONObject json = new JSONObject(data);
                String phone = json.optString("data");
                //执行打电话的操作,具体代码省略
                PhoneUtils.call(this, phone);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return;
        }
        if(code.equals("toast")) {
            try {
                JSONObject json = new JSONObject(data);
                String toast = json.optString("data");
                Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return;
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

最后,特别说明一下shouldOverrideUrlLoading()方法的返回值问题,该方法的返回值有三种:

1.返回true,即根据代码逻辑执行相应操作,webview不加载该url;

2.返回false,除执行相应代码外,webview加载该url;

3.返回super.shouldOverrideUrlLoading(),点进父类中,我们可以看到,返回的还是false。

你可能感兴趣的:(混合开发)