求使用GeckoView实现Html页面与Android应用双向交互的完整示例
GeckoView 网页与Android双向通信完整实现方案
核心逻辑说明
- 以GeckoView官方推荐的Web扩展作为中间桥接层,实现页面和原生应用的消息中转
- 通信链路:页面 ↔ 扩展内容脚本 ↔ 扩展后台脚本 ↔ Android原生代码
步骤1:创建内置Web扩展
在Android项目的assets目录下新建gecko_extension文件夹,里面新增三个文件:
1.1 扩展配置文件 manifest.json
{ "manifest_version": 2, "name": "GeckoMessageBridge", "version": "1.0", "content_scripts": [ { "matches": ["http://example.com/*"], "js": ["content_script.js"], "run_at": "document_start" } ], "background": { "scripts": ["background.js"] }, "permissions": [ "nativeMessaging", "http://example.com/*" ], "gecko_view": { "native_messaging": true } }
1.2 内容脚本 content_script.js(注入到目标页面,负责和页面交互)
// 监听页面发来的消息 window.addEventListener("message", event => { // 只接收目标域名的消息,避免安全风险 if (event.origin !== "http://example.com") return; if (event.data.type === "PAGE_TO_APP") { // 转发消息给扩展后台 browser.runtime.sendMessage({ type: "PAGE_TO_APP", content: event.data.content }); } }); // 监听扩展后台发来的消息 browser.runtime.onMessage.addListener(message => { if (message.type === "APP_TO_PAGE") { // 调用页面暴露的全局方法,wrappedJSObject用于访问页面上下文的全局变量 window.wrappedJSObject.appToPage(message.content); } });
1.3 后台脚本 background.js(负责和Android原生交互)
// 监听Android原生发来的消息,转发给内容脚本 browser.runtime.onMessage.addListener((message, sender) => { if (message.type === "APP_TO_PAGE") { browser.tabs.query({active: true, currentWindow: true}).then(tabs => { browser.tabs.sendMessage(tabs[0].id, message); }); } }); // 监听内容脚本发来的消息,转发给Android原生 browser.runtime.onMessage.addListener((message, sender) => { if (message.type === "PAGE_TO_APP") { browser.runtime.sendNativeMessage("gecko_bridge", message); } });
步骤2:修改前端页面代码
补全原有x.html里的pageToApp方法即可,其余代码不需要改动:
function pageToApp(s) { // 发消息给注入的内容脚本 window.postMessage({ type: "PAGE_TO_APP", content: s }, "http://example.com"); log('sent:' + s); }
步骤3:补全Android侧代码
直接替换原有MainActivity的对应逻辑即可:
import org.mozilla.geckoview.GeckoSession; import org.mozilla.geckoview.GeckoView; import org.mozilla.geckoview.WebExtension; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; public class MainActivity extends AppCompatActivity { private GeckoSession geckoSession; private WebExtension messageBridgeExtension; // 扩展在assets目录下的路径 private static final String EXTENSION_LOCATION = "resource://android/assets/gecko_extension/"; private static final String EXTENSION_ID = "gecko_bridge@example.com"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); GeckoView geckoView = findViewById(R.id.geckoview); geckoSession = new GeckoSession(); geckoView.setSession(geckoSession); // 注册消息桥接扩展 WebExtension.Installer installer = geckoSession.getWebExtensionController() .installBuiltIn(EXTENSION_LOCATION, EXTENSION_ID); installer.whenComplete(extension -> { messageBridgeExtension = extension; // 设置扩展消息监听 messageBridgeExtension.setMessageDelegate(new WebExtension.MessageDelegate() { @Override public void onMessage(String nativeApp, WebExtension.MessageSender sender, Object message, WebExtension.MessageSender.Response response) { // 接收页面发来的消息 if (nativeApp.equals("gecko_bridge") && message instanceof org.json.JSONObject) { try { org.json.JSONObject msgJson = (org.json.JSONObject) message; if (msgJson.getString("type").equals("PAGE_TO_APP")) { String content = msgJson.getString("content"); onMessageFromPage(content); } } catch (Exception e) { e.printStackTrace(); } } } }, "gecko_bridge"); // 扩展加载完成后再加载目标页面 geckoSession.loadUri("http://example.com/x.html"); }, throwable -> { Log.e("GeckoExtension", "扩展加载失败", throwable); }); } // Android按钮点击回调 public void onSendButtonClick(View view) { if (messageBridgeExtension == null) return; try { // 构造发往页面的消息 org.json.JSONObject msg = new org.json.JSONObject(); msg.put("type", "APP_TO_PAGE"); msg.put("content", "helloFromApp"); // 发送给扩展后台 messageBridgeExtension.sendMessage(EXTENSION_ID, msg, null); } catch (Exception e) { e.printStackTrace(); } } // 接收页面消息的回调 public void onMessageFromPage(String s) { Log.d("msgFromPage", s); // 此处可自行扩展业务逻辑 } }
适配说明
如果你的页面域名不是http://example.com,需要同步修改以下三处配置:
manifest.json里的matches规则content_script.js里的origin校验逻辑- 页面
pageToApp方法中postMessage的目标域名参数
内容的提问来源于stack exchange,提问作者Flavio Lenz Cesar
相关产品推荐
相关产品推荐

