通过WebView与JSBridge集成vue的方式搭建WebAPP,以扫码为例

业务场景:

    由于公司项目中包含移动端的功能,并且需要使用硬件的配合才能完成业务,比如蓝牙连接打印机,二维码等条码的扫描,如果按照以往编写适配移动端的网页肯定是不合适的,但是目前团队也没有熟练使用android进行开发的小伙伴,所以急需一个能使用手机硬件并且使用网页开发移动端的解决方案。

    为此提出了以下的解决方案:

         1. 微信小程序:可以使用硬件能力,并且跨平台,开发简单,但是由于我们的项目是内网使用,所以被无情的抛弃了

         2. HTML+:公信部下HTML5中国产业联盟推出,看上去很强大提供了大量调用硬件能力的API,但是由于文档太烂,社区貌似也不咋地,主要是没那么多时间,两天内要决定使用哪种方案,并且要把框架搭建起来,试错成本太高,因此拒绝,当然我是不会说是因为我太笨学不会的,看上去挺不错的,以后有时间研究一下。

         3. android的webView控件: 底层是webkit的内核,解决webView与原生安卓的通信问题即可,百度看了下webView是支持的,并且有很成熟的解决方案,所以当时直接就决定使用此方式来构建webApp。这种方式的好处是技术成熟,网络上文章很多,至少不会掉坑了爬不出来,支持内网使用,可以打包为APP,逼格提升不少,并且可以借助html来编写漂亮的页面,相对于原生安卓来说简单很多。

         html打包apk的方式有很多,但是都大同小异,这里使用自己写壳的方式相对来说并不复杂,但是安卓那一块相对可控,并不是一个黑匣子,只需解决通信问题即可。

技术栈:

    android:不需要很熟,知道如何创建项目,了解android的项目结构,控件与activity的交互方式。

    webView:webView底层就是webkit内核在驱动,所以这个对象你可以看成是js中的window对象。

    JSBridge:android与webView内通信的桥梁,android将方法注册暴露给js调用,js注册暴露给android调用

    vue:使用vue编写页面,其它的亦可,html就行

    Zxing:一个开源的扫码框架

效果图:                                         

初始化页面
点击【更改WEBVIEW为蓝色】
点击【更改WEBVIEW为红色】

 

扫码后

 

实现步骤:

    1.新建一个安卓工程(主界面白色背景,hello world正中间的那种)

    2.加入JSBridge的相关依赖

        在项目的【build.gradle】加入:maven { url "https://jitpack.io" }

       

        在module的【build.gradle】加入:compile 'com.github.lzyzsd:jsbridge:1.0.4'

       

3.加入Zxing的相关依赖,扫码插件

        我这里并没有直接使用它,而是使用封装好的项目QcCodeLib,从GitHub上下载下来后复制zxing-lib文件夹

       

        复制到当前android项目的根路径下

   

        在module的【build.gradle】加入:implementation project(':zxing-lib')

        修改【settings.gradle】

4.配置Android的项目清单文件【app/src/main/AndroidManifest.xml】,该文件用来说明Android的配置信息,如app名称,主界面,权限等等

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.mywebapp">
    
    <!-- android:usesCleartextTraffic="true",9.0以后默认不支持明文通信,需配置 -->
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:usesCleartextTraffic="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!-- 注册扫码的activity -->
        <activity android:name="com.google.zxing.activity.CaptureActivity" />

    </application>

    <!-- 权限相关 -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.hardware.usb.UsbAccessory" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.CAMERA"/>


</manifest>

5. 安卓主界面,使用线性布局,里面的颜色是使用的常量,自己去资源文件下建好即可

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/blue"
        android:orientation="horizontal">
        <TextView
            android:layout_marginTop="10dp"
            android:layout_marginLeft="10dp"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textSize = "20dp"
            android:textColor="@color/write"
            android:text = "android与js通信"
            />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:orientation="horizontal">

        <Button
            android:id="@+id/updBlue"
            android:layout_margin="10dp"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textColor="@color/write"
            android:background="@color/blue"
            android:text="更改webView为蓝色"/>

        <Button
            android:id="@+id/updRed"
            android:layout_margin="10dp"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textColor="@color/write"
            android:background="@color/blue"
            android:text="更改webView为红色" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:id="@+id/res"
            android:layout_width="match_parent"
            android:layout_height="20dp"
            android:layout_marginLeft="20dp"
            android:text="显示安卓端扫码结果:" />
        
        <!-- 通过JSBridge增强过的WebView控件,只做增强不改变原有功能 -->
        <com.github.lzyzsd.jsbridge.BridgeWebView
            android:id="@+id/test_bridge_webView"
            android:layout_margin="20dp"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />

    </LinearLayout>

