preface

There are many ways of mixed development, such as Uniapp, React Native, Flutter, hybridApp, etc. What we introduce here is that most of the functions are still native, and a small part of the functions are web. In this way, some functions do not need to develop two sets of Android and ios and can be updated in real time without upgrading the APP. The disadvantage is that the page loading speed is slow and depends on the network. Hybrid development is when some functions are handed over to the Web front end, but the Web page does not call the phone's camera, taking pictures, positioning and other functions. Ultimately, Android engineers need to package these parts of the Web and native interaction into JS for the front-end use.Copy the code

preparation

You need to use a github third-party library called JsBridge, which the authors haven't updated in a while but still works. If you don't want to use it you have to write it yourself and we're not going to talk about it here. The BridgeHelper class is missing in the latest version of the BridgeHelper class. Look at the issue only to download [19 years on July 10, this version of the] (https://github.com/lzyzsd/JsBridge/commits/master).Copy the code

demand

The goal here is that the Web needs to call the native location and camera functions and return, while the native needs to call a method of the Web to communicate between the two ends.Copy the code

The body of the

Here we must first prepare a JS for the Use of the Web side, this JS is equivalent to a bridge between the Web and Android, communication between all through this JS to establish contact. const u = navigator.userAgent; window.isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; // Android terminal window.isios =!! u.match(/\(i[^;] +; ( U;) ? CPU.+Mac OS X/); / / ios terminal / * all calls the premise must be registered first, this method is called * / function registerBridge (the callback) {if (window. WebViewJavascriptBridge) {return callback(WebViewJavascriptBridge); } else { document.addEventListener( 'WebViewJavascriptBridgeReady', function () { callback(WebViewJavascriptBridge) }, false ); } if (window.isIOS) { if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); } if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); } window.WVJBCallbacks = [callback]; // Create an array of WVJBCallbacks global properties and insert callback into the array. } const WVJBIframe = document.createElement('iframe'); // Create an iframe element wvjbiframe.style. display = 'none'; SRC = 'wvjbscheme://__BRIDGE_LOADED__'; / / set the iframe SRC attribute document. The documentElement. The appendChild (WVJBIframe); // Add iframe to the current text navigation. SetTimeout (function () {document. DocumentElement. RemoveChild (WVJBIframe)}, 0)} window. RuibetterH5 = {/ * js calls native, Function (callback) {function (bridge) {function (bridge) {function (bridge) { bridge.callHandler('getLocation',{},function (data) { callback(data) }) }) }, / * call native album * / getSelectorImages: function (the callback) {registerBridge (function (bridge) { bridge.callHandler("getSelectorImages",{},function (data) { callback(data) }) }) }, }Copy the code

Write a Web page

1. Invoke native location

Web page code

<div class="weui-flex__item child_two_flex_last location"> point I get native latitude and longitude coordinates </div> /* Native call js,js returns data to native */ $('.location').on("click", function () { RuibetterH5.getLocation(function (response) { $('.location').text(response) }) })Copy the code

The Android code

<? The XML version = "1.0" encoding = "utf-8"? > <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> <include android:id="@+id/webToolbar" layout="@layout/toolbar"/> <com.github.lzyzsd.jsbridge.BridgeWebView android:id="@+id/bridgeWebView" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/webToolbar" App: layout_constraintBottom_toBottomOf = "parent" / > < / androidx. Constraintlayout. Widget. Constraintlayout > / / load the link Bridge. LoadUrl (" http://192.168.1.7:3011/html/water_watch.html ") bridge. RegisterHandler (" getLocation ") {data, function -> rxPermissions.request(Manifest.permission.ACCESS_FINE_LOCATION) .subscribe { if (it) { val location = LocationUtils.getInstance().getLocations(this) function.onCallBack(location) } else { val alertDialog = AlertDialog.Builder(this) alertDialog.setTitle(AppUtils.getAppName()) AlertDialog. SetMessage (" location permissions for must permissions, or unable to provide positioning service for you better, click ok to set up a center to open. ") alertDialog. SetNegativeButton (" cancel ") {dialog, Which - > dialog. Dismiss ()} alertDialog. SetPositiveButton (" sure ") {dialog, which -> dialog.dismiss() PermissionSettingUtil.gotoPermission(WaterApp.context) } alertDialog.show() } } }Copy the code

