Hybird开发,webview和H5交互

H5调用原生功能

封装一个统一的接口,供H5页面调用原生的功能,比如获取设备信息、打开相机、分享内容等。这样,H5页面只需调用这个接口,而无需关心具体的原生实现细节。

//定义一个类,用@JavascriptInterface注解来注释一个方法,这个方法就可以被H5直接调用
public class NativeAPI {
    @JavascriptInterface
    public void getDeviceInfo() {
        // 假设获取设备信息的原生方法
        String deviceInfo = "Device: Android, Version: 11";
        // 将结果回调给H5页面
        String javascriptCode = "javascript:onDeviceInfoReceived('" + deviceInfo + "')";
        webView.loadUrl(javascriptCode);
    }
}

 // 添加 JavaScript 接口到 WebView 中
 webView.addJavascriptInterface(new NativeBridge(), "NativeBridge");

H5端调用:

function getDeviceInfo() {
  // 调用原生获取设备信息
  NativeAPI.getDeviceInfo();
}

原生调用H5

直接调用API:

String javascriptCode = "javascript:xxxx')";
webView.loadUrl(javascriptCode);
webView.evaluateJavascript("XXX")

原生回调H5

为了实现在 H5 页面中传递回调函数给原生代码,我们可以通过另外一种方式:使用随机生成的标识符来标记回调函数,并在原生代码中回调这个标识符对应的函数。

以下是修改后的示例代码,演示了在 H5 页面中调用复杂类型的 JavaScript 函数并传递回调函数标识符,然后在原生代码中通过标识符找到对应的回调函数并调用:

在 H5 页面中(例如 index.html):

DOCTYPE html>
<html>
<head>
    <title>Complex Function Callback Exampletitle>
head>
<body>
    <h1>Complex Function Callback Exampleh1>

    <script>
        // 用于保存回调函数
        var callbacks = {};

        // 定义复杂的 JavaScript 函数,返回一个包含多个属性的对象
        function getComplexData() {
            var complexData = {
                name: "John Doe",
                age: 30,
                address: {
                    city: "New York",
                    country: "USA"
                },
                interests: ["Reading", "Traveling", "Coding"]
            };
            return complexData;
        }

        // 示例:调用一个复杂的 JavaScript 函数,并传递回调函数
        function triggerComplexFunctionWithCallback() {
            var complexData = getComplexData();

            // 生成一个随机标识符作为回调函数的标识
            var callbackId = "cb_" + new Date().getTime();

            // 将回调函数保存到 callbacks 对象中,用于在原生代码中调用
            callbacks[callbackId] = function(result) {
                // 在回调函数中处理原生传递回来的复杂类型数据
                console.log("Received complex data from Native: ", result);
            };

            // 调用原生方法,并传递回调函数标识符
            window.NativeBridge.onComplexFunctionWithCallbackResult(complexData, callbackId);
        }
    script>
body>
html>

在 Android 原生代码中:

import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import androidx.appcompat.app.AppCompatActivity;
import org.json.JSONException;
import org.json.JSONObject;

public class MainActivity extends AppCompatActivity {

    private WebView webView;

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