</LinearLayout>

6. MainActivity,具体看注释

package com.example.mywebapp;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.*;
import com.github.lzyzsd.jsbridge.BridgeHandler;
import com.github.lzyzsd.jsbridge.BridgeWebView;
import com.github.lzyzsd.jsbridge.CallBackFunction;
import com.google.zxing.activity.CaptureActivity;
import com.google.zxing.util.Constant;

public class MainActivity extends AppCompatActivity {
	
	private static  final  String TAG = MainActivity.class.getSimpleName();
	
	/** 使用了JSBridge增强过的WebView */
	private BridgeWebView mBridgeWebView;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		/** 初始化 */
		init();
	}
	
	/** 初始化页面 */
	public void init() {
		/** 获得WebView控件并配置 */
		mBridgeWebView= (BridgeWebView) findViewById( R.id.test_bridge_webView);
		/** 以http的模式加载页面,这里也可以将写好的页面放到项目下,以文件的形式加载 */
		mBridgeWebView.loadUrl("http://192.168.1.100:8083");
		mBridgeWebView.getSettings().setAllowContentAccess(true);
		/** 是否启用JS */
		mBridgeWebView.getSettings().setJavaScriptEnabled(true);
		
		/** 注册让js调用的方法 */
		mBridgeWebView.registerHandler("scanQrCode", new BridgeHandler() {
			
			@Override
			public void handler(String data, CallBackFunction function) {
				/** 扫码 */
				startQrCode();
				/** js传递的参数 */
				Toast.makeText(MainActivity.this,"JS调用扫码发送的参数:" + data,Toast.LENGTH_SHORT).show();
				/** 执行安卓的方法后将结果返回给JS */
				function.onCallBack("正在处理,处理后返回结果");

			}
		});
		
		/** 点击修改背景为蓝色的按钮 */
		findViewById(R.id.updBlue).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {
				/** 调用html注册的修改颜色的函数,值为blue */
				mBridgeWebView.callHandler("updateBackColor","blue",new CallBackFunction(){
					@Override
					public void onCallBack(String data) {
						Log.e(TAG, "来自web的回传数据:" + data);
					}
				});
			}
		});
		/** 点击修改背景为红色的按钮 */
		findViewById(R.id.updRed).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {
				/** 调用html注册的修改颜色的函数,值为red */
				mBridgeWebView.callHandler("updateBackColor","red",new CallBackFunction(){
					@Override
					public void onCallBack(String data) {
						Log.e(TAG, "来自web的回传数据:" + data);
					}
				});
			}
		});
		
	}
	
	/** 开始扫码 */
	private void startQrCode() {
		/** 申请相机权限 */
		if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
			/** 申请权限 */
			if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
				Toast.makeText(MainActivity.this,"Toast提示消息",Toast.LENGTH_SHORT).show();
			}
			ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, Constant.REQ_PERM_CAMERA);
			return;
		}
		/** 申请文件写入权限 */
		if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!=PackageManager.PERMISSION_GRANTED){
			ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Constant.REQ_PERM_CAMERA);
		}

		/** 二维码扫码,调用注册的Activity,之前导入的包已经里面已经写好 */
		Intent intent = new Intent(MainActivity.this, CaptureActivity.class);
		/** 启动并标识Activity,后面扫描结束时需要唯一标识 */
		startActivityForResult(intent, Constant.REQ_QR_CODE);
	}
	
	
	/**
	 * 当前页面启用其它活动时返回结果的回调
	 * @param requestCode
	 * @param resultCode
	 * @param data
	 */
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		/** 扫描结果回调 */
		if (requestCode == Constant.REQ_QR_CODE && resultCode == RESULT_OK) {
			Bundle bundle = data.getExtras();
			String scanResult = bundle.getString(Constant.INTENT_EXTRA_KEY_QR_SCAN);
			/** 将扫描出的信息显示出来 */
			Toast.makeText(MainActivity.this,"扫描结果: " + scanResult,Toast.LENGTH_SHORT).show();
			
			TextView res = findViewById(R.id.res);
			res.setText("安卓端显示扫码结果:" + scanResult);
			
			/** 调用接收扫码结束的函数,参数是扫码结果 */
			mBridgeWebView.callHandler("scanResult",scanResult, new CallBackFunction() {
				@Override
				public void onCallBack(String data) {
					Log.e("TAG","JS反馈的结果:" + data);
				}
				
			});
		}
	}
	
	private long exitTime = 0;
	
	/**
	 * 监听按键事件
	 * @param keyCode
	 * @param event
	 * @return
	 */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		
		/** 监听回退事件,一秒点击两次回退才退出APP,否则只是使WebView回退到上一次访问的路径*/
		if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
			if (System.currentTimeMillis() - exitTime > 1000) {
				Toast.makeText(this, "再按一次退出程序",Toast.LENGTH_LONG);
				exitTime = System.currentTimeMillis();
				/** WebView回退,与浏览器的回退一样*/
				mBridgeWebView.goBack();
			} else {
				finish();
				System.exit(0);
			}
			return true;
		}
		return false;
	}
}

