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 { public static final String APP_ID="2882303761518725212"; public static final String APP_KEY="5391872516212";
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";
public static final String DATA_Reg="XiaoMi_RegId"; public static final String DATA_Push="XiaoMi_Push";
public static final String RegIdTag="regid"; public static final String PushTag="push"; 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();
if (isMiUI()&&shouldInit()){ MiPushClient.registerPush(this, BaseConstants.APP_ID,BaseConstants.APP_KEY); }
LoggerInterface newLogger = new LoggerInterface() {
@Override public void setTag(String tag) { }
@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"); }
private boolean shouldInit() { 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) { 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;
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());
String data = miPushMessage.getExtra().toString();
try{
ComponentName componentName = new ComponentName(context.getPackageName(),"com.jumanyi.merchant.MainActivity"); Intent intent=new Intent(); 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){
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"
服务
<service android:name="com.xiaomi.push.service.XMPushService" android:enabled="true" android:process=":pushservice" />
<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 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> <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>
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());
getIntentData(intent); }
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"); }
}
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;
}
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 的逻辑。
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()); }); }
String regId =await methodChannel.invokeMethod("getXiaoMiRegId");
String getReciveData = await methodChannel.invokeMethod("getReciveData");
总结
出了问题还是要静下心来,好好看看文档。
细心再细心。
思维要发散。
如果这篇文章遇到什么问题,可联系交流。