全部产品

阿里云移动推送+ReactNative最佳实践

更新时间:2020-10-27 19:02:51

React Native(简称RN)是Facebook于2015年4月开源的跨平台移动应用开发框架,是Facebook早先开源的UI框架React在原生移动应用平台的衍生产物,目前支持Android和iOS两大平台。RN使用Javascript语言,类似于HTML的JSX,以及CSS来开发移动应用,因此熟悉Web前端开发的技术人员只需很少的学习就可以进入移动应用开发领域。

近年来Web化已成为移动端开发的一大趋势,越来越多的开发者倾向于使用ReactNative等web框架来开发App,而阿里云移动推送同样支持ReactNative开发模式,接下来为大家介绍如何在ReactNative工程中集成阿里云移动推送服务。

1. 构建您的ReactNative

如果您已有构建好的ReactNative工程,可直接跳过本节。如果您是第一次接触ReactNative,可以参考官方教程创建自己的第一个ReactNative应用。

2. 接入移动推送Android SDK

本节以ReactNative实例工程AwesomeProject作为实例工程为大家介绍移动推送Android SDK的接入步骤。

可先下载Demo示例工程,再结合本教程效果更佳。

2.1 接入推送SDK

ReactNative模式下接入移动推送SDK的方式和传统Android开发模式下接入SDK一样,相关接入方式可以参考:移动推送Android SDK接入指南,SDK版本号以接入指南为准,这里介绍Maven远程依赖具体写法,手动依赖参见接入指南。

2.1.1 配置Maven库

在Android工程根目录(比如\AwesomeProject\android\)下build.gradle文件中配置maven库URL:

  1. allprojects {
  2. repositories {
  3. jcenter()
  4. maven {
  5. url 'http://maven.aliyun.com/nexus/content/repositories/releases/'
  6. }
  7. }
  8. }
2.1.2 添加依赖

在Android工程对应的module(比如\AwesomeProject\android\app\)下的build.gradle文件中添加对应依赖:

  1. android {
  2. ......
  3. defaultConfig {
  4. applicationId "com.xxx.xxx" //包名,与控制台包名一直
  5. ......
  6. ndk {
  7. //选择要添加的对应cpu类型的.so库。
  8. //推送支持'arm64-v8a', 'armeabi', 'armeabi-v7a', 'x86', 'x86_64', 'mips', 'mips64'七种。
  9. abiFilters 'armeabi', 'x86'
  10. }
  11. ......
  12. }
  13. ......
  14. }
  15. dependencies {
  16. ......
  17. compile 'com.aliyun.ams:alicloud-android-push:3.1.12@aar'
  18. compile 'com.aliyun.ams:alicloud-android-utdid:2.5.1-proguard'
  19. compile 'com.aliyun.ams:alicloud-android-utils:1.1.6.4'
  20. compile 'com.aliyun.ams:alicloud-android-ut:5.4.3'
  21. compile 'com.aliyun.ams:alicloud-android-beacon:1.0.4.3'
  22. // 或
  23. compile 'com.aliyun.ams:alicloud-android-push:3.1.12'
  24. ......
  25. }

注意:如果在添加以上abiFilter配置之后编译或其他场景出现以下提示:

  1. NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin.

则在Android工程根目录(比如\AwesomeProject\android\)的gradle.properties文件中添加:

  1. android.useDeprecatedNdk=true
2.1.3 AppKeyhe AppSecret配置

在Android工程的对应module目录(比如\AwesomeProject\android\app\src\main\)的AndroidManifest.xml中,application标签下设置appKey,appSecret:

  1. <application android:name="*****">
  2. <meta-data android:name="com.alibaba.app.appkey" android:value="*****"/> <!-- 请填写你自己的- appKey -->
  3. <meta-data android:name="com.alibaba.app.appsecret" android:value="****"/> <!-- 请填写你自己的appSecret -->
  4. </application>

2.2 JavaScript调用推送SDK Native接口

ReactNative模式下常常需要通过JavaScript调用推送SDK native接口,完整调用过程包含以下三步。

2.2.1 创建PushModule模块

