RN - 封装Android原生WebView组件,实现JS获取原生消息回调及JS控制native组件
Posted iOSTianNan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RN - 封装Android原生WebView组件,实现JS获取原生消息回调及JS控制native组件相关的知识,希望对你有一定的参考价值。
之前为了处理react-native-webView 无法处理安卓端微信支付的问题, 封装了安卓原生组件提供给RN使用, 此次还需要针对RN组件支持
回调事件
和操作原生组件执行某些操作
这个是基于RN 调用原生WebPage 解决微信支付Referer问题的继续扩展
目的:
1.实现RN webView组件 获取onCanGoBack=(canGoBack)=>
回调
2.实现RN webView组件 获取onTitle=(title)=>
回调
3.实现RN webView组件通过ref调用 goBlack() 函数
, 实现导航箭头的回退功能
核心内容:
1. native向RN发送消息事件, RN通过回调函数获取消息
->即实现 onCanGoBack=(canGoBack:boolean)=>
->onTitle=(title)=>
2.RN向Native发送操作命令,Native接收后执行操作
-> 即实现this.refs.web.goBack()
A: 基础部分 - 封装原生WebView组件
1. WxPayReactWebViewManager实现
新建ViewManager类,并继承SimpleViewManager,SimpleViewManager类需要传入一个泛型,该泛型继承android的View,也就是说该泛型是要使用android 平台的哪个View就传入该View
public static final String RN_CLASS = "WxPayRCTWebView";
@Override
public String getName()
return RN_CLASS;
实现视图方法 -返回ViewManager的实例对象, 这里返回WebView的实例对象
@SuppressLint("ResourceAsColor")
@Override
protected WebView createViewInstance(ThemedReactContext reactContext)
...具体代码省略
设置RN组件可以支持的参数名
/**
* js传递的参数
*
* @param view
* @param loadInfo
*/
@ReactProp(name = "loadInfo")
public void setLoadInfo(WebView view, @Nullable ReadableMap loadInfo)
...具体代码省略
2 WxPayWebViewReactPackage的实现
createNativeModules()是注册模块的,createViewManagers()是注册管理器的,如果想要注册多个模块就在List里面多个添加就行
/**
* 注册模块
* @param reactContext
* @return
*/
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext)
return Collections.emptyList();
/**
* 注册管理器
* @param reactContext
* @return
*/
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext)
return Arrays.<ViewManager>asList(
new WxPayReactWebViewManager()
);
3 MainApplication中注册ReactPackage
@Override
protected List<ReactPackage> getPackages()
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for
// example:
packages.add(new WxPayWebViewReactPackage());
return packages;
4 RN中 - 创建WxPayWeb组件
WxPayRCTWebView:
requireNativeComponent
- 通过该函数将原生组件名称 与 RN组件类名 进行关联,返回原生组件的实例
var WxPayRCTWebView = requireNativeComponent('WxPayRCTWebView', WxPayWeb, nativeOnly: propABC:true)
//propABC属性不想暴露给外面的话,就是通过nativeOnly来设置
WxPayWeb
:
WxPayWeb 真正的RN组件类, 实际是返回的一个
<WxPayRCTWebView ...this.props .../>
原生组件
render()
return <WxPayRCTWebView
...this.props
ref=RCT_WxPayWebVIEW_REF
//onCanGoBack=this._onCanGoBack
//onTitle=this._onTitle
/>
WxPayWeb.propTypes
:
声明RN组件的支持参数
//RN组件支持的props列表
//新版本必须加...View.propTypes
WxPayWeb.propTypes =
loadInfo:PropTypes.any,
html: PropTypes.string,
onCanGoBack: PropTypes.func,
onTitle: PropTypes.func,
...View.propTypes
目前支持的类型映射
:
// java对应js 类型
Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array
5 具体使用
loadInfo
就是自定义的参数名
<WxPayWeb
ref=(WxPayWeb: any) => this.web = WxPayWeb
style=flex: 1
loadInfo=
url: url,
referer: url
/>
至此,已经完成基本原生组件的封装,以及RN层面组件的使用,也支持 传递参数供原生使用
B: Native与RN 事件传递及通信
一. Native向RN发送消息,RN通过回调函数获取消息
重写方法- 创建要支持的自定义事件名
自定义事件,只需要重写ReactVideoManager
下的getExportedCustomBubblingEventTypeConstants()
方法就好
/**
* 注册-自定义事件名
* 自定义多个
*
* @return
*/
@Override
public Map<String, Object> getExportedCustomBubblingEventTypeConstants()
// onCanGoBack 自定义事件名, 其他固定写法 (包括 bubbled)
MapBuilder.Builder<String, Object> builder = MapBuilder.<String, Object>builder();
builder.put("onCanGoBack", MapBuilder.of("phasedRegistrationNames", MapBuilder.of("bubbled", "onCanGoBack")));
// onTitle 自定义事件名, 其他固定写法 (包括 bubbled)
builder.put("onTitle", MapBuilder.of("phasedRegistrationNames", MapBuilder.of("bubbled", "onTitle")));
return builder.build();
webView
加载时,通过webView.canGoBack()
方法判断是否canGoBack, 向RN层发送消息事件
// 网页回调函数
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon)
if (wv.canGoBack())
WritableMap event = Arguments.createMap();
event.putBoolean("canGoBack", true);
// 发送给js
this.dispatch(reactContext, "onCanGoBack", event);
else
WritableMap event = Arguments.createMap();
event.putBoolean("canGoBack", false);
// 发送给js
this.dispatch(reactContext, "onCanGoBack", event);
super.onPageStarted(view, url, favicon);
/**
* 向js发送事件回调,传值
* @param context
* @param EventName
* @param eventMap
*/
public void dispatch(ThemedReactContext context, String EventName, WritableMap eventMap)
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(wv.getId(), EventName, eventMap);
onCanGoBack
: 约定的 回调函数名
canGoBack
: 回调函数返回的参数名
onTitle
: 约定的 回调函数名
title
: 回调函数返回的参数名
RN端回调函数实现及数据接收:
render()
return <BXRCTWebView
...this.props
ref=RCT_BXWEBVIEW_REF
onCanGoBack=this._onCanGoBack
onTitle=this._onTitle
/>
// onCanGoBack 回调
_onCanGoBack = (event) =>
//回调均接受的是event,取值为 event.nativeEvent下的各个字段
this.props.onCanGoBack && this.props.onCanGoBack(event.nativeEvent.canGoBack)
// onTitle 回调
_onTitle = (event) =>
//回调均接受的是event,取值为 event.nativeEvent下的各个字段
this.props.onTitle && this.props.onTitle(event.nativeEvent.title);
//新版本必须加...View.propTypes
BxWeb.propTypes =
url: PropTypes.string,
html: PropTypes.string,
onCanGoBack: PropTypes.func,
onTitle: PropTypes.func,
...View.propTypes
WxPayWeb
组件的完整使用
<WxPayWeb
ref=(WxPayWeb: any) => this.web = WxPayWeb
style=flex: 1
loadInfo=
url: url,
referer: url
onCanGoBack=(canGoBack: boolean) =>
// canGoBack
onTitle=(title: string) =>
// title
/>
二. RN向Native发送操作命令,Native接收后执行操作
RN端加载的网页跳转了多次,现在想返回上一级网页,最终其实是通过RN组件向
Native组件发送操作命令, Native接收到命令后,调用自己组件的方法,
这里就是webViewd.goBack()
在native侧, 需要实现两个函数, 用于注册命名列表, 以及接收命令列表
实现 getCommandsMap:
原生组件提供函数调用列表
receiveCommand:
接收js命令的函数
注意 是这个 public void receiveCommand(WebView root, int commandId, ReadableArray args)
而不是 public void receiveCommand(WebView root, String commandId, ReadableArray args)
后者接收不到
/**
* 注册 js调用native的方法集合
*
* @return
*/
@Nullable
@Override
public Map<String, Integer> getCommandsMap()
return MapBuilder.of(
"goBack", 1,
"toast", 2
);
/**
* 响应 js调用函数的实现
*
* @param root
* @param commandId
* @param args
*/
@Override
public void receiveCommand(WebView root, int commandId, ReadableArray args)
Log.d("receiveCommand======1", commandId + "");
switch (commandId)
case 1:
root.goBack();
break;
case 2:
Toast.makeText(wv.getContext(), "hello", Toast.LENGTH_LONG);
break;
default:
break;
RN组件 WxPayWeb
对 goBack
命令的实现
// js组件执行事件 向native层发送命令
goBack()
//向native层发送命令
UIManager.dispatchViewManagerCommand(
findNodeHandle(this.refs[RCT_WxPayWebVIEW_REF]),
UIManager.WxPayRCTWebView.Commands.goBack,//Commands.goBack与native层定义的"goBack"一致
null//命令携带的参数数据
);
toast_test()
//向native层发送命令
UIManager.dispatchViewManagerCommand(
findNodeHandle(this.refs[RCT_WxPayWebVIEW_REF]),
UIManager.WxPayRCTWebView.Commands.toast,//Commands.toast与native层定义的"toast"一致
["a", "b"]//命令携带的参数数据 [数据a,数据b...]
);
RN组件调用执行
this.refs.web.goBack();
所有代码
Java侧代码:
WxPayWebViewReactPackage
package com.regan.ebankhome;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class WxPayWebViewReactPackage implements ReactPackage
/**
* 注册模块
* @param reactContext
* @return
*/
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext)
return Collections.emptyList();
/**
* 注册管理器
* @param reactContext
* @return
*/
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext)
return Arrays.<ViewManager>asList(
new WxPayReactWebViewManager()
);
WxPayReactWebViewManager
package com.regan.ebankhome;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
import android.util.Log;
import android.webkit.ClientCertRequest;
import android.webkit.SslErrorHandler;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
import com.facebook.react.uimanager.annotations.ReactPropertyHolder;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import java.util.HashMap;
import java.util.Map;
import static com.umeng.socialize.utils.ContextUtil.getContext;
/**
* ViewManager
*/
public class WxPayReactWebViewManager extends SimpleViewManager<WebView>
public static final String RN_CLASS = "WxPayRCTWebView";
public String url;
public String referer;
private WebView wv;
/**
* 返回的 组件名称 - 原生组件名称
* @return
*/
@Override
public String getName()
return RN_CLASS;
/**
* 返回ViewManager的实例对象, 这里返回WebView的实例对象
* @param reactContext
* @return
*/
@SuppressLint("ResourceAsColor")
@Override
protected WebView createViewInstance(ThemedReactContext reactContext)
wv = new WebView(reactContext);
webSetting();
wv.setWebViewClient(new WebViewClient()
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
try
if (url.startsWith("weixin://") || url.startsWith("alipays://"))
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
reactContext.startActivity(intent);
return true;
catch (Exception e)
return false;
if (url.contains("https://wx.tenpay.com"))
Map<String, String> extraHeaders = new HashMap<>();
extraHeaders.put("Referer", referer.length() > 0 ? referer : url);
view.loadUrl(url, extraHeaders);
return true;
view.loadUrl(url);
return true;
/**
* 向js发送事件回调,传值
* @param context
* @param EventName
* @param eventMap
*/
public void dispatch(ThemedReactContext context, String EventName, WritableMap eventMap)
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(wv.getId(), EventName, eventMap);
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon)
if (wv.canGoBack())
Log.d("canGoBack===>", "yes-goback");
WritableMap event = Arguments.createMap();
event.putBoolean("canGoBack", true);
// 发送给js
this.dispatch(reactContext, "onCanGoBack", event);
else
WritableMap event = Arguments.createMap();
event.putBoolean以上是关于RN - 封装Android原生WebView组件,实现JS获取原生消息回调及JS控制native组件的主要内容,如果未能解决你的问题,请参考以下文章