快速搞定移动端混合开发基础

目录

写在前面

一、混合开发介绍

1.1、什么是混合开发?

1.2、混合开发优缺点

1.3、混合开发应用场景

1.4、了解混合开发的意义

二、混合开发的核心技术

2.1、混合开发核心技术——JSBridge

2.2、混合开发主流技术框架

2.3、JSBridge实现原理

三、JSBridge的实现方式

3.1、创建Native和Web工程

3.2、拦截URL Schema

3.3、注入JS API

3.4、带回调的JSBridge

3.5、JSBridge的开源实现

四、实战案例——开发一个简单的混合APP


写在前面

文章有点长,请注意保护您的眼睛,如若引起身体不适请自行关闭!

之前是定了个目标说是做到每周一更,很开心已经坚持了三周了,希望接下来能够再接再厉吧!今天抽空写个基础业务方面的——移动端混合开发基础。对于应用层来说,其实混合开发用的还是比较多的,相信你肯定也用过,很多为了快速开发快速迭代节省成本的业务,基本上都会采用这种方式去进行开发,今天就做个总结,整理一下这方面的基础知识点。

本篇代码地址:https://github.com/JArchie/JSBridgeDemo

一、混合开发介绍

1.1、什么是混合开发?

  • 它是一种开发模式,并不是一种具体的技术,英文名称叫Hybrid APP
  • 混合使用Native(Android、iOS)和Web(HTML、CSS、JavaScript)技术开发

这两类技术各自的特点通过混合开发可以把它们结合起来,比如对于原生开发Android一般使用的就是Java和Kotlin,iOS一般使用的就是Objective-C和Swift。原生开发的实际操作过程都是比较慢的,改动一行代码你就需要重新编译打包安装到硬件设备上面看效果,如果有问题又要重复这个过程,在你发布升级的时候,用户需要重新安装这个APP,所以推送升级也是个问题。而Web技术就可以克服这些缺点,比如你修改代码之后,刷新一下页面就可以立马看到修改后的结果,当它发布的时候,部署之后用户下次打开就可以看到最新的页面,所以它并不存在发包覆盖率的问题,把这两种技术结合起来,就可以各自做各自擅长的事情,这也是混合开发的意义所在,也因此来说混合开发只是一种模式。

1.2、混合开发优缺点

其实上面也提到了一部分,这里做个总结性的概括:

  • 优点:开发快(介于原生和Web开发的速度之间)、易更新(原生开发作为容器,里面的内容采用Web技术部署静态页面,用户动态加载更新)、开发周期短(开发速度快加之具有即时发布更新的特点)
  • 缺点:性能问题(相较于纯原生开发Web技术在切页和手势动画等方面没有原生流畅)、兼容性问题(原生承载Web网页相当于原生充当了浏览器的角色,浏览器不同的版本支持的Web特性是不一样的)
  • Android 5.0+iOS 9.0+ 缺点不再明显(硬件设备自身性能和操作系统版本提升)

1.3、混合开发应用场景

①、微信公众号,通过JSSDK连接Native端和Web端

我们可以把微信看做是原生开发,微信公众号内部的网页则是Web开发,它们通过JSSDK连接起来,这样在做微信公众号的时候可以使用Web技术发布更新,同时可以利用JSSDK使用到微信提供的原生能力。

②、微信小程序,通过内置框架连接Native段和Web端

微信小程序相比较于微信公众号则更进一步,它相当于是将JSSDK和Web技术内置整合了一套框架,内部会将这些调用和渲染连接到Native端,使用它的框架编写程序,它自身会做桥接的工作。

1.4、了解混合开发的意义

①、更好的使用第三方平台

比如我们要开发微信小程序或者在某个Web平台上开发一些插件,我们就需要了解混合开发的原理和机制,这样在使用第三方平台提供的接口调用时若发生某些特定的行为,可以深刻的理解生命周期和调用的链路,从而能够更好的使用第三方平台。

②、更灵活的技术方案选型

比如领导让你开发一款新的移动应用时,如果你了解混合开发,那么你可以选择纯原生开发,也可以通过原生结合Web技术进行混合开发,技术选型更加灵活。

③、具备搭建平台和输出服务的能力

当你了解混合开发之后,你可以搭建一个类似微信公众号或微信小程序这样的平台,可以把原生APP提供各种接口能力并输出成JSSDK。