在Android工程目录的java代码目录中(比如\AwesomeProject\android\app\src\main\java\com\awesomeproject\),创建PushModule,继承自ReactContextBaseJavaModule,可参考以下代码:

  1. public class PushModule extends ReactContextBaseJavaModule {
  2. private static ReactContext context;
  3. public PushModule(ReactApplicationContext reactContext) {
  4. super(reactContext);
  5. context = reactContext;
  6. }
  7. public static ReactContext getContext() {
  8. return context;
  9. }
  10. //模块名,在JavaScript中调用相关方法时需要首先引入MPush模块
  11. @Override
  12. public String getName() {
  13. return "MPush";
  14. }
  15. @ReactMethod
  16. public void getDeviceId(Callback callback) {
  17. callback.invoke(PushServiceFactory.getCloudPushService().getDeviceId());
  18. }
  19. @ReactMethod
  20. public void bindAccount(String account, final Callback callback) {
  21. PushServiceFactory.getCloudPushService().bindAccount(account, new CommonCallback() {
  22. @Override
  23. public void onSuccess(String s) {
  24. callback.invoke("bind account success");
  25. }
  26. @Override
  27. public void onFailed(String s, String s1) {
  28. callback.invoke("bind account failed. errorCode:" + s + ", errorMsg:" + s1);
  29. }
  30. });
  31. }
  32. ......
  33. }
  • getName(): 这个方法用于在JavaScript端标记这个模块。这里我们把这个模块命名为MPush,这样就可以在JavaScript中通过NativeModules.MPush访问到这个模块。
  • @ReactMethod:要导出一个方法给JavaScript使用,Java方法需要使用注解 @ReactMethod,方法的返回类型必须为void。React Native的跨语言访问是异步进行的,所以想要给JavaScript返回一个值的唯一办法是使用回调函数或者发送事件。
2.2.2 注册模块

在Android工程目录的java代码目录中(比如\AwesomeProject\android\app\src\main\java\com\awesomeproject\),创建一个Package类 PushPackage并实现ReactPackage,在createNativeModules方法中添加这个模块。

  1. public class PushPackage implements ReactPackage {
  2. @Override
  3. public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
  4. List<NativeModule> modules = new ArrayList<>();
  5. modules.add(new PushModule(reactContext));
  6. return modules;
  7. }
  8. @Override
  9. public List<Class<? extends JavaScriptModule>> createJSModules() {
  10. return Collections.emptyList();
  11. }
  12. @Override
  13. public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
  14. return Collections.emptyList();
  15. }
  16. }
2.2.3 添加模块

在Android工程目录的java代码目录(比如\AwesomeProject\android\app\src\main\java\com\awesomeproject),Application(比如默认创建的是MainApplication extends Application implements ReactApplication)中的ReactNativeHost实例里getPackages方法添加PushPackage实例。

  1. @Override
  2. protected List<ReactPackage> getPackages() {
  3. return Arrays.<ReactPackage>asList(
  4. new MainReactPackage(),
  5. new PushPackage()
  6. );
  7. }
2.2.4 在JavaScript中调用Native方法
  1. //引入MPush模块
  2. var {NativeModules}=require('react-native');
  3. var mPush = NativeModules.MPush;
  4. export default class AwesomeProject extends Component {
  5. ......
  6. //调用Native方法
  7. getDeviceId() {
  8. var that = this;
  9. mPush.getDeviceId(function(args) {
  10. that.setState({
  11. deviceIdBtnTitle: args
  12. });
  13. });
  14. }
  15. ......
  16. }

2.3 将推送消息传递到JavaScript

当终端接收到推送消息时需要将对应的消息发送到JavaScript,此时需要用到ReactNative的事件机制。

2.3.1 消息接收Receiver改造

