Flutter Android小米推送集成

原创
10/18 09:43
阅读数 3.2K

Flutter 小米推送集成

最近 Flutter 项目需要集成推送,IOS 还好,Android 需要接入各个厂商的推送通道。可谓一步一坑,有时候真的是边看文档边骂。不过还好,含泪填坑后,最终集成了华为,小米,Vivo,Oppo,极光等推送。

恰好昨晚一网友也咨询我小米推送集成的问题,还有就是我感觉小米集也确实有点稍微不一样,所以记录一下 Flutter 小米推送集成的步骤,也算是一个教程。

Flutter 集成小米推送,有插件,插件叫:xiaomipush_plugin: ^1.0.0。源码和用法我看了,也都挺简单的。但是用的时候和后端推送 API 对接不起来。我出现的现象是:

后端调接口如果携带参数,点击通知能收到 NotificationMessageClicked 类型的监听。但是如果程序处于被杀死的情况下,无法唤起应用重新运行。后台定义打开应用首页时,点击通知却不能收到 NotificationMessageClicked 类型的监听。

后来我就直接把小米推送集成到自己的应用了,此处我新开一个应用总结一下:创建应用的包名:com.jumanyi.merchant

开始

Step0 导入小米的集成 Jar 包

在 android/app 路径下新建 libs 文件夹,并移入 jar 包,如下图,并在 build.gradle 文件末尾添加:

dependencies {    api fileTree(include: ['*.jar'], dir: 'libs')
// implementation files('app/libs/MiPush_SDK_Client_3_8_2.jar')}

新建常量类 BaseConstants
public class BaseConstants {        //小米后台的App_ID    public static final String APP_ID="2882303761518725212";    //小米后台的App_KEY    public static final String APP_KEY="5391872516212";

//Flutter 和Android 互相通信的通道 public static final String PUSH_MSG_METHOD_CHANEL="com.push.xiaomi.msg.method"; public static final String PUSH_MSG_EVENT_CHANEL="com.push.xiaomi.msg.event";

//共享参数RegId存储的TAG public static final String DATA_Reg="XiaoMi_RegId"; public static final String DATA_Push="XiaoMi_Push";
//RegIdTag public static final String RegIdTag="regid"; //PushTag public static final String PushTag="push"; //Intent 存值的键 public static final String Extras="extras";

Step1 初始化小米推送

在包名下新建 base 包,再新建 BaseApp 类继承 FlutterApplication,重写 onCreate 方法。

package com.jumanyi.merchant.base;


import android.app.ActivityManager;import android.content.Context;import android.os.Build;import android.os.Process;import android.util.Log;
import com.jumanyi.merchant.BaseConstants;import com.xiaomi.channel.commonutils.logger.LoggerInterface;import com.xiaomi.mipush.sdk.Logger;import com.xiaomi.mipush.sdk.MiPushClient;
import java.util.List;
import io.flutter.app.FlutterApplication;
public class BaseApp extends FlutterApplication { public static final String TAG = "com.jumanyi.merchant";
@Override public void onCreate() { super.onCreate();
//Step 1. 调用注册接口 if (isMiUI()&&shouldInit()){ MiPushClient.registerPush(this, BaseConstants.APP_ID,BaseConstants.APP_KEY); }

//打开Log LoggerInterface newLogger = new LoggerInterface() {
@Override public void setTag(String tag) { // ignore }
@Override public void log(String content, Throwable t) { Log.d(TAG, content, t); }
@Override public void log(String content) { Log.d(TAG, content); } }; Logger.setLogger(this, newLogger);
}

//判断是否是小米手机 private boolean isMiUI(){
return Build.MANUFACTURER.equalsIgnoreCase("xiaomi"); }


//因为推送服务XMPushService在AndroidManifest.xml中设置为运行在另外一个进程, //这导致本Application会被实例化两次,所以我们需要让应用的主进程初始化 private boolean shouldInit() { //通过ActivityManager我们可以获得系统里正在运行的activities //包括进程(Process)等、应用程序/包、服务(Service)、任务(Task)信息。 ActivityManager am = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE)); List<ActivityManager.RunningAppProcessInfo> processInfos = am.getRunningAppProcesses(); String mainProcessName = getApplicationInfo().processName;
int myPid = Process.myPid(); for (ActivityManager.RunningAppProcessInfo info : processInfos) { //通过比较进程的唯一标识和包名判断进程里是否存在该App if (info.pid == myPid && mainProcessName.equals(info.processName)) { return true; } } return false; }}

Step2 重写 PushMessageReceiver

这里主要是监听一些消息的行为。我简单说一下我这里的逻辑。

当接收到注册监听的回调后把 RegisterId 存入共享参数,当 Flutter 那边需要 RegisterId 时通过 MethodChanel 取就行了。

重写通知的点击事件

当应用处于前台,再次打开 MainActiv 时会走 onNewIntent  方法,此时直接通过 EventChanel 发送数据到 Flutter,flutter 再根据参数跳转界面即可。

当应用处于被杀死的状态,再次打开 MainActiv 时会走 onCreate 方法,此时把接收到的参数再写入共享参数存储。Flutter 选择时候的时候获取到参数在根据参数跳转界面。

package com.jumanyi.merchant.miMsg;
import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.SharedPreferences;import android.util.Log;
import com.jumanyi.merchant.BaseConstants;import com.xiaomi.mipush.sdk.MiPushCommandMessage;import com.xiaomi.mipush.sdk.MiPushMessage;import com.xiaomi.mipush.sdk.PushMessageReceiver;

// Step 2. 重写PushMessageReceiver
public class XiaoMiMessageReceiver extends PushMessageReceiver {

//日志标签 public static final String TAG=XiaoMiMessageReceiver.class.getCanonicalName();

//通知点击事件 @Override public void onNotificationMessageClicked(Context context, MiPushMessage miPushMessage) { super.onNotificationMessageClicked(context, miPushMessage);
Log.i(TAG, "=============onNotificationMessageClicked:"+miPushMessage.toString());
//此处的data 可能是服务端传过来的参数 String data = miPushMessage.getExtra().toString();
//点击通知 拉起应用首页,此处能解决我遇到的问题 try{
ComponentName componentName = new ComponentName(context.getPackageName(),"com.jumanyi.merchant.MainActivity"); Intent intent=new Intent(); //新开一个任务栈,这样当应用处于前台,再次打开MainActivity会走 NewIntent 方法 //当应用处于杀死状态,会走onCreate方法 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setComponent(componentName); if(data!=null){ intent.putExtra(BaseConstants.Extras,data);//存入参数 } context.startActivity(intent);
}catch (Exception e){ Log.i(TAG, "=============Exception:"+e.toString()); } }


//注册结果监听 @Override public void onReceiveRegisterResult(Context context, MiPushCommandMessage message) { super.onReceiveRegisterResult(context, message);
Log.i(TAG, "=============onReceiveRegisterResult:"+message.toString());
//当注册结果到来的时候,我把它存入共享参数 if(message!=null&&message.getCommandArguments()!=null&&message.getCommandArguments().size()>0){
//保存RegisId saveRegisterId(context,message.getCommandArguments().get(0)); } }



//保存参数 private void saveRegisterId(Context context,String regId){ if(null==context||regId.isEmpty())return; //覆盖模式 SharedPreferences preferences = context.getSharedPreferences(BaseConstants.DATA_Reg,Context.MODE_PRIVATE); SharedPreferences.Editor editor= preferences.edit(); editor.putString(BaseConstants.RegIdTag,regId); editor.commit();
}
///////////////////////////////////////////////////华丽的分割线
//通知到来监听 //应用在前台时不弹出通知的通知消息到达客户端时也会回调函数 @Override public void onNotificationMessageArrived(Context context, MiPushMessage miPushMessage) { super.onNotificationMessageArrived(context, miPushMessage);
Log.i(TAG, "=============onNotificationMessageArrived:"+miPushMessage.toString());
}
@Override public void onRequirePermissions(Context context, String[] strings) { super.onRequirePermissions(context, strings); Log.i(TAG, "=============onRequirePermissions:"+strings.toString()); }