Request permission through rxPermission

   <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <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"/>
    <uses-feature android:name="android.hardware.camera"
        android:required="false" />
    <uses-feature android:name="android.hardware.camera.autofocus"
        android:required="false" />
Copy the code

Adding a Network Request

2. Call the album

Web page code

<input id="uploaderInput" class="weui-uploader__input" type="file" accept="image/*" onchange="imagePreview(this)"/> / / function imagePreview(input){let files = input.files; for (let i = 0; i < files.length; i++) { let file = files[i]; let imageType = /^image\//; if ( ! Imagetype.test (file.type)) {// alert(" No image format can be displayed "); continue; } let img = document.createElement("img"); $uploaderFiles.append(img) img.setAttribute("class","water_weui-uploader__file") let reader = new FileReader(); reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img); reader.readAsDataURL(file); }}Copy the code

The Android code

private lateinit var bridge:BridgeWebView bridge.webChromeClient = object :WebChromeClient(){ override fun onShowFileChooser( webView: WebView? , filePathCallback: ValueCallback<Array<Uri>>? , fileChooserParams: FileChooserParams? ) : Boolean { mUploadMessageArray = filePathCallback rxPermissions.request(Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE) .subscribe {  if (it) { PictureSelector.create(this@WaterWebViewActivity) .openGallery(PictureMimeType.ofImage()) .imageEngine(GlideEngine.createGlideEngine()) .forResult(PictureConfig.CHOOSE_REQUEST) } else { val alertDialog = AlertDialog.Builder(this@WaterWebViewActivity) alertDialog.setTitle(AppUtils.getAppName()) AlertDialog. SetMessage (" store permissions for must permissions, or unable to better serve you choose picture, click ok to set up a center to open. ") alertDialog. SetNegativeButton (" cancel ") {dialog, Which - > dialog. Dismiss ()} alertDialog. SetPositiveButton (" sure ") {dialog, which -> dialog.dismiss() PermissionSettingUtil.gotoPermission(WaterApp.context) } alertDialog.show() } } return true } override fun onReceivedTitle(view: WebView? , title: String?) { super.onReceivedTitle(view, title) title? .... Let {waterBinding webToolbar titleTv text = "point I native inform Js"}}} override fun onActivityResult (requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if(resultCode == RESULT_OK ){ if(requestCode == PictureConfig.CHOOSE_REQUEST) { data? .let { val localMedias = PictureSelector.obtainMultipleResult(data) if(localMedias.isNotEmpty()) { val uriMedias = mutableListOf<Uri>() localMedias.forEach { localMediaItem -> uriMedias.add(UriUtils.file2Uri(File(if(TextUtils.isEmpty(localMediaItem.androidQToPath)) localMediaItem.realPath else localMediaItem.androidQToPath))) } mUploadMessageArray? .onReceivevalue (uriMedias.toTypeDarray ())}}}}else {// If you call album and do not select the photo click return, the next click select album will not trigger mUploadMessageArray? .onReceiveValue(null) } }Copy the code

Here’s a three-way selection of picturesframe to model elector

   <uses-permission android:name="android.permission.INTERNET"/>
    <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"/>
    <uses-feature android:name="android.hardware.camera"
        android:required="false" />
    <uses-feature android:name="android.hardware.camera.autofocus"
        android:required="false" />
Copy the code

3. Invoke the Web natively

The web page

<input id="js_input" type="text" class="weui-input" placeholder=" bridge.registerHandler("functionInJs",function (data,responseCallback) { $("#js_input").val(data) const responseData = 'Java called JS I have received, this is the data returned to you by JS '; responseCallback(responseData); Return data to Java})})Copy the code

The Android code

WaterBinding. WebToolbar. TitleTv. SetOnClickListener {ToastUtils. ShowShort (" I ordered ") Bridge. CallHandler ("functionInJs"," native return 123"){data -> toastutils.showshort (data)}}Copy the code

The final result

conclusion

To this mixed development requirements are complete, complete the manual!!