二、混合开发的核心技术

2.1、混合开发核心技术——JSBridge

  • 实现Native端和Web端双向通信的一种机制
  • 以JavaScript引擎或WebView容器为媒介
  • 通过约定协议进行通信

2.2、混合开发主流技术框架

  • Web渲染:Cordova(前身是PhoneGap)
  • 原生渲染:React Native、Weex
  • 混合渲染:微信小程序

2.3、JSBridge实现原理

由上图可以看出,上面是Web端,下面是Native端,JSBridge在中间起了一个桥梁作用,透过JSBridge,Web端可以直接调用Native端的Java接口,Native端也可以调用Web端提供的JavaScript接口,从而可以实现Web端和Native端之间的双向通信。可以把这种模式和客户端/服务器这种C/S模式作一个对比,把Web端比作客户端,Native端比作Server端,在Web端调用Native端接口时和客户端向Server端发出一个请求类似,其中JSBridge在这个过程充当的就是HTTP协议的角色。

  • 类比Client/Server模式
  • 将Native端原生接口封装成JavaScript接口
  • 将Web端JavaScript接口封装成原生接口
  • Web端和Native端之间双向通信

三、JSBridge的实现方式

3.1、创建Native和Web工程

在说JSBridge的几种实现方式之前,我们还得做一件事,先来创建两个Demo工程,一个是Android端,一个是Web端,然后在这个基础上通过实际的代码来介绍几种JSBridge的用法,这样看的也比较清晰明了!

OK,先来创建一个Web工程,这里我使用的是IDEA,然后在/Users/Jarchie/Desktop/AndroidProjects/Web/src 这个目录下创建一个index.html页面,内容也很简单,如下图所示:

这里我使用node安装一个http静态服务来跑这个web页面,打开终端进入到页面所在目录,使用npm i -g http-server 命令安装静态资源服务器,执行 http-server 它会在当前服务下启一个静态资源服务,这样可以拿到一个地址,如下所示:

然后可以直接通过这个地址在浏览器中访问这个静态页面,如下图:

然后再来创建一个Android工程,界面也比较简单,上面是Web页面,下面是Android页面,布局代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#eaeaea"
    android:orientation="vertical">

    <WebView
        android:id="@+id/mWebview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical">

        <EditText
            android:id="@+id/mEditText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请输入内容"
            android:textSize="45sp" />

        <TextView
            android:id="@+id/mShowBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#BBB5B5"
            android:text="显示Web弹窗"
            android:textSize="45sp" />

        <TextView
            android:id="@+id/mRefreshWeb"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:background="#BBB5B5"
            android:text="刷新Web页面"
            android:textSize="45sp" />
    </LinearLayout>

</LinearLayout>

然后在Activity页面中进行数据初始化操作:

package com.jarchie.jsbridge;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.webkit.WebView;
import android.widget.EditText;
import android.widget.TextView;

import java.util.Date;