android的东西就那么多了,接下来看一下移动端的代码。

---------------------------------------------------------------------------我是分割线 ------------------------------------------------------------------------------

前端的关键不是用什么写页面,而是能用js注册于调用函数即可

7.使用JSBridge.js用来调用android注册的方法或者注册方法给android调用,写法比较固定,这里只针对安卓,IOS也支持,写法有点差异,JSBridge原理的文章很多,感兴趣自行百度

//执行回调函数
function setupWebViewJavascriptBridge(callback) {
        //如果该对象已存在则直接执行
        if (window.WebViewJavascriptBridge) {
            callback(WebViewJavascriptBridge)
        } else {
            //否则添加事件监听再执行
            document.addEventListener(
                'WebViewJavascriptBridgeReady'
                , function() {
                    callback(WebViewJavascriptBridge)
                },
                false
            );
        }
}
//注册回调函数
setupWebViewJavascriptBridge(function (bridge) {
        //初始化
        bridge.init(function (message, responseCallback) {
            var data = {
                'Javascript Responds': 'Wee!'
            };
            responseCallback(data);
        })
})
//暴露当前模块的方法
export default {
    // js调APP方法 (参数分别为:app提供的方法名  传给app的数据  回调)
    callHandler(name, data, callback) {
        setupWebViewJavascriptBridge(function (bridge) {
            bridge.callHandler(name, data, callback)
        })
    },
    // APP调js方法 (参数分别为:js提供的方法名  回调)
    registerHandler(name, callback) {
        setupWebViewJavascriptBridge(function (bridge) {
            bridge.registerHandler(name, function (data, responseCallback) {
                callback(data, responseCallback)
            })
        })
    }
}

8.页面如何使用JSBridge

<template>
  <div class="def" v-bind:class="backColor">
    
    <button
          v-bind:class="backColor"
          type=""
          style="width:80%;margin-top:100px;"
          @click="scanQrCode()"
          >扫码</button>
    <div>当前颜色: {{backColor}}</div>
    <div>HTML显示扫描结果: {{code}}</div>
  </div>
</template>

<script>
import $JSbridge from './JSbridge'

export default {
  name: "JSBridgeTest",
  data() {
      return {
        backColor: 'black',
        code:''
      }
  },
  mounted() {
      //执行注册给安卓调用的方法
      this.registerUpdateBackColor();
      this.registerScanResultFunction();
  },
  methods:{

      //调用安卓的扫码方法,这里需要传递参数给安卓所以为空
      scanQrCode() {
          $JSbridge.callHandler('scanQrCode','',response => {
              this.code = response;//
          })
      },
     
     //注册接收扫码返回值的方法
     registerScanResultFunction() {
        $JSbridge.registerHandler("scanResult",(data,responseCallback) => {
            //获得安卓的扫描结果
            this.code = data;
            //接收成功并返回值给安卓
            responseCallback("success");
        })
     },
     
      //注册修改背景色的方法给安卓调用
      registerUpdateBackColor() {
          $JSbridge.registerHandler("updateBackColor",(data,responseCallback) => {
            this.backColor = data;
            console.log(this.backColor)
            responseCallback("success")
        })
      }
  }
};
</script>
<style >
.def{
    width: 100%;
    height: 1000px;
}
.black{
    background-color: black
}
.red{
    background-color: red;
}
.blue{
    background-color: blue;
}
</style>

到这里就可以达到开始图片时的效果了。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值