//透传消息 @Override public void onReceivePassThroughMessage(Context context, MiPushMessage miPushMessage) { super.onReceivePassThroughMessage(context, miPushMessage); Log.i(TAG, "=============onReceivePassThroughMessage:"+miPushMessage.toString()); } @Override public void onCommandResult(Context context, MiPushCommandMessage miPushCommandMessage) { super.onCommandResult(context, miPushCommandMessage);
Log.i(TAG, "=============onCommandResult:"+miPushCommandMessage.toString());
}}

Step3 配置清单文件与服务

权限
  <!--小米推送-->    <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.ACCESS_WIFI_STATE" />    <uses-permission android:name="android.permission.READ_PHONE_STATE" />    <uses-permission android:name="android.permission.VIBRATE"/>    <!--前缀用包名-->    <permission android:name="com.jumanyi.merchant.permission.MIPUSH_RECEIVE"        android:protectionLevel="signature"/>    <uses-permission android:name="com.jumanyi.merchant.permission.MIPUSH_RECEIVE"/>    <!--小米推送-->
替换 AndroidManifest.xml 文件中的 application 之前为:io.flutter.app.FlutterApplication
android:name="com.jumanyi.merchant.base.BaseApp"
服务
 <!-- xiao mi service  -->        <service            android:name="com.xiaomi.push.service.XMPushService"            android:enabled="true"            android:process=":pushservice" />
<!--注:此service必须在3.0.1版本以后(包括3.0.1版本)加入--> <service android:name="com.xiaomi.push.service.XMJobService" android:enabled="true" android:exported="false" android:permission="android.permission.BIND_JOB_SERVICE" android:process=":pushservice" />
<service android:name="com.xiaomi.mipush.sdk.PushMessageHandler" android:enabled="true" android:exported="true" />
<!--注:此service必须在2.2.5版本以后(包括2.2.5版本)加入--> <service android:name="com.xiaomi.mipush.sdk.MessageHandleService" android:enabled="true" />
<receiver android:name="com.xiaomi.push.service.receivers.NetworkStatusReceiver" android:exported="true"> <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>
<receiver android:name="com.xiaomi.push.service.receivers.PingReceiver" android:exported="false" android:process=":pushservice"> <intent-filter> <action android:name="com.xiaomi.push.PING_TIMER" /> </intent-filter> </receiver> <!--自己写的,继承了PushMessageReceiver的DemoMessageReceiver的广播注册--> <receiver android:name="com.jumanyi.merchant.miMsg.XiaoMiMessageReceiver" android:exported="true"> <intent-filter> <action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" /> </intent-filter> <intent-filter> <action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED" /> </intent-filter> <intent-filter> <action android:name="com.xiaomi.mipush.ERROR" /> </intent-filter> </receiver>
<!-- xiao mi service -->

Step4 MainActivity 逻辑

初始化通信通道

在 onCreate 方法中初始化。当 getIntent 有值的时候,将值存入共享参数。

 @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //注册通信通道        //注册事件通道        new EventChannel(getFlutterEngine().getDartExecutor().getBinaryMessenger(), BaseConstants.PUSH_MSG_EVENT_CHANEL).setStreamHandler(                new EventChannel.StreamHandler() {                    @Override                    public void onListen(Object arguments, EventChannel.EventSink events) {                        //初始化                        eventSink = events;                    }