        webView = findViewById(R.id.webView);

        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);

        // 添加 JavaScript 接口到 WebView 中
        webView.addJavascriptInterface(new NativeBridge(), "NativeBridge");

        webView.setWebChromeClient(new WebChromeClient());
        webView.loadUrl("file:///android_asset/index.html");
    }

    // 定义一个 JavaScript 接口类,供 H5 页面调用
    private class NativeBridge {
        @JavascriptInterface
        public void onComplexFunctionWithCallbackResult(String jsonString, String callbackId) {
            // 在主线程中解析 JSON 字符串为复杂类型数据
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    try {
                        JSONObject json = new JSONObject(jsonString);
                        String name = json.getString("name");
                        int age = json.getInt("age");
                        JSONObject address = json.getJSONObject("address");
                        String city = address.getString("city");
                        String country = address.getString("country");
                        // 处理复杂类型数据
                        // ...

                        // 构造一个复杂类型数据的 JSON 字符串作为回调给 H5 页面
                        String result = "{ \"message\": \"Received complex data from Native\" }";

                        // 获取 H5 页面传递过来的回调函数标识符对应的回调函数
                        String callbackJs = "callbacks['" + callbackId + "']";

                        // 使用字符串拼接的方式将回调结果传递给 H5 页面的回调函数
                        webView.evaluateJavascript(callbackJs + "(" + result + ")", null);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

在上述示例中,我们在 H5 页面中定义了一个 JavaScript 对象 callbacks 来保存回调函数,并使用一个随机标识符来作为回调函数的键。在 triggerComplexFunctionWithCallback() 方法中,我们将回调函数保存到 callbacks 对象中,并传递回调函数的标识符给原生代码。

在原生代码中的 NativeBridge.onComplexFunctionWithCallbackResult(String jsonString, String callbackId) 方法中,我们获取到回调函数标识符,并通过字符串拼接的方式构造出相应的回调函数调用语句,从而正确地执行了 H5 页面传递过来的回调函数。

事件派发

封装一个事件派发机制,让原生和H5页面可以通过发送和监听事件来实现双向通信。这样可以在不直接调用对方方法的情况下,进行通信和数据交换。

在 Hybrid 开发中,事件派发是指从原生代码向 H5 页面发送消息或通知,让 H5 页面可以感知到某个事件的发生,从而执行相应的处理逻辑。事件派发是一种在原生和 H5 之间进行双向通信的手段之一。

以下是一种简单的实现方式,演示了如何在原生代码中派发一个事件给 H5 页面:

在 H5 页面中(例如 index.html):

DOCTYPE html>
<html>
<head>
    <title>Event Dispatch Exampletitle>
head>
<body>
    <h1>Event Dispatch Exampleh1>

    <script>
        // 监听自定义事件
        window.addEventListener("customEvent", function(event) {
            console.log("Received custom event:", event.detail);
            // 在这里可以执行相应的处理逻辑
            // ...
        });
    script>
body>
html>

在 Android 原生代码中:

import android.os.Bundle;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private WebView webView;

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

        webView = findViewById(R.id.webView);

        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);

        webView.setWebChromeClient(new WebChromeClient());
        webView.loadUrl("file:///android_asset/index.html");

        // 派发事件到 H5 页面
        dispatchCustomEvent("Hello from Native!");
    }

    // 派发自定义事件到 H5 页面
    private void dispatchCustomEvent(String eventData) {
        // 使用 evaluateJavascript 方法调用 H5 页面中的 JavaScript 函数
        String script = "var event = new CustomEvent('customEvent', { detail: '" + eventData + "' });" +
                        "window.dispatchEvent(event);";
        webView.evaluateJavascript(script, null);
    }
}

我们在 H5 页面中使用 window.addEventListener 方法来监听名为 “customEvent” 的自定义事件。然后,在原生代码中的 dispatchCustomEvent 方法中,我们使用 webView.evaluateJavascript 方法执行 JavaScript 代码,从而派发一个名为 “customEvent” 的自定义事件,并传递事件数据给 H5 页面。

当原生代码执行 dispatchCustomEvent(“Hello from Native!”) 后,H5 页面中的监听器会捕获到该事件,并执行相应的处理逻辑。

需要注意的是,事件派发时,我们可以自定义事件的类型和传递的数据,并通过 JavaScript 的 CustomEvent 构造函数来创建自定义事件对象。在这个例子中,我们通过 CustomEvent(‘customEvent’, { detail: eventData }) 创建了一个自定义事件,其中 eventData 就是我们要传递给 H5 页面的事件数据。在原生代码中,我们将 eventData 作为字符串传递给 H5 页面,但你也可以将更复杂的数据结构转换为 JSON 字符串,然后传递给 H5 页面进行处理。

错误处理

设计统一的错误处理机制,让原生和H5页面能够更好地处理错误情况,并向对方传递错误信息。例如,当某个功能不支持时,通过错误回调通知H5页面。

在 Hybrid 开发中,为了解决回调函数的问题,我们可以使用一种约定的方式,让原生代码返回一个标识符给 H5 端,表示回调函数的唯一标识。然后,H5 端将这个标识符保存起来,并在需要的时候调用原生代码提供的另一个方法,传递这个标识符和需要回调的数据。原生代码根据标识符找到对应的回调函数,并执行回调处理。

以下是一个重新设计的例子,演示了如何在 Hybrid 开发中实现回调函数的传递和调用:

在 H5 页面中(例如 index.html):