在消息接收回调函数中添加事件发送逻辑,先在Android工程目录的java代码目录中(比如\AwesomeProject\android\app\src\main\java\com\awesomeproject\),创建自定义接收器,比如MyMessageReceiver,继承com.alibaba.sdk.android.push.MessageReceiver:(这里举例通知类型接收到时触发的onNotification,以及消息类型接收到时触发的onMessage,全部回调接口参见SDK API介绍

  1. public class MyMessageReceiver extends MessageReceiver {
  2. public MyMessageReceiver() {
  3. super();
  4. }
  5. @Override
  6. protected void onMessage(Context context, CPushMessage cPushMessage) {
  7. super.onMessage(context, cPushMessage);
  8. WritableMap params = Arguments.createMap();
  9. params.putString("messageId", cPushMessage.getMessageId());
  10. params.putString("content", cPushMessage.getContent());
  11. params.putString("title", cPushMessage.getTitle());
  12. PushModule.sendEvent(getReactContext(), "onMessage", params);
  13. }
  14. @Override
  15. protected void onNotification(Context context, String s, String s1, Map<String, String> map) {
  16. super.onNotification(context, s, s1, map);
  17. WritableMap params = Arguments.createMap();
  18. params.putString("content", s1);
  19. params.putString("title", s);
  20. for (Map.Entry<String, String> entry: map.entrySet()) {
  21. params.putString(entry.getKey(), entry.getValue());
  22. }
  23. sendEvent(getReactContext(), "onNotification", params);
  24. }
  25. private void sendEvent(ReactContext context, String eventName, @Nullable WritableMap params) {
  26. if (context == null) {
  27. Log.i(TAG, "reactContext==null");
  28. }else{
  29. context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
  30. .emit(eventName, params);
  31. }
  32. }
  33. }

将该receiver添加到Android工程中对应module目录(比如\AwesomeProject\android\app\src\main\)的AndroidManifest.xml中:

  1. <!-- 消息接收监听器(用户可自主扩展)-->
  2. <receiver
  3. android:name=".MyMessageReceiver"
  4. android:exported="false"> <!-- 为保证receiver安全,建议设置不可导出,如需对其他应用开放可通过android:permission进行限制 -->
  5. <intent-filter>
  6. <action android:name="com.alibaba.push2.action.NOTIFICATION_OPENED" />
  7. </intent-filter>
  8. <intent-filter>
  9. <action android:name="com.alibaba.push2.action.NOTIFICATION_REMOVED" />
  10. </intent-filter>
  11. <intent-filter>
  12. <action android:name="com.alibaba.sdk.android.push.RECEIVE" />
  13. </intent-filter>
  14. </receiver>
2.3.2 JavaScript监听相关事件
  1. //导入相关模块
  2. import {
  3. AppRegistry,
  4. MPush,
  5. DeviceEventEmitter,
  6. ......
  7. } from 'react-native';
  8. export default class AwesomeProject extends Component {
  9. ......
  10. //绑定事件
  11. componentDidMount() {
  12. DeviceEventEmitter.addListener('onMessage', this.onMessage);
  13. DeviceEventEmitter.addListener('onNotification', this.onNotification);
  14. }
  15. //解绑事件
  16. componentWillUnmount() {
  17. DeviceEventEmitter.removeListener('onMessage', this.onMessage);
  18. DeviceEventEmitter.removeListener('onNotification', this.onNotification);
  19. }
  20. //事件处理逻辑
  21. onMessage(e){
  22. alert("Message Received. Title:" + e.title + ", Content:" + e.content);
  23. }
  24. onNotification(e){
  25. alert("Notification Received.Title:" + e.title + ", Content:" + e.content);
  26. }
  27. }

2.4 初始化

2.4.1 在“2.2.3 添加模块”步骤的Application中初始化推送SDK:
  1. public class MainApplication extends Application implements ReactApplication {
  2. private static final String TAG = MainApplication.class.getName();
  3. private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
  4. @Override
  5. public boolean getUseDeveloperSupport() {
  6. return BuildConfig.DEBUG;
  7. }
  8. @Override
  9. protected List<ReactPackage> getPackages() {
  10. return Arrays.<ReactPackage>asList(
  11. new MainReactPackage()
  12. new PushPackage()
  13. );
  14. }
  15. };
  16. @Override
  17. public ReactNativeHost getReactNativeHost() {
  18. return mReactNativeHost;
  19. }
  20. @Override
  21. public void onCreate() {
  22. super.onCreate();
  23. SoLoader.init(this, /* native exopackage */ false);
  24. this.initCloudChannel();
  25. }
  26. private void initCloudChannel() {
  27. PushServiceFactory.init(this.getApplicationContext());
  28. CloudPushService pushService = PushServiceFactory.getCloudPushService();
  29. pushService.register(this.getApplicationContext(), new CommonCallback() {
  30. @Override
  31. public void onSuccess(String s) {
  32. Log.e(TAG, "init cloudchannel success");
  33. }
  34. @Override
  35. public void onFailed(String s, String s1) {
  36. Log.e(TAG, "init cloudchannel failed. errorCode:" + s + ". errorMsg:" + s1);
  37. }
  38. });
  39. }
  40. }
2.4.2 Android 8.0及以上NotificationChannel机制

Android 8+系统需要单独进行适配,客户端和服务端都有设备,详情参见常见问题:Android 8.0以上设备通知接收不到

2.4.3 Android 9.0及以上需要设置允许http请求

详细参见常见问题:只在Android9+系统报errorCode为10109的错误

3 接入移动推送iOS SDK

本节以ReactNative实例工程AwesomeProject作为实例工程为大家介绍iOS推送手动集成 SDK的步骤。

