使用Leancloud实现React Native App的消息推送(Push Notification)- Android篇
前言
在上一篇文章 中我们详细讲解了用Leancloud实现iOS消息推送的流程,今天本文将继续讲解实现Android的消息推送。
接入Leancloud
在接入Leancloud之前,还是推荐先阅读Leancloud官方的 Android消息推送开发指南。
安装Leancloud SDK
SDK有多种安装方式,详情请参考Android SDK安装指南。我选择用Gradle安装,先在根目录下的build.gradle
中添加Leancloud的maven仓库地址:
buildscript { repositories { jcenter() maven { url 'https://maven.google.com/' name 'Google' } maven { url "http://mvn.leancloud.cn/nexus/content/repositories/public" } } dependencies { classpath 'com.android.tools.build:gradle:2.3.3' } } allprojects { repositories { mavenLocal() jcenter() maven { url "$rootDir/../node_modules/react-native/android" } maven { url 'https://maven.google.com/' name 'Google' } maven { url "http://mvn.leancloud.cn/nexus/content/repositories/public" } } }
然后打开 app 目录下的 build.gradle
进行如下配置:
android { //为了解决部分第三方库重复打包了META-INF的问题 packagingOptions{ exclude 'META-INF/LICENSE.txt' exclude 'META-INF/NOTICE.txt' } lintOptions { abortOnError false } ... } ... dependencies { ... // LeanCloud 基础包 compile ('cn.leancloud.android:avoscloud-sdk:v4.6.4') // 推送与实时聊天需要的包 compile ('cn.leancloud.android:avoscloud-push:v4.6.4@aar'){transitive = true} }
初始化Leancloud
我们需要在App创建后用Leancloud的AppId,AppKey进行初始化,修改MainApplication
如下:
@Override public void onCreate() { super.onCreate(); ... //初始化leancloud AVOSCloud.initialize(this,"ppdriT1clcnRoda0okCPaB48-gzGzoHsz","Qzarq5cMdWzAMjwDW4umWpBL"); }
接下来,在AndroidManifest.xml
中配置Leancloud SDK所需的权限以及消息推送所需的service和receiver:
... <!-- 基础模块(必须加入以下声明)START --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 基础模块 END --> <application ... android:name=".MainApplication" > ... <!-- 实时通信模块、推送(均需要加入以下声明) START --> <!-- 实时通信模块、推送都要使用 PushService --> <service android:name="com.avos.avoscloud.PushService"/> <receiver android:name="com.avos.avoscloud.AVBroadcastReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.USER_PRESENT"/> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> </receiver> <!-- 实时通信模块、推送 END --> </application>
到此,Leancloud SDK的接入完成,我们需要测试一下SDK能不能正常使用。我们在MainActivity.java
的onCreate
方法中添加代码看能不能保存数据到Leancloud数据库:
@Override protected void onCreate(Bundle savedInstanceState) { ... // 测试 SDK 是否正常工作的代码 AVObject testObject = new AVObject("TestObject"); testObject.put("words","Hello World!"); testObject.saveInBackground(new SaveCallback() { @Override public void done(AVException e) { if(e == null){ Log.d("saved","success!"); } } }); ... }
启动App,前往Leancloud控制台,查看数据库中是否多了一条TestObject的记录,如果有说明Leancloud SDK接入成功:
保存Installation
和iOS一样,Android也需要保存installation才能让Leancloud确定推送到哪些设备。但是比较坑的是:Leancloud官方提供的 leancloud-installation只能正确保存iOS设备的installation。 因此我们只能使用Android的SDK保存installation,而且我们最好把这个方法封装成一个native模块暴露给js调用,以方便在保存成功或失败后执行相应操作。
在com.leancloudpushdemo
文件夹中创建PushModule.java
,PushDemo
继承于ReactContextBaseJavaModule
并实现ActivityEventListener
接口,添加如下代码:
package com.leancloudpushdemo; import android.app.Activity; import android.content.Intent; import com.avos.avoscloud.AVException; import com.avos.avoscloud.AVInstallation; import com.avos.avoscloud.SaveCallback; import com.facebook.react.bridge.ActivityEventListener; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; public class PushModule extends ReactContextBaseJavaModule implements ActivityEventListener { public PushModule(ReactApplicationContext reactContext) { super(reactContext); } @Override public String getName() { return "androidPushModule"; } @Override public void onNewIntent(Intent intent) {} @Override public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {} /** * 保存installation */ @ReactMethod public void saveInstaillation(final Callback resultCallback) { AVInstallation.getCurrentInstallation().saveInBackground(new SaveCallback() { public void done(AVException e) { if (e == null) { // 保存成功 String installationId = AVInstallation.getCurrentInstallation().getInstallationId(); resultCallback.invoke(installationId); } else { resultCallback.invoke(); } } }); } }
接着在同一目录下面添加PushPackage.java
用于注册PushModule
模块,代码如下:
package com.leancloudpushdemo; 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.ArrayList; import java.util.Collections; import java.util.List; public class PushPackage implements ReactPackage { @Override public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new PushModule(reactContext)); return modules; } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } }
然后,在MainApplication.java
中的getPackages
方法中增加PushPackage
:
@Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( ... new PushPackage() ); }
接着,在我们的PushService.js
中引入PushModule
并保存installation:
... import { NativeModules } from 'react-native'; const AndroidPush = NativeModules.androidPushModule; ... class PushService { ... //Android _an_initPush = () => { this._an_saveInstallation(); } _an_saveInstallation = () => { AndroidPush.saveInstaillation((installationId) => { if (installationId) { console.log('Android installation 保存成功!'); } }) } ... }
最后,在App.js
中执行Android的初始化:
componentDidMount() { if (Platform.OS === 'ios') { PushService._iOS_initPush(); } else { PushService._an_initPush(); } MessageBarManager.registerMessageBar(this.refs.alert); }
重启App,前往Leancloud控制台中查看数据库中是否多了一条installation记录,如果有说明保存成功:
如果确认代码没问题,但是还是保存不成功,我建议:
- 重启Android Studio
- 重启React Native Packager
- 重启电脑、手机。。
- 如果还有问题,欢迎咨询我
实现系统推送
启动推送服务
首先调用Leancloud SDK启动推送服务:
PushService.setDefaultPushCallback(getReactApplicationContext(), PushHandlerActivity.class);
PushHandlerActivity
为收到通知默认打开的activity,我们接下来实现。
PushHandlerActivity实现
该activity的定位为接收并初步解析通知数据。我们在com.leancloudpushdemo
文件夹下添加PushHandlerActivity.java
,内容如下:
package com.leancloudpushdemo; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import java.util.HashMap; import java.util.Map; public class PushHandlerActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); processPush(); finish(); if (!PushModule.isActive()) { //todo:判断PushModule是否实例化 relaunchActivity(); } } private void processPush() { try { Intent intent = getIntent(); String action = intent.getAction(); String channel = intent.getExtras().getString("com.avos.avoscloud.Channel"); String data = intent.getExtras().getString("com.avos.avoscloud.Data"); Map<String, String> map = new HashMap<String, String>(); map.put("action", action); map.put("channel", channel); map.put("data", data); PushModule.onReceive(map); //todo:处理通知 } catch (Exception e) { PushModule.onError(e); // todo:处理错误 } } private void relaunchActivity() { PackageManager pm = getPackageManager(); Intent launchIntent = pm.getLaunchIntentForPackage(getApplicationContext().getPackageName()); startActivity(launchIntent); } }
别忘了在AndroidManifest.xml
中加上该activity:
<activity android:name=".PushHandlerActivity"></activity>
主要处理逻辑实现
PushHandlerActivity
代码中有三处todo
是我们接下来要在PushModule
中实现的逻辑。关于接收到通知后如何处理,我的思路是当native module收到通知时,通过RCTDeviceEventEmitter
触发相应的Event,在js中监听这些Event并响应,修改PushModule
如下:
public class PushModule extends ReactContextBaseJavaModule implements ActivityEventListener { private static PushModule singleton; private static String ON_RECEIVE = "leancloudPushOnReceive"; private static String ON_ERROR = "leancloudPushOnError"; public PushModule(ReactApplicationContext reactContext) { super(reactContext); singleton = this; } ... protected static boolean isActive() { return singleton != null; } private static WritableMap getWritableMap(Map<String, String> map) { WritableMap writableMap = Arguments.createMap(); writableMap.putString("action", map.get("action")); writableMap.putString("channel", map.get("channel")); writableMap.putString("data", map.get("data")); return writableMap; } protected static void onReceive(Map<String, String> map) { if (singleton != null) { WritableMap pushNotification = getWritableMap(map); DeviceEventManagerModule.RCTDeviceEventEmitter emitter = singleton.getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class); emitter.emit(ON_RECEIVE, pushNotification); } } protected static void onError(Exception e) { if (singleton != null) { WritableMap error = Arguments.createMap(); error.putString("message", e.getLocalizedMessage()); DeviceEventManagerModule.RCTDeviceEventEmitter emitter = singleton.getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class); emitter.emit(ON_ERROR, error); } } @Override public Map<String, Object> getConstants() { final Map<String, Object> constants = new HashMap<>(); constants.put("ON_RECEIVE", ON_RECEIVE); constants.put("ON_ERROR", ON_ERROR); return constants; } ...
最后,我们在PushService.js
增加对消息通知相关事件的监听和处理的逻辑,我选择在保存installation成功后增加监听:
... import { DeviceEventEmitter } from 'react-native'; ... class PushService { ... _an_saveInstallation = () => { AndroidPush.saveInstaillation((installationId, error) => { if (installationId) { DeviceEventEmitter.addListener(AndroidPush.ON_RECEIVE, (notification) => { console.log('receive android notification'); this._an_onNotificationTapped(notification); }); DeviceEventEmitter.addListener(AndroidPush.ON_ERROR, (res) => { console.log('android notification error'); console.log(res); }); } else { console.log(error); } }) } _an_onNotificationTapped = (notification) => { Alert.alert('Android Notification Tapped'); } } ...
现在我们在Leancloud控制台发送一条通知,手机应该能收到消息:
当点击通知的时候,App打开并执行我们自定义的逻辑:
实现App打开状态下的推送
到目前为止,我们已经实现了系统级的推送,和iOS一样,我们希望Android App打开状态下也能弹出通知提醒。Leancloud提供了这样的可能,我们可以通过 自定义Receiver 来实现。
自定义Receiver
我们在com.leancloudpushdemo
路径下添加CustomPushReceiver.java
,代码如下:
package com.leancloudpushdemo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import java.util.HashMap; import java.util.Map; public class CustomPushReceiver extends BroadcastReceiver { private static final String TAG = "CustomPushReceiver"; private HandleMessage handleMessage; @Override public void onReceive(Context context, Intent intent) { try { String action = intent.getAction(); String channel = intent.getExtras().getString("com.avos.avoscloud.Channel"); //获取消息内容 String data = intent.getExtras().getString("com.avos.avoscloud.Data"); JSONObject jsonObject = JSON.parseObject(data); if (jsonObject != null) { Map<String, String> map = new HashMap<String, String>(); map.put("action", action); map.put("channel", channel); map.put("data", data); PushModule.onCustomReceive(map); //todo: 处理通知 if (handleMessage!=null){ handleMessage.receiveMessage(jsonObject); } } } catch (JSONException e) { PushModule.onError(e); } } interface HandleMessage{ public void receiveMessage(JSONObject jsonObject); } public void setHandleMessage(HandleMessage handleMessage) { this.handleMessage = handleMessage; } }
todo
的方法待会儿在PushModule
中实现。接着,在AndroidManifest.xml
中添加custom receiver:
<receiver android:name="com.leancloudpushdemo.CustomPushReceiver"> <intent-filter> <action android:name="com.cnuip.INNER_NOTI" /> </intent-filter> </receiver>
通知处理
然后修改PushModule
如下:
public class PushModule extends ReactContextBaseJavaModule implements ActivityEventListener { ... private static String ON_CUSTOM_RECEIVE = "leancloudPushOnCustomReceive"; ... protected static void onCustomReceive(Map<String, String> map) { if (singleton != null) { WritableMap pushNotification = getWritableMap(map); DeviceEventManagerModule.RCTDeviceEventEmitter emitter = singleton.getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class); emitter.emit(ON_CUSTOM_RECEIVE, pushNotification); } } ... @Override public Map<String, Object> getConstants() { final Map<String, Object> constants = new HashMap<>(); constants.put("ON_RECEIVE", ON_RECEIVE); constants.put("ON_CUSTOM_RECEIVE", ON_CUSTOM_RECEIVE); constants.put("ON_ERROR", ON_ERROR); return constants; } }
最后,修改PushService.js
,增加对ON_CUSTOM_RECEIVE
事件的监听:
... _an_saveInstallation = () => { AndroidPush.saveInstaillation((installationId, error) => { if (installationId) { ... DeviceEventEmitter.addListener(AndroidPush.ON_CUSTOM_RECEIVE, (notification) => { console.log('receive custom android notification'); this._showAlert(JSON.parse(notification.data).alert); }); ... } else { ... } }) } ...
同时通知的消息提也需要做相应修改,才能让custom receiver接收到,我们可以用Postman来发送消息:
消息发出后,App中成功弹出消息提醒,完美。
结语
经过不懈的努力,我们已经成功使用Leancloud实现了iOS和Android上的消息通知,第一次写这么长的文章还是有点累的。。如果对你有帮助欢迎点赞!还有虽然功能都实现了,但是我想可能还会有更好的实现方式,欢迎找到的同学分享,谢谢!
相关链接
iOS篇地址:使用Leancloud实现React Native App的消息推送(Push Notification)- iOS篇
本文Demo Github地址:https://github.com/MudOnTire/LeancloudPushDemo)