DOCTYPE html>
<html>
<head>
    <title>Error Handling Exampletitle>
head>
<body>
    <h1>Error Handling Exampleh1>

    <script>
        // 保存回调函数的对象
        var callbacks = {};

        // 定义一个处理错误的回调函数
        function onError(errorMessage) {
            console.error("Received error message from Native:", errorMessage);
            // 在这里可以执行相应的错误处理逻辑
            // ...
        }

        // 定义一个注册回调函数的方法,返回一个唯一的标识符
        function registerCallback(callback) {
            var callbackId = "cb_" + new Date().getTime();
            callbacks[callbackId] = callback;
            return callbackId;
        }

        // 示例:调用原生方法,处理可能出现的错误
        function callNativeMethod() {
            // 注册回调函数,获取回调函数的标识符
            var callbackId = registerCallback(function(result) {
                // 检查 result 是否包含 error 字段
                if (result.error) {
                    // 如果 result 包含 error 字段,则触发错误处理回调
                    onError(result.error);
                } else {
                    // 否则,继续处理正常的结果
                    console.log("Received result from Native:", result);
                    // 在这里可以执行正常的处理逻辑
                    // ...
                }
            });

            // 调用原生方法,并传递回调函数的标识符
            window.NativeBridge.someFunction(callbackId);
        }
    script>
body>
html>

在 Android 原生代码中:

import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import androidx.appcompat.app.AppCompatActivity;
import org.json.JSONException;
import org.json.JSONObject;

public class MainActivity extends AppCompatActivity {

    private WebView webView;

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

        webView = findViewById(R.id.webView);

        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);

        // 添加 JavaScript 接口到 WebView 中
        webView.addJavascriptInterface(new NativeBridge(), "NativeBridge");

        webView.setWebChromeClient(new WebChromeClient());
        webView.loadUrl("file:///android_asset/index.html");
    }

    // 定义一个 JavaScript 接口类,供 H5 页面调用
    private class NativeBridge {
        @JavascriptInterface
        public void someFunction(String callbackId) {
            try {
                // 假设这里出现了一个错误,我们模拟一个包含错误信息的 JSON 对象
                JSONObject errorObject = new JSONObject();
                errorObject.put("error", "Something went wrong!");

                // 获取回调函数的标识符对应的回调函数
                String callbackJs = "callbacks['" + callbackId + "']";

                // 使用字符串拼接的方式将错误信息传递给 H5 页面的回调函数
                webView.evaluateJavascript(callbackJs + "(" + errorObject.toString() + ")", null);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }
}

桥接封装

将原生代码和H5交互的桥接代码进行封装,使其更易用、更健壮,避免直接在业务逻辑中直接操作桥接代码,从而降低耦合性。

对于桥接代码,可以封装一个BridgeHelper类,用于处理原生和H5交互的细节,使得在业务逻辑中不直接操作桥接代码。

public class BridgeHelper {
    private WebView webView;

    public BridgeHelper(WebView webView) {
        this.webView = webView;
    }

    // 封装调用原生功能的方法
    public void callNativeFunction(String functionName) {
        // 构建JavaScript代码
        String javascriptCode = "javascript:" + functionName + "()";
        // 调用原生方法
        webView.loadUrl(javascriptCode);
    }
}
//其他地方调用
BridgeHelper bridgeHelper = new BridgeHelper(webView);
bridgeHelper.callNativeFunction("someNativeFunction");

安全处理

考虑安全性问题,确保对于原生和H5之间的通信,只暴露必要的接口,并对传递的数据进行验证和过滤,以防止潜在的安全风险。

在封装的API中,对于接收的参数进行验证,确保数据的有效性和安全性。

H5代码:

function sendDataToNative(data) {
  // 对数据进行验证,确保不为空且符合要求
  if (data !== null && typeof data === 'object') {
    // 调用原生方法并传递数据
    NativeAPI.sendData(data);
  } else {
    console.error("Invalid data format!");
  }
}

版本兼容

针对不同的原生和H5版本,可以进行一些兼容性处理,确保在不同平台和环境下都能正常运行。

调试日志

在桥接代码中添加调试日志,以便在开发和测试过程中更容易发现问题,对于某些复杂的交互,可以在日志中打印交互数据,方便排查问题。

你可能感兴趣的:(交互)