3.1 控制台下载SDK

参考:移动研发平台 EMAS > 快速入门的下载SDK章节。

3.2 SDK集成

  • 打开AwesomeProject/ios/AwesomeProject.xcodeproj,将推送iOS SDK拖进工程中。
  • Build Phases -> Link Binary With Libraries中,引入下列的公共包:
    • libz.tbd
    • libresolv.tbd
    • CoreTelephony.framework
    • SystemConfiguration.framework
    • libsqlite3.tbd(阿里云平台下载的SDK无需依赖,百川平台下载的SDK需要依赖)

注意:Targets -> Build Settings -> Linking -> Other Linker Flags,请加上-ObjC这个属性,否则推送服务无法正常使用。如果之前已经设置了force_load,需要设置-force_load <framework_path>/CloudPushSDK.framework/CloudPushSDK

3.3 Xcode 设置

Xcode 8 打开推送开关,TARGETS > Capabilitie > Push Notifications,并会自动在项目中生成.entitlement文件。

3.4 SDK配置

  1. - (void)initCloudPush {
  2. [CloudPushSDK asyncInit:@"*****" appSecret:@"*****" callback:^(CloudPushCallbackResult *res) {
  3. if (res.success) {
  4. NSLog(@"Push SDK init success, deviceId: %@.", [CloudPushSDK getDeviceId]);
  5. } else {
  6. NSLog(@"Push SDK init failed, error: %@", res.error);
  7. }
  8. }];
  9. }
  • 向苹果APNs注册获取deviceToken并上报到阿里云推送服务器;
  1. /**
  2. * 注册苹果推送,获取deviceToken用于推送
  3. *
  4. * @param application
  5. */
  6. - (void)registerAPNS:(UIApplication *)application {
  7. if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
  8. // iOS 8 Notifications
  9. [application registerUserNotificationSettings:
  10. [UIUserNotificationSettings settingsForTypes:
  11. (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge)
  12. categories:nil]];
  13. [application registerForRemoteNotifications];
  14. }
  15. else {
  16. // iOS < 8 Notifications
  17. [[UIApplication sharedApplication] registerForRemoteNotificationTypes:
  18. (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
  19. }
  20. }
  21. /*
  22. * 苹果推送注册成功回调,将苹果返回的deviceToken上传到CloudPush服务器
  23. */
  24. - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  25. [CloudPushSDK registerDevice:deviceToken withCallback:^(CloudPushCallbackResult *res) {
  26. if (res.success) {
  27. NSLog(@"Register deviceToken success.");
  28. } else {
  29. NSLog(@"Register deviceToken failed, error: %@", res.error);
  30. }
  31. }];
  32. }
  33. /*
  34. * 苹果推送注册失败回调
  35. */
  36. - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
  37. NSLog(@"didFailToRegisterForRemoteNotificationsWithError %@", error);
  38. }
  • 推送消息到来监听;
  1. /**
  2. * 注册推送消息到来监听
  3. */
  4. - (void)registerMessageReceive {
  5. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMessageReceived:) name:@"CCPDidReceiveMessageNotification" object:nil];
  6. }
  7. /**
  8. * 处理到来推送消息
  9. *
  10. * @param notification
  11. */
  12. - (void)onMessageReceived:(NSNotification *)notification {
  13. CCPSysMessage *message = [notification object];
  14. NSString *title = [[NSString alloc] initWithData:message.title encoding:NSUTF8StringEncoding];
  15. NSString *body = [[NSString alloc] initWithData:message.body encoding:NSUTF8StringEncoding];
  16. NSLog(@"Receive message title: %@, content: %@.", title, body);
  17. }
  • 通知打开监听;
  1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  2. // 点击通知将App从关闭状态启动时,将通知打开回执上报
  3. // [CloudPushSDK handleLaunching:launchOptions];(Deprecated from v1.8.1)
  4. [CloudPushSDK sendNotificationAck:launchOptions];
  5. return YES;
  6. }
  7. /*
  8. * App处于启动状态时,通知打开回调
  9. */
  10. - (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo {
  11. NSLog(@"Receive one notification.");
  12. // 取得APNS通知内容
  13. NSDictionary *aps = [userInfo valueForKey:@"aps"];
  14. // 内容
  15. NSString *content = [aps valueForKey:@"alert"];
  16. // badge数量
  17. NSInteger badge = [[aps valueForKey:@"badge"] integerValue];
  18. // 播放声音
  19. NSString *sound = [aps valueForKey:@"sound"];
  20. // 取得Extras字段内容
  21. NSString *Extras = [userInfo valueForKey:@"Extras"]; //服务端中Extras字段,key是自己定义的
  22. NSLog(@"content = [%@], badge = [%ld], sound = [%@], Extras = [%@]", content, (long)badge, sound, Extras);
  23. // iOS badge 清0
  24. application.applicationIconBadgeNumber = 0;
  25. // 通知打开回执上报
  26. // [CloudPushSDK handleReceiveRemoteNotification:userInfo];(Deprecated from v1.8.1)
  27. [CloudPushSDK sendNotificationAck:userInfo];
  28. }

3.5 将推送消息传递到JavaScript

  1. // 新创建 一个继承 RCTEventEmitter 的类
  2. // SREventEmitter.h 文件
  3. #import <React/RCTBridgeModule.h>
  4. #import <React/RCTEventEmitter.h>
  5. NS_ASSUME_NONNULL_BEGIN
  6. @interface SREventEmitter : RCTEventEmitter <RCTBridgeModule>
  7. + (void)postNotiToReactNative:(NSString *)str withDic:(NSDictionary *)dic;
  8. @end
  9. NS_ASSUME_NONNULL_END
  10. // SREventEmitter.m 文件
  11. #import "SREventEmitter.h"
  12. @implementation SREventEmitter
  13. RCT_EXPORT_MODULE();
  14. - (NSArray<NSString *> *)supportedEvents {
  15. return @[@"AliyunPush"]; // 这里返回的将是你要发送的消息名的数组
  16. }
  17. - (void)startObserving {
  18. [[NSNotificationCenter defaultCenter] addObserver:self
  19. selector:@selector(emitEventInternal:)
  20. name:@"event-emitted"
  21. object:nil];
  22. }
  23. - (void)stopObserving {
  24. [[NSNotificationCenter defaultCenter] removeObserver:self];
  25. }
  26. - (void)emitEventInternal:(NSNotification *)notification {
  27. NSLog(@"\n ===== notification %@",notification);
  28. dispatch_async(dispatch_get_main_queue(), ^{
  29. [self sendEventWithName:@"AliyunPush"
  30. body:notification.object];
  31. });
  32. }
  33. + (void)postNotiToReactNative:(NSString *)str withDic:(NSDictionary *)dic {
  34. NSMutableDictionary *tempDic = [NSMutableDictionary dictionary];
  35. tempDic[@"str"] = str;
  36. tempDic[@"dic"] = dic;
  37. [[NSNotificationCenter defaultCenter] postNotificationName:@"event-emitted" object:tempDic];
  38. }
  39. @end
  40. // AppDelegate.m 文件
  41. - (void)onMessageReceived:(NSNotification *)notification {
  42. CCPSysMessage *message = [notification object];
  43. NSString *title = [[NSString alloc] initWithData:message.title encoding:NSUTF8StringEncoding];
  44. NSString *body = [[NSString alloc] initWithData:message.body encoding:NSUTF8StringEncoding];
  45. [SREventEmitter postNotiToReactNative:@"MessageReminder" withDic:@{ @"title" : title,@"body" : body }];
  46. }
  47. // index.ios.js 文件
  48. import React, { Component } from 'react';
  49. import {
  50. AppRegistry,
  51. StyleSheet,
  52. Text,
  53. View,
  54. Alert,
  55. NativeModules,
  56. NativeEventEmitter
  57. } from 'react-native';
  58. var nativeBridge = NativeModules.SREventEmitter;
  59. const NativeModule = new NativeEventEmitter(nativeBridge);
  60. export default class MyApp extends Component {
  61. render() {
  62. return (
  63. <View style={styles.container}>
  64. <Text style={styles.welcome}>
  65. This is Push iOS Demo!
  66. </Text>
  67. <Text style={styles.instructions}>
  68. To get started, edit index.js
  69. </Text>
  70. <Text style={styles.instructions}>
  71. Press Cmd+R to reload,{'\n'}
  72. Cmd+D or shake for dev menu
  73. </Text>
  74. </View>
  75. );
  76. }
  77. componentDidMount(){
  78. console.log('here2')
  79. this.subscription = NativeModule.addListener(
  80. 'AliyunPush',
  81. (reminder) => {
  82. console.log('here1')
  83. alert(reminder.str)
  84. }
  85. );
  86. }
  87. componentWillUnmount() {
  88. this.subscription.remove()
  89. }
  90. }