Flutter Android小米推送集成

Flutter 小米推送集成

最近 Flutter 項目須要集成推送,IOS 還好,Android 須要接入各個廠商的推送通道。可謂一步一坑,有時候真的是邊看文檔邊罵。不過還好,含淚填坑後,最終集成了華爲,小米,Vivo,Oppo,極光等推送。javascript

剛好昨晚一網友也諮詢我小米推送集成的問題,還有就是我感受小米集也確實有點稍微不同,因此記錄一下 Flutter 小米推送集成的步驟,也算是一個教程。java

Flutter 集成小米推送,有插件,插件叫:xiaomipush_plugin: ^1.0.0。源碼和用法我看了,也都挺簡單的。可是用的時候和後端推送 API 對接不起來。我出現的現象是:android

後端調接口若是攜帶參數,點擊通知能收到 NotificationMessageClicked 類型的監聽。可是若是程序處於被殺死的狀況下,沒法喚起應用從新運行。後臺定義打開應用首頁時,點擊通知卻不能收到 NotificationMessageClicked 類型的監聽。

後來我就直接把小米推送集成到本身的應用了,此處我新開一個應用總結一下:建立應用的包名:com.jumanyi.merchantnginx

開始

Step0 導入小米的集成 Jar 包

在 android/app 路徑下新建 libs 文件夾,並移入 jar 包,以下圖,並在 build.gradle 文件末尾添加:typescript

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

這裏主要是監聽一些消息的行爲。我簡單說一下我這裏的邏輯。api

當接收到註冊監聽的回調後把 RegisterId 存入共享參數,當 Flutter 那邊須要 RegisterId 時經過 MethodChanel 取就好了。微信

重寫通知的點擊事件

當應用處於前臺,再次打開 MainActiv 時會走 onNewIntent  方法,此時直接經過 EventChanel 發送數據到 Flutter,flutter 再根據參數跳轉界面便可。app

當應用處於被殺死的狀態,再次打開 MainActiv 時會走 onCreate 方法,此時把接收到的參數再寫入共享參數存儲。Flutter 選擇時候的時候獲取到參數在根據參數跳轉界面。async

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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索