/**
 * 作者: 乔布奇
 * 日期: 2020-04-11 13:50
 * 邮箱: [email protected]
 * 描述: Native端页面展示层,上方为Web页面,下方为Android页面
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private WebView mWebview;
    private EditText mEditText;
    private TextView mShowBtn,mRefreshWeb;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initListener();
        initData();
    }

    //初始化数据加载
    private void initData() {
        //为了每次获取的页面都是最新的,加了个时间戳,防止页面有缓存
        mWebview.loadUrl("http://192.168.0.102:8080/?timestamp" + new Date().getTime());
    }

    //初始化事件监听
    private void initListener() {
        mShowBtn.setOnClickListener(this);
        mRefreshWeb.setOnClickListener(this);
    }

    //初始化绑定控件
    private void initView() {
        mWebview = findViewById(R.id.mWebview);
        mEditText = findViewById(R.id.mEditText);
        mShowBtn = findViewById(R.id.mShowBtn);
        mRefreshWeb = findViewById(R.id.mRefreshWeb);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.mShowBtn:
                break;
            case R.id.mRefreshWeb:
                mWebview.loadUrl("http://192.168.0.102:8080/?timestamp" + new Date().getTime());
                break;
        }
    }
}

接着把它运行一下,看下结果:

到了这里,准备工作就已经做完了,接下来就来说JSBridge的几种用法了!

Web端调用Native端的实现方式有两种:①、拦截WebView请求的URL Schema;②、向WebView注入JS API

原生端调用Web端的实现方式只有一种:直接执行js代码去调用web端代码

3.2、拦截URL Schema

3.2.1、原理及特点简介

  • URL Schema是类URL的一种请求格式
  • <protocol>://<domain>/<path>?<query> 比如:https://www.google.com/search?keyword=xxx
  • 自定义JSBridge通信的URL Schema
  • jsbridge://<method>?<params> 比如:jsbridge://showToast?name=jarchie&age=27

上面的这个例子中自定义的Schema是以jsbridge开头,后面的showToast是调用的原生方法名,问号后面是需要传给这个方法的参数对,那么它是怎么实现拦截来调用原生方法的呢?它的原理这里用一张图片来说明:

原生端的WebView加载Web网页之后,Web网页中发出的所有请求都会经过WebView组件,所以原生端可以去重写WebView组件里面的方法从而拦截Web网页中的请求,对请求的地址作出判断,如果它是符合自定义jsbridge的格式,就进行解析,解析之后拿到对应的方法名及相关参数,去调用对应的原生方法,如果不是自定义的URL Schema,比如它是一个http协议,在正常的拦截之后就进行转发请求真正的服务。这个过程Web端虽然没有真正的调用Native端的方法,但它间接实现了调用的过程,从逻辑上来看就是Web端调用了原生端的方法,这就是它的实现机制。

  • 优点:兼容性好(Android4.4以下也是支持的)
  • 缺点:不直观、URL长度有限制

3.2.2、代码实战

(一)、原生端调用Web端弹窗展示内容

在写代码之前先来理一下业务逻辑,我们需要实现的效果是:在原生端输入内容,然后点击按钮调用Web端弹窗展示。

OK,按照这个顺序一步一步来写,首先要给原生的Webview设置支持js脚本:

mWebview.getSettings().setJavaScriptEnabled(true);

接着封装一个方法,将原生端输入的内容传入,然后执行一段js代码,调用Web端弹窗展示这个内容:

//Native端调用Web端方法,只有一种方式,直接执行js代码
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void showWebDialog(String content){
    String jsCode = String.format("window.showWebDialog('%s')",content);
    mWebview.evaluateJavascript(jsCode,null);
}

添加点击事件,将输入框的内容传入该方法:

case R.id.mShowBtn://点击按钮获取输入框值,并传入对应的调用Web端的方法中
    showWebDialog(mEditText.getText().toString().trim());
break;

在Web端的showWebDialog方法中调用window展示:

<script>
    window.showWebDialog = content => window.alert(content);
</script>

这样我们就完成了,来看下效果,如下图所示:

(二)、Web端调用原生端弹窗展示内容

这次的业务逻辑是反向操作,在Web端输入内容,点击Web端按钮调用原生端的弹窗展示这个内容。

首先,在Web端给文档添加一个监听事件,在DOM加载完成之后获取对应的DOM元素,然后给按钮添加一个点击事件,拿到输入框的值,接着封装一个方法,将获取的输入框的值传入,在方法内部实现调用原生端的弹窗,这里就是通过自定义URL Schema的方式去实现,具体的代码如下:

    <script>
        document.addEventListener('DOMContentLoaded', e => {
            const editText = document.querySelector('#editText');
            const showBtn = document.querySelector('#showBtn');
            showBtn.addEventListener('click', e => {
                const inputValue = editText.value;
            showNativeDialog(inputValue)
            })
        })
        function showNativeDialog(content) {
            window.alert('jsbridge://showNativeDialog?content=' + content);
        }
    </script>

然后我们在原生端自定义WebChromeClient拦截Alert方法,通过解析URL Schema获取Web端输入的值:

//自定义WebChromeClient拦截自定义的URL Schema
    private class MyWebChromeClient extends WebChromeClient{
        @Override
        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
            if (!message.startsWith("jsbridge://")){
                return super.onJsAlert(view, url, message, result);
            }
            String content = message.substring(message.indexOf("=")+1);
            showNativeDialog(content);
            result.confirm();
            return true;
        }
    }

然后再封装一个显示原生弹窗的方法showNativeDialog:

//Web端调用原生端方法
    private void showNativeDialog(String content){
        new AlertDialog.Builder(this)
                .setTitle("Web端调用Native端")
                .setMessage(content)
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                })
                .create()
                .show();
    }

这样也就完成了,来看下效果:

3.3、注入JS API

3.3.1、原理及特点简介

这种方式相比较于自定义URL Schema的方式更加直观,从下图中也能够看出:

APP可以将原生的Java接口注入到WebView中,在WebView中暴露出一个JS对象,JS对象方法名跟原生的接口方法名是一一对应的,Web端就可以直接引用这个暴露的全局JS对象,通过这个对象调用到原生端的方法。

  • 优点:简单直观
  • 缺点:有兼容性问题(Android 4.2+,之前存在兼容性和安全漏洞,现在随着系统升级这些问题也都有对应的解决办法,实际应用中优先推荐这种方式)

3.3.2、代码实战

原生端调用Web端的代码和上面的一样不用做任何的改动,因为它就一种实现方式,直接执行js脚本。

重点来看Web端调用原生端的这块业务,这里使用JS注入API的方式来实现。首先来看下原生端如何修改?我们这里使用WebView的addJavaScriptInterface方法暴露全局的js对象,需要传入一个js对象和需要暴露的js对象的方法名,所以这里写一个内部类,就是需要暴露的js对象及相关的方法:

class NativeBridge {
        private Context mContext;

        NativeBridge(Context context) {
            this.mContext = context;
        }

        //注意必须加这个注解
        @JavascriptInterface
        public void showNativeDialog(String content) {
            new AlertDialog.Builder(mContext)
                    .setTitle("Web端调用Native端")
                    .setMessage(content)
                    .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.cancel();
                        }
                    })
                    .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.cancel();
                        }
                    })
                    .create()
                    .show();
        }
    }
mWebview.addJavascriptInterface(new NativeBridge(this), "NativeBridge");

然后看Web端,这里给文档添加事件监听,给按钮添加点击事件,获取输入框的值这一部分不用改动,需要改动的是我们封装的调用原生弹窗的方法:

    <script>
        window.showWebDialog = content => window.alert(content);
        document.addEventListener('DOMContentLoaded', e => {
            const editText = document.querySelector('#editText');
            const showBtn = document.querySelector('#showBtn');
            showBtn.addEventListener('click', e => {
                const inputValue = editText.value;
            showNativeDialog(inputValue)
            })
        })
        function showNativeDialog(content) {
            window.NativeBridge.showNativeDialog(content);
        }
    </script>

来看下运行的效果,如下图所示:

3.4、带回调的JSBridge

3.4.1、简介及原理说明

在实际应用场景中,我们经常需要在对端执行结果后需要把执行结果给返回,比如:Web端获取Native端输入框的值,那么这就需要支持回调的JSBridge了,什么是带回调的JSBridge?这里总结亮点:

  • 在对端执行操作并返回结果
  • 有输入有输出才是完整的调用

如何实现带回调的JSBridge?

从上图中可以看到,在WebView实现单向调用的时候,可以在参数中加一个特殊的标记callback id,Native端收到Web端的调用请求之后,会去检查参数中是否带有callback id,如果有,就立即执行一次调用,从Native端调用Web端接收消息的方法,将Native端执行的结果传回给Web端,Web端拿到结果之后,同时会检查callback id跟之前请求的callback id是否一致,这样就知道这个结果是哪一次调用所返回的,从而实现了支持带回调的JSBridge。

3.4.2、代码实战

(一)、Web端展示原生端输入框内容

首先在web端新加一个按钮获取Native端输入:

<div>
    <button id="showBtn2">获取Native输入</button>
</div>

然后在js中保存这个按钮的引用,并给这个按钮添加一个点击事件,在点击事件内部获取web端的输入并用原生弹窗展示:

const showBtn2 = document.querySelector('#showBtn2');
showBtn2.addEventListener('click', e=> {
    window.JSSDK.getNativeEdittextValue(value => window.alert('Native端输入值:'+value))
})

这里封装了一个JSSDK,这里创建创建一个回调id,并使用Map将其保存,每次请求都产生一个新的回调id,并切提供两个支持回调的方法:获取原生输入框的值、接收原生消息返回的值:

        let id = 1
        const callbackMap = {}
        window.JSSDK = {
            getNativeEdittextValue(callback){
                const callbackId = id++
                callbackMap[callbackId] = callback
                NativeBridge.getNativeEdittextValue(callbackId)
            },
            receiveMessage(callbackId,value){
                if (callbackMap[callbackId]){
                    callbackMap[callbackId](value)
                }
            }
        }

然后在Native这边新增一个获取输入框值的方法,并调用web端方法,这里同样的就是构造js代码:

class NativeBridge {
        private Context mContext;

        NativeBridge(Context context) {
            this.mContext = context;
        }

        @JavascriptInterface
        public void getNativeEdittextValue(int callbackId){
            final MainActivity mainActivity = (MainActivity) mContext;
            String value = mainActivity.mEditText.getText().toString().trim();
            final String jsCode = String.format("window.JSSDK.receiveMessage('%s','%s')",callbackId,value);
            mainActivity.runOnUiThread(new Runnable() {
                @RequiresApi(api = Build.VERSION_CODES.KITKAT)
                @Override
                public void run() {
                    mainActivity.mWebview.evaluateJavascript(jsCode,null);
                }
            });
        }
    }

这样就写完了,来看下运行效果:

(二)、Native端展示Web端输入框内容

首先在原生端页面上添加一个按钮mShowBtn2,绑定控件并添加点击事件,代码都很简单就不贴了,后面大家自己看源码吧。

然后同样的封装一个NativeSDK,内部提供两个方法:获取Web端输入框的值并传入一个回调接口,接收Web消息回传的值:

interface Callback {
        void invoke(String value);
    }

    class NativeSDK {
        private Context mContext;
        private int id = 1;
        private Map<Integer, Callback> callbackMap = new HashMap();

        NativeSDK(Context context) {
            this.mContext = context;
        }

        void getWebEditTextValue(Callback callback) {
            int callbackId = id++;
            callbackMap.put(callbackId, callback);
            final String jsCode = String.format("window.JSSDK.getWebEditTextValue(%s)", callbackId);
            ((MainActivity) mContext).runOnUiThread(new Runnable() {
                @RequiresApi(api = Build.VERSION_CODES.KITKAT)
                @Override
                public void run() {
                    ((MainActivity) mContext).mWebview.evaluateJavascript(jsCode, null);
                }
            });
        }

        void receiveMessage(int callbackId, String value) {
            if (callbackMap.containsKey(callbackId)) {
                callbackMap.get(callbackId).invoke(value);
            }
        }
    }

然后在NativeBridge中添加一个接收web端回传值的方法:

@JavascriptInterface
public void receiveMessage(int callbackId, String value) {
    ((MainActivity) mContext).nativeSDK.receiveMessage(callbackId, value);
}

接着同样定义一个显示原生窗口的方法:

//获取Web端输入的值并展示
    private void showNativeDialog2(String content) {
        new AlertDialog.Builder(this)
                .setTitle("获取Web端输入的值")
                .setMessage(content)
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                })
                .create()
                .show();
    }

然后实例化我们之前定义的Native SDK,点击按钮通过sdk调用对应的方法,展示弹窗:

case R.id.mShowBtn2:
    nativeSDK.getWebEditTextValue(new Callback() {
        @Override
        public void invoke(String value) {
            showNativeDialog2("Web 输入值:" + value);
        }
    });
    break;

最后修改web端,在JSSDK中定义getWebEditTextValue方法,将输入框的值通过NativeBridge的receiveMessage方法传回:

getWebEditTextValue(callbackId){
    const editText = document.querySelector('#editText');
    const value = editText.value
    NativeBridge.receiveMessage(callbackId,value)
}

来看下运行结果:

3.5、JSBridge的开源实现

3.5.1、JSBridge开源库介绍

首先我们学习JSBridge应当掌握它的实现原理,具备造轮子的能力,但是我们应该避免重复造轮子,尽量复用开源社区已有的实现,并且这些开源库都是经过大量的实践验证目前已经很稳定了,对于JSBridge来说有哪些优秀的开元实现呢?

  • JSBridge:基于拦截URL Schema
  • DSBridge:基于注入JS API

掌握这两类实现方式基本上可以应对大部分的场景了,大家可以根据自己的需要去选择,这里我选择使用第二种方式来介绍它的用法,接下来我会使用DSBridge来重写我们上面的Demo,下面给大家看一下DSBridge的相关介绍:

3.5.2、DSBridge重写Demo

(一)、Web端展示Native端输入框内容

首先来看下Web端的实现:根据文档先要引入dsbridge.js,引入之后会在全局暴露一个dsBridge的全局变量,通过这个变量可以调用一些原生提供的方法,这里通过dsBridge.call方法去调用原生的getNativeEditTextValue方法,获取到输入框的值之后会得到一个回调,在回调内部通过Web弹窗将获取的值显示出来:

<script src="https://unpkg.com/[email protected]/dist/dsbridge.js"> </script>
    <script>
        document.addEventListener('DOMContentLoaded', e => {
            const showBtn2 = document.querySelector('#showBtn2');
            showBtn2.addEventListener('click', e => {
                dsBridge.call('getNativeEditTextValue','',value => {
                    window.alert('Native端的输入值:' + value)
                })
            })
        })
    </script>

原生端的DWebView(DSBridge中提供的,我们需要使用这个WebView)中提供了一个注册原生接口的方法addJavascriptObject,然后提供一个自定义的JSAPI的类,类中统一管理提供给webview的方法:

mWebview.addJavascriptObject(new JSAPI(this),null);
class JSAPI{
        private Context mContext;
        
        public JSAPI(Context context){
            this.mContext = context;
        }

        @JavascriptInterface
        public void getNativeEditTextValue(Object msg, CompletionHandler<String> handler){
            String content = ((MainActivity)mContext).mEditText.getText().toString().trim();
            handler.complete(content);
        }
    }

来看下运行结果:

(二)、Native端展示Web端输入框内容

首先Native端在按钮的点击事件触发时,通过DWebView调用callHandler方法,这个方法有三个参数分别是:要调用的Web端的api名称、传递的参数、结果回调,这里定义web端方法名为getWebEditTextValue,在回调中调用显示原生弹窗的方法:

mWebview.callHandler("getWebEditTextValue", null, new OnReturnValue<String>() {
                    @Override
                    public void onValue(String retValue) {
                        showNativeDialog("Web端输入值:" + retValue);
                    }
                });

然后Web端通过dsBridge调用register去注册一个web方法给原生端调用,并且可以将返回值返回给原生端:

dsBridge.register('getWebEditTextValue', () => {
            const editText = document.querySelector('#editText')
            return editText.value
        })

来看下运行结果:

OK,写到这里关于JSBridge相关的知识点就写完了,从上面的实现过程相信大家也能看出,使用开源库来实现JSBridge代码还是相当简洁的,内部都是做了高度封装,我们只需要简单的调用几个API就可以实现我们开始一大堆代码才能完成的效果,所以实际开发中还是推荐大家使用开源库来实现

四、实战案例——开发一个简单的混合APP

文章的最后一部分通过DSBridge这个开源框架来实现一个简单的Hybrid APP,关于DSBridge的相关用法上面也介绍过了,更加详细的用法请移步至官方文档:https://github.com/wendux/DSBridge-Android.

因为是一个综合案例,所以具体的代码我就不详细写了,因为用到的技术点上面也都说过了,这里就说一下具体的需求、实现思路以及实现效果,具体的源码我也已经放到github上面了,有需要的请自提!

功能需求:

  • ①、Web端调用原生端发送HTTP请求,并将请求结果展示到Web端;
  • ②、APP整体沉浸式换肤效果

实现思路:

需求①:首先在原生端通过addJavascriptObject注册一个原生接口,并提供一个JSAPI类,类的内部提供requestHttp()方法,同时将结果返回给Web端;在Web端添加所需的html元素,并给按钮添加点击事件,通过dsBridge.call调用原生端封装的发送http请求的方法比如就叫requestHttp(),拿到的返回值放到对应的html标签上展示即可

需求②:首先在页面标题栏添加一个菜单栏就叫换肤,点击换肤调用换肤方法实现换肤效果。对于状态栏、标题栏、导航栏这些直接调用原生API设置颜色值改变对应的颜色,对于Web页面使用webview.callHandler传入web需要定义的方法名及参数,在web端通过dsBridge.register注册原生端需要的方法,在方法内部实现web页面背景色的更改。

实现效果:

如下图所示,左图是需求①的效果,右图是需求②的效果:

写到这里很是疲惫,总结的不好有点惭愧,没啥说的下期再会!

工程源码:https://github.com/JArchie/JSBridgeDemo

发布了48 篇原创文章 · 获赞 47 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/JArchie520/article/details/105374427