@Override public void onCancel(Object arguments) {
} } ); //注册方法通道 new MethodChannel( getFlutterEngine().getDartExecutor().getBinaryMessenger(), BaseConstants.PUSH_MSG_METHOD_CHANEL ).setMethodCallHandler( new MethodChannel.MethodCallHandler() { @Override public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
if (call.method.equals("getReciveData")) { String content = getPushRecevieMsg(); result.success(content); } else if (call.method.equals("getXiaoMiRegId")) { String regId = getRegId(); if (!regId.isEmpty()) { result.success(regId); }else { result.success("没有获取到RegId"); } } else { result.notImplemented(); } } } );
//存贮 从被杀死的应用点击通知,传进来的参数 saveIntentData(getIntent());
} //存储消息的逻辑 private void saveIntentData(Intent intent) { if (null != intent && intent.getExtras() != null && intent.getStringExtra(BaseConstants.Extras) != null) {
String content = intent.getStringExtra(BaseConstants.Extras); Log.i(TAG, "save receive data from push, data = " + content); SharedPreferences preferences = getSharedPreferences(BaseConstants.DATA_Push, Context.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); editor.putString(BaseConstants.PushTag, content); editor.commit();
} else {
Log.i(TAG, "存储消息错误"); } }
重写 onNewIntent 方法

应用处于前台,点击通知后会走该方法,拿到参数后通过 EventChannel 把数据发给 Flutter。

  @Override    protected void onNewIntent(@NonNull Intent intent) {        super.onNewIntent(intent);        Log.i(TAG, intent.toString());        //应用处于前台时,直接通过eventChanel 通知flutter
//获取intentData getIntentData(intent); }
///////////////////////////
//获取intentData private void getIntentData(Intent intent) { if (null != intent && intent.getExtras() != null && intent.getStringExtra(BaseConstants.Extras) != null) {
String content = intent.getStringExtra(BaseConstants.Extras); Log.i(TAG, "save receive data from push, data = " + content);
pushMsgEvent(content); } else { Log.i(TAG, "intent is null"); }
}

//发送消息至flutter private void pushMsgEvent(String content) {
new Handler().postDelayed( new Runnable() { @Override public void run() { if (eventSink != null) { eventSink.success(content); } } } , 500); }
Flutter 调用 Android 方法的实现

获取 RegisterId 和推送参数。

 ///////获取消息逻辑    private String getPushRecevieMsg() {        SharedPreferences preferences = getSharedPreferences(BaseConstants.DATA_Push, Context.MODE_PRIVATE);        String data = preferences.getString(BaseConstants.PushTag, "");        SharedPreferences.Editor editor = preferences.edit();
//清理数据 if (!data.isEmpty()) { editor.remove(BaseConstants.PushTag); editor.commit(); } return data;
}


//获取注册Id private String getRegId(){ SharedPreferences preferences = getSharedPreferences(BaseConstants.DATA_Reg, Context.MODE_PRIVATE); String data = preferences.getString(BaseConstants.RegIdTag, ""); Log.i(TAG, "getReciveData"+data); return data; }

Step5 MainActivity 逻辑

flutter 中 main.dart 的逻辑。

//通信通道保持和Android 定义的一至  String PUSH_MSG_METHOD_CHANEL = "com.push.xiaomi.msg.method";  String PUSH_MSG_EVENT_CHANEL = "com.push.xiaomi.msg.event";
MethodChannel methodChannel;

//初始化通道 Future<void> initChanel() async { methodChannel = MethodChannel(PUSH_MSG_METHOD_CHANEL);
EventChannel XiaoMiEventChannel = EventChannel(PUSH_MSG_EVENT_CHANEL);
//注册监听 XiaoMiEventChannel.receiveBroadcastStream().listen((dynamic msgData) async { print("XiaoMiDataChannel ---->" + msgData.toString()); //跳转自己的页面逻辑 doSomeThing(); }, onError: (Object error) { print("XiaoMiDataChannel---->" + error.toString()); }); }

//方法名也和Android定义的一至 String regId =await methodChannel.invokeMethod("getXiaoMiRegId");
String getReciveData = await methodChannel.invokeMethod("getReciveData");

总结

  • 出了问题还是要静下心来,好好看看文档。

  • 细心再细心。

  • 思维要发散。


如果这篇文章遇到什么问题,可联系交流。

本文分享自微信公众号 - Flutter学习簿(gh_d739155d3b2c)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
1
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
1
分享
返回顶部
顶部