接触到混合应用开发提议后,首先想到了之前学的ionic,其次便是这两年火遍全球的RN,由于ionic框架用的angular.js,而且angular1和angular2写法区别很大,angular2对于初学者还是比较好接受的,但是angular1写的很乱,代码很难维护,便放弃了。决定用RN后觉得还是蛮顺手的,跟angular2很相似,又是ES6,很快便能入手,框架的选择还需要考虑的是插件的支持,尤其是:推送,广播,二维码扫描,服务,本地数据存储这些功能能否实现,由于要与硬件厂商对接,用到广播,就研究了一下如何用RN实现广播。
百度了所有的资料就是找不到如何RN实现原生广播,没有一篇类似的,于是查看一下RN项目结构,看了安卓后决定用AS加载一下看看能不能在里边编辑一下,发现MainApplication能够在应用启动时执行onCreate方法,那么我就一定能在onCreate时Toast一段话或发送一个广播的动作,那么接受发送广播基本实现了,果不其然广播真的接受到了。那么下面我要解决的问题便是RN事件如何触发android原生java代码中的方法,如果这个问题解决了,我就能通过点击事件发送一个广播。
一、RN项目添加广播,跟原生添加广播一样,两种方法,我们采用其中一种常驻广播:
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="com.example.broadcastreceiverdemo.BROADCAST"></action>
</intent-filter>
</receiver>
广播要处理的就是toast一段话:
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context arg0,Intent arg1) {
// TODO Auto-generated method stub
String string=arg1.getStringExtra("data");
Toast.makeText(arg0,"received:"+string,Toast.LENGTH_SHORT).show();
}
}
二、React Native 调用原生Android模块
该方法参考:http://www.cnblogs.com/lgp142332/p/6024280.html
我们首先来创建一个原生模块。一个原生模块是一个继承了ReactContextBaseJavaModule的Java类,它可以实现一些JavaScript所需的功能。我们这里的目标是可以在JavaScript里写ToastAndroid.show(‘Awesome’,ToastAndroid.SHORT);,来调起一个Toast通知。
package com.testproject.module;
import android.content.Intent;
import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
/** * Created by Administrator on 2017/4/15. */
public class SendBroadcastModule extends ReactContextBaseJavaModule {
public SendBroadcastModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "SendBroadcast";
}
@ReactMethod
public void sendBroadcast(){
//发送广播
Toast.makeText(getReactApplicationContext(),"开始发送...",Toast.LENGTH_SHORT).show();
Intent intent=new Intent("com.example.broadcastreceiverdemo.BROADCAST");
intent.putExtra("data","hello");
getReactApplicationContext().sendBroadcast(intent);
}
@Override
public boolean canOverrideExistingModule() {
return true;
}
}
ReactContextBaseJavaModule要求派生类实现getName方法。这个函数用于返回一个字符串名字,这个名字在JavaScript端标记这个模块。这里我们把这个模块叫做SendBroadcast,这样就可以在JavaScript中通过React.NativeModules.SendBroadcast访问到这个模块。
注意:模块名前的RCT前缀会被自动移除。所以如果返回的字符串为”RCTSendBroadcast”,在JavaScript端依然通过React.NativeModules.SendBroadcast访问到这个模块。
要导出一个方法给JavaScript使用,Java方法需要使用注解@ReactMethod。方法的返回类型必须为void。React Native的跨语言访问是异步进行的,所以想要给JavaScript返回一个值的唯一办法是使用回调函数或者发送事件。
注册模块
在Java这边要做的最后一件事就是注册这个模块。我们需要在应用的Package类的createNativeModules方法中添加这个模块。如果模块没有被注册,它也无法在JavaScript中被访问到。
package com.testproject.module;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import com.testproject.module.SendBroadcastModule;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/** * Created by Administrator on 2017/4/15. */
public class SendBroadcastPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules=new ArrayList<>();
modules.add(new SendBroadcastModule(reactContext));
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
注意:重写的两个方法createJSModules和createViewManagers不能return null,否则运行会报红屏AddAll的错误,一定要return Collections.emptyList();
接着我们需要在MainApplication中的getPackages方法中注册:
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),new BarcodeScannerPackage(),new RealmReactPackage(),new SendBroadcastPackage()//注册广播模块
);
}
接下来便是RN的js代码了:
为了让你的功能从JavaScript端访问起来更为方便,通常我们都会把原生模块封装成一个JavaScript模块。这不是必须的,但省下了每次都从NativeModules中获取对应模块的步骤。这个JS文件也可以用于添加一些其他JavaScript端实现的功能。
var { NativeModules } = require('react-native');
export { NativeModules as default }
然后在你需要调用的componet中引入NativeModules并调用发送广播的方法:
<Text onPress={ () => {
ToastAndroid.show("。。。",ToastAndroid.SHORT);
NativeModules.SendBroadcast.sendBroadcast();
}}>发送广播</Text>