在上一篇文章中,我與你分享了如何使用 Provider 去維護 Flutter 組件共用的數據狀態。在 Flutter 中狀態即數據,通過數據資源封裝、注入和讀寫這三步,我們不僅可以實現跨組件之間的數據共享,還能精確控制 UI 刷新粒度,避免無關組件的刷新。
其實,數據共享不僅存在于客戶端內部,同樣也存在于服務端與客戶端之間。比如,有新的微博評論,或者是發生了重大新聞,我們都需要在服務端把這些狀態變更的消息實時推送到客戶端,提醒用戶有新的內容。有時,我們還會針對特定的用戶畫像,通過推送實現精準的營銷信息觸達。
可以說,消息推送是增強用戶黏性,促進用戶量增長的重要手段。那么,消息推送的流程是什么樣的呢?
## 消息推送流程
手機推送每天那么多,導致在我們看來這很簡單啊。但其實,消息推送是一個橫跨業務服務器、第三方推送服務托管廠商、操作系統長連接推送服務、用戶終端、手機應用五方的復雜業務應用場景。
在 iOS 上,蘋果推送服務(APNs)接管了系統所有應用的消息通知需求;而 Android 原生,則提供了類似 Firebase 的云消息傳遞機制(FCM),可以實現統一的推送托管服務。
當某應用需要發送消息通知時,這則消息會由應用的服務器先發給蘋果或 Google,經由 APNs 或 FCM 被發送到設備,設備操作系統在完成解析后,最終把消息轉給所屬應用。這個流程的示意圖,如下所示。
:-: 
圖 1 原生消息推送流程
不過,Google 服務在大陸地區使用并不穩定,因此國行 Android 手機通常會把 Google 服務換成自己的服務,定制一套推送標準。而這對開發者來說,無疑是增大了適配負擔。所以針對 Android 端,我們通常會使用第三方推送服務,比如極光推送、友盟推送等。
雖然這些第三方推送服務使用自建的長連接,無法享受操作系統底層的優化,但它們會對所有使用推送服務的 App 共享推送通道,只要有一個使用第三方推送服務的應用沒被系統殺死,就可以讓消息及時送達。
而另一方面,這些第三方服務簡化了業務服務器與手機推送服務建立連接的操作,使得我們的業務服務器通過簡單的 API 調用就可以完成消息推送。
而為了保持 Android/iOS 方案的統一,在 iOS 上我們也會使用封裝了 APNs 通信的第三方推送服務。
第三方推送的服務流程,如下圖所示。
:-: 
圖 2 第三方推送服務流程
這些第三方推送服務廠商提供的能力和接入流程大都一致,考慮到極光的社區和生態相對活躍,所以今天我們就以極光為例,來看看在 Flutter 應用中如何才能引用原生的推送能力。
## 原生推送接入流程
要想在 Flutter 中接收推送消息,我們需要把原生的推送能力暴露給 Flutter 應用,即在原生代碼宿主實現推送能力(極光 SDK)的接入,并通過方法通道提供給 Dart 層感知推送消息的機制。
### 插件工程
在[第 26 篇](https://time.geekbang.org/column/article/127601)文章中,我們學習了如何在原生工程中的 Flutter 應用入口注冊原生代碼宿主回調,從而實現 Dart 層調用原生接口的方案。這種方案簡單直接,適用于 Dart 層與原生接口之間交互代碼量少、數據流動清晰的場景。
但對于推送這種涉及 Dart 與原生多方數據流轉、代碼量大的模塊,這種與工程耦合的方案就不利于獨立開發維護了。這時,我們需要使用 Flutter 提供的插件工程對其進行單獨封裝。
Flutter 的插件工程與普通的應用工程類似,都有 android 和 ios 目錄,這也是我們完成平臺相關邏輯代碼的地方,而 Flutter 工程插件的注冊,則仍會在應用的入口完成。除此之外,插件工程還內嵌了一個 example 工程,這是一個引用了插件代碼的普通 Flutter 應用工程。我們通過 example 工程,可以直接調試插件功能。
:-: 
圖 3 插件工程目錄結構
在了解了整體工程的目錄結構之后,接下來我們需要去 Dart 插件代碼所在的 flutter\_push\_plugin.dart 文件,實現 Dart 層的推送接口封裝。
### Dart 接口實現
為了實現消息的準確觸達,我們需要提供一個可以標識手機上 App 的地址,即 token 或 id。一旦完成地址的上報,我們就可以等待業務服務器給我們發消息了。
因為我們需要使用極光這樣的第三方推送服務,所以還得進行一些前置的應用信息關聯綁定,以及 SDK 的初始化工作。可以看到,對于一個應用而言,接入推送的過程可以拆解為以下三步:
1. 初始化極光 SDK;
2. 獲取地址 id;
3. 注冊消息通知。
這三步對應著在 Dart 層需要封裝的 3 個原生接口調用:setup、registrationID 和 setOpenNotificationHandler。
前兩個接口是在方法通道上調用原生代碼宿主提供的方法,而注冊消息通知的回調函數 setOpenNotificationHandler 則相反,是原生代碼宿主在方法通道上調用 Dart 層所提供的事件回調,因此我們需要在方法通道上為原生代碼宿主注冊反向回調方法,讓原生代碼宿主收到消息后可以直接通知它。
另外,考慮到推送是整個應用共享的能力,因此我們將 FlutterPushPlugin 這個類封裝成了單例:
~~~
//Flutter Push 插件
class FlutterPushPlugin {
// 單例
static final FlutterPushPlugin _instance = new FlutterPushPlugin.private(const MethodChannel('flutter_push_plugin'));
// 方法通道
final MethodChannel _channel;
// 消息回調
EventHandler _onOpenNotification;
// 構造方法
FlutterPushPlugin.private(MethodChannel channel) : _channel = channel {
// 注冊原生反向回調方法,讓原生代碼宿主可以執行 onOpenNotification 方法
_channel.setMethodCallHandler(_handleMethod);
}
// 初始化極光 SDK
setupWithAppID(String appID) {
_channel.invokeMethod("setup", appID);
}
// 注冊消息通知
setOpenNotificationHandler(EventHandler onOpenNotification) {
_onOpenNotification = onOpenNotification;
}
// 注冊原生反向回調方法,讓原生代碼宿主可以執行 onOpenNotification 方法
Future<Null> _handleMethod(MethodCall call) {
switch (call.method) {
case "onOpenNotification":
return _onOpenNotification(call.arguments);
default:
throw new UnsupportedError("Unrecognized Event");
}
}
// 獲取地址 id
Future<String> get registrationID async {
final String regID = await _channel.invokeMethod('getRegistrationID');
return regID;
}
}
~~~
Dart 層是原生代碼宿主的代理,可以看到這一層的接口設計算是簡單。接下來,我們分別去接管推送的 Android 和 iOS 平臺上完成相應的實現。
### Android 接口實現
考慮到 Android 平臺的推送配置工作相對較少,因此我們先用 Android Studio 打開 example 下的 android 工程進行插件開發工作。需要注意的是,由于 android 子工程的運行依賴于 Flutter 工程編譯構建產物,所以在打開 android 工程進行開發前,你需要確保整個工程代碼至少 build 過一次,否則 IDE 會報錯。
> 備注:以下操作步驟參考[極光 Android SDK 集成指南](https://docs.jiguang.cn//jpush/client/Android/android_guide/)。
首先,我們需要在插件工程下的 build.gradle 引入極光 SDK,即 jpush 與 jcore:
~~~
dependencies {
implementation 'cn.jiguang.sdk:jpush:3.3.4'
implementation 'cn.jiguang.sdk:jcore:2.1.2'
}
~~~
然后,在原生接口 FlutterPushPlugin 類中,依次把 Dart 層封裝的 3 個接口調用,即 setup、getRegistrationID 與 onOpenNotification,提供極光 Android SDK 的實現版本。
需要注意的是,由于極光 Android SDK 的信息綁定是在應用的打包配置里設置,并不需要通過代碼完成(iOS 才需要),因此 setup 方法的 Android 版本是一個空實現:
~~~
public class FlutterPushPlugin implements MethodCallHandler {
// 注冊器,通常為 MainActivity
public final Registrar registrar;
// 方法通道
private final MethodChannel channel;
// 插件實例
public static FlutterPushPlugin instance;
// 注冊插件
public static void registerWith(Registrar registrar) {
// 注冊方法通道
final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_push_plugin");
instance = new FlutterPushPlugin(registrar, channel);
channel.setMethodCallHandler(instance);
// 把初始化極光 SDK 提前至插件注冊時
JPushInterface.setDebugMode(true);
JPushInterface.init(registrar.activity().getApplicationContext());
}
// 私有構造方法
private FlutterPushPlugin(Registrar registrar, MethodChannel channel) {
this.registrar = registrar;
this.channel = channel;
}
// 方法回調
@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("setup")) {
// 極光 Android SDK 的初始化工作需要在 App 工程中配置,因此不需要代碼實現
result.success(0);
}
else if (call.method.equals("getRegistrationID")) {
// 獲取極光推送地址標識符
result.success(JPushInterface.getRegistrationID(registrar.context()));
} else {
result.notImplemented();
}
}
public void callbackNotificationOpened(NotificationMessage message) {
// 將推送消息回調給 Dart 層
channel.invokeMethod("onOpenNotification",message.notificationContent);
}
}
~~~
可以看到,我們的 FlutterPushPlugin 類中,僅提供了 callbackNotificationOpened 這個工具方法,用于推送消息參數回調給 Dart,但這個類本身并沒有去監聽極光 SDK 的推送消息。
為了獲取推送消息,我們分別需要繼承極光 SDK 提供的兩個類:JCommonService 和 JPushMessageReceiver。
* JCommonService 是一個后臺 Service,實際上是極光共享長連通道的核心,可以在多手機平臺上使得推送通道更穩定。
* JPushMessageReceiver 則是一個 BroadcastReceiver,推送消息的獲取都是通過它實現的。我們可以通過覆蓋其 onNotifyMessageOpened 方法,從而在用戶點擊系統推送消息時獲取到通知。
作為 BroadcastReceiver 的 JPushMessageReceiver,可以長期在后臺存活,監聽遠端推送消息,但 Flutter 可就不行了,操作系統會隨時釋放掉后臺應用所占用的資源。因此,**在用戶點擊推送時,我們在收到相應的消息回調后,需要做的第一件事情不是立刻通知 Flutter,而是應該啟動應用的 MainActivity。**在確保 Flutter 已經完全初始化后,才能通知 Flutter 有新的推送消息。
因此在下面的代碼中,我們在打開 MainActivity 后,等待了 1 秒,才執行相應的 Flutter 回調通知:
~~~
//JPushXCustomService.java
// 長連通道核心,可以使推送通道更穩定
public class JPushXCustomService extends JCommonService {
}
//JPushXMessageReceiver.java
// 獲取推送消息的 Receiver
public class JPushXMessageReceiver extends JPushMessageReceiver {
// 用戶點擊推送消息回調
@Override
public void onNotifyMessageOpened(Context context, final NotificationMessage message) {
try {
// 找到 MainActivity
String mainClassName = context.getApplicationContext().getPackageName() + ".MainActivity";
Intent i = new Intent(context, Class.forName(mainClassName));
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
// 啟動主 Activity
context.startActivity(i);
} catch (Exception e) {
Log.e("tag"," 找不到 MainActivity");
}
new Timer().schedule(new TimerTask() {
@Override
public void run() {
FlutterPushPlugin.instance.callbackNotificationOpened(message);
}
},1000); // 延遲 1 秒通知 Dart
}
}
~~~
最后,我們還需要在插件工程的 AndroidManifest.xml 中,分別聲明 receiver JPushXMessageReceiver 和 service JPushXCustomService,完成對系統的注冊:
~~~
...
<application>
<!-- 注冊推送消息接收類 -->
<receiver android:name=".JPushXMessageReceiver">
<intent-filter>
<action android:name="cn.jpush.android.intent.RECEIVE_MESSAGE" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<!-- 注冊長連通道 Service -->
<service android:name=".JPushXCustomService"
android:enabled="true"
android:exported="false"
android:process=":pushcore">
<intent-filter>
<action android:name="cn.jiguang.user.service.action" />
</intent-filter>
</service>
</application>
...
~~~
接收消息和回調消息的功能完成后,FlutterPushPlugin 插件的 Android 部分就搞定了。接下來,我們去開發插件的 iOS 部分。
### iOS 接口實現
與 Android 類似,我們需要使用 Xcode 打開 example 下的 ios 工程進行插件開發工作。同樣,在打開 ios 工程前,你需要確保整個工程代碼至少 build 過一次,否則 IDE 會報錯。
> 備注:以下操作步驟參考[極光 iOS SDK 集成指南](https://docs.jiguang.cn//jpush/client/iOS/ios_guide_new/)
首先,我們需要在插件工程下的 flutter\_push\_plugin.podspec 文件中引入極光 SDK,即 jpush。這里,我們選用了不使用廣告 id 的版本:
~~~
Pod::Spec.new do |s|
...
s.dependency 'JPush', '3.2.2-noidfa'
end
~~~
然后,在原生接口 FlutterPushPlugin 類中,同樣依次為 setup、getRegistrationID 與 onOpenNotification,提供極光 iOS SDK 的實現版本。
需要注意的是,APNs 的推送消息是在 ApplicationDelegate 中回調的,所以我們需要在注冊插件時,為插件提供同名的回調函數,讓極光 SDK 把推送消息轉發到插件的回調函數中。
與 Android 類似,在極光 SDK 收到推送消息時,我們的應用可能處于后臺,因此在用戶點擊了推送消息,把 Flutter 應用喚醒時,我們應該在確保 Flutter 已經完全初始化后,才能通知 Flutter 有新的推送消息。
因此在下面的代碼中,我們在用戶點擊了推送消息后也等待了 1 秒,才執行相應的 Flutter 回調通知:
~~~
@implementation FlutterPushPlugin
// 注冊插件
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
// 注冊方法通道
FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"flutter_push_plugin" binaryMessenger:[registrar messenger]];
// 初始化插件實例,綁定方法通道
FlutterPushPlugin* instance = [[FlutterPushPlugin alloc] init];
instance.channel = channel;
// 為插件提供 ApplicationDelegate 回調方法
[registrar addApplicationDelegate:instance];
// 注冊方法通道回調函數
[registrar addMethodCallDelegate:instance channel:channel];
}
// 處理方法調用
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if([@"setup" isEqualToString:call.method]) {
// 極光 SDK 初始化方法
[JPUSHService setupWithOption:self.launchOptions appKey:call.arguments channel:@"App Store" apsForProduction:YES advertisingIdentifier:nil];
} else if ([@"getRegistrationID" isEqualToString:call.method]) {
// 獲取極光推送地址標識符
[JPUSHService registrationIDCompletionHandler:^(int resCode, NSString *registrationID) {
result(registrationID);
}];
} else {
// 方法未實現
result(FlutterMethodNotImplemented);
}
}
// 應用程序啟動回調
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 初始化極光推送服務
JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
// 設置推送權限
entity.types = JPAuthorizationOptionAlert|JPAuthorizationOptionBadge|JPAuthorizationOptionSound;
// 請求推送服務
[JPUSHService registerForRemoteNotificationConfig:entity delegate:self];
// 存儲 App 啟動狀態,用于后續初始化調用
self.launchOptions = launchOptions;
return YES;
}
// 推送 token 回調
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
/// 注冊 DeviceToken,換取極光推送地址標識符
[JPUSHService registerDeviceToken:deviceToken];
}
// 推送被點擊回調
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
// 獲取推送消息
NSDictionary * userInfo = response.notification.request.content.userInfo;
NSString *content = userInfo[@"aps"][@"alert"];
if ([content isKindOfClass:[NSDictionary class]]) {
content = userInfo[@"aps"][@"alert"][@"body"];
}
// 延遲 1 秒通知 Flutter,確保 Flutter 應用已完成初始化
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.channel invokeMethod:@"onOpenNotification" arguments:content];
});
// 清除應用的小紅點
UIApplication.sharedApplication.applicationIconBadgeNumber = 0;
// 通知系統,推送回調處理完畢
completionHandler();
}
// 前臺應用收到了推送消息
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger options))completionHandler {
// 通知系統展示推送消息提示
completionHandler(UNNotificationPresentationOptionAlert);
}
@end
~~~
至此,在完成了極光 iOS SDK 的接口封裝之后,FlutterPushPlugin 插件的 iOS 部分也搞定了。
FlutterPushPlugin 插件為 Flutter 應用提供了原生推送的封裝,不過要想 example 工程能夠真正地接收到推送消息,我們還需要對 exmaple 工程進行最后的配置,即:為它提供應用推送證書,并關聯極光應用配置。
### 應用工程配置
在單獨為 Android/iOS 應用進行推送配置之前,我們首先需要去[極光的官方網站](https://www.jiguang.cn/dev2/#/overview/appCardList),為 example 應用注冊一個唯一標識符(即 AppKey):
:-: 
圖 4 極光應用注冊
在得到了 AppKey 之后,我們需要**依次進行 Android 與 iOS 的配置工作**。
Android 的配置工作相對簡單,整個配置過程完全是應用與極光 SDK 的關聯工作。
首先,根據 example 的 Android 工程包名,完成 Android 工程的推送注冊:
:-: 
圖 5 example Android 推送注冊
然后,通過 AppKey,在 app 的 build.gradle 文件中實現極光信息的綁定:
~~~
defaultConfig {
...
//ndk 支持架構
ndk {
abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a'
}
manifestPlaceholders = [
JPUSH_PKGNAME : applicationId, // 包名
JPUSH_APPKEY : "f861910af12a509b34e266c2", //JPush 上注冊的包名對應的 Appkey
JPUSH_CHANNEL : "developer-default", // 填寫默認值即可
]
}
~~~
至此,Android 部分的所有配置工作和接口實現都已經搞定了。接下來,我們再來看看 iOS 的配置實現。
**iOS 的應用配置相對 Android 會繁瑣一些**,因為整個配置過程涉及應用、蘋果 APNs 服務、極光三方之間的信息關聯。
除了需要在應用內綁定極光信息之外(即 handleMethodCall 中的 setup 方法),還需要在[蘋果的開發者官網](https://developer.apple.com/)提前申請蘋果的推送證書。關于申請證書,蘋果提供了.p12 證書和 APNs Auth Key 兩種鑒權方式。
這里,我推薦使用更為簡單的 Auth Key 方式。申請推送證書的過程,極光官網提供了詳細的[注冊步驟](https://docs.jiguang.cn//jpush/client/iOS/ios_cer_guide/),這里我就不再贅述了。需要注意的是,申請 iOS 的推送證書時,你只能使用付費的蘋果開發者賬號。
在拿到了 APNs Auth Key 之后,我們同樣需要去極光官網,根據 Bundle ID 進行推送設置,并把 Auth Key 上傳至極光進行托管,由它完成與蘋果的鑒權工作:
:-: 
圖 6 example iOS 推送注冊
通過上面的步驟,我們已經完成了將推送證書與極光信息綁定的操作,接下來,我們**回到 Xcode 打開的 example 工程,進行最后的配置工作**。
首先,我們需要為 example 工程開啟 Application Target 的 Capabilities->Push Notifications 選項,啟動應用的推送能力支持,如下圖所示:
:-: 
圖 7 example iOS 推送配置
然后,我們需要切換到 Application Target 的 Info 面板,手動配置 NSAppTransportSecurity 鍵值對,以支持極光 SDK 非 https 域名服務:
:-: 
圖 8 example iOS 支持 Http 配置
最后,在 Info tab 下的 Bundle identifier 項,把我們剛剛在極光官網注冊的 Bundle ID 顯式地更新進去:
:-: 
圖 9 Bundle ID 配置
至此,example 工程運行所需的所有原生配置工作和接口實現都已經搞定了。接下來,我們就可以在 example 工程中的 main.dart 文件中,使用 FlutterPushPlugin 插件來實現原生推送能力了。
在下面的代碼中,我們在 main 函數的入口,使用插件單例注冊了極光推送服務,隨后在應用 State 初始化時,獲取了極光推送地址,并設置了消息推送回調:
~~~
// 獲取推送插件單例
FlutterPushPlugin fpush = FlutterPushPlugin();
void main() {
// 使用 AppID 注冊極光推送服務 (僅針對 iOS 平臺)
fpush.setupWithAppID("f861910af12a509b34e266c2");
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// 極光推送地址 regID
String _regID = 'Unknown';
// 接收到的推送消息
String _notification = "";
@override
initState() {
super.initState();
// 注冊推送消息回調
fpush.setOpenNotificationHandler((String message) async {
// 刷新界面狀態,展示推送消息
setState(() {
_notification = message;
});
});
// 獲取推送地址 regID
initPlatformState();
}
initPlatformState() async {
// 調用插件封裝的 regID
String regID = await fpush.registrationID;
// 刷新界面狀態,展示 regID
setState(() {
_regID = regID;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
children: <Widget>[
// 展示 regID,以及收到的消息
Text('Running on: $_regID\n'),
Text('Notification Received: $_notification')
],
),
),
),
);
}
}
~~~
點擊運行,可以看到,我們的應用已經可以獲取到極光推送地址了:
:-: 
圖 10 iOS 運行示例
:-: 
圖 11 Android 運行示例
接下來,我們再去[極光開發者服務后臺](https://www.jiguang.cn/dev2/#/overview/appCardList)發一條真實的推送消息。在服務后臺選擇我們的 App,隨后進入極光推送控制臺。這時,我們就可以進行消息推送測試了。
在發送通知一欄,我們把通知標題改為“測試”,通知內容設置為“極光推送測試”;在目標人群一欄,由于是測試賬號,我們可以直接選擇“廣播所有人”,如果你希望精確定位到接收方,也可以提供在應用中獲取到的極光推送地址(即 Registration ID):
:-: 
圖 12 極光推送后臺
點擊發送預覽并確認,可以看到,我們的應用不僅可以被來自極光的推送消息喚醒,還可以在 Flutter 應用內收到來自原生宿主轉發的消息內容:
:-:
圖13 iOS 推送消息
:-: 
圖 14 Android 推送消息
## 總結
好了,今天的分享就到這里。我們一起來小結一下吧。
我們以 Flutter 插件工程的方式,為極光 SDK 提供了一個 Dart 層的封裝。插件工程同時提供了 iOS 和 Android 目錄,我們可以在這兩個目錄下完成原生代碼宿主封裝,不僅可以為 Dart 層提供接口正向回調(比如,初始化、獲取極光推送地址),還可以通過方法通道以反向回調的方式將推送消息轉發給 Dart。
今天,我和你分享了很多原生代碼宿主的配置、綁定、注冊的邏輯。不難發現,推送過程鏈路長、涉眾多、配置復雜,要想在 Flutter 完全實現原生推送能力,工作量主要集中在原生代碼宿主,Dart 層能做的事情并不多。
我把今天分享所改造的[Flutter\_Push\_Plugin](https://github.com/cyndibaby905/31_flutter_push_plugin)放到了 GitHub 中,你可以把插件工程下載下來,多運行幾次,體會插件工程與普通 Flutter 工程的異同,并加深對消息推送全流程的認識。其中,Flutter\_Push\_Plugin 提供了實現原生推送功能的最小集合,你可以根據實際需求完善這個插件。
需要注意的是,我們今天的實際工程演示是通過內嵌的 example 工程示例所完成的,如果你有一個獨立的 Flutter 工程(比如[Flutter\_Push\_Demo](https://github.com/cyndibaby905/31_flutter_push_demo))需要接入 Flutter\_Push\_Plugin,其配置方式與 example 工程并無不同,唯一的區別是,需要在 pubspec.yaml 文件中將對插件的依賴顯示地聲明出來而已:
~~~
dependencies:
flutter_push_plugin:
git:
url: https://github.com/cyndibaby905/31_flutter_push_plugin.git
~~~
## 思考題
在 Flutter\_Push\_Plugin 的原生實現中,用戶點擊了推送消息把 Flutter 應用喚醒時,為了確保 Flutter 完成初始化,我們等待了 1 秒才執行相應的 Flutter 回調通知。這段邏輯有需要優化的地方嗎?為了讓 Flutter 代碼能夠更快地收到推送消息,你會如何優化呢?
- 前言
- 開篇詞
- 預習篇
- 01丨預習篇 · 從0開始搭建Flutter工程環境
- 02丨預習篇 · Dart語言概覽
- Flutter開發起步
- 03丨深入理解跨平臺方案的歷史發展邏輯
- 04丨Flutter區別于其他方案的關鍵技術是什么?
- 05丨從標準模板入手,體會Flutter代碼是如何運行在原生系統上的
- Dart語言基礎
- 06丨基礎語法與類型變量:Dart是如何表示信息的?
- 07丨函數、類與運算符:Dart是如何處理信息的?
- 08丨綜合案例:掌握Dart核心特性
- Flutter基礎
- 09丨Widget,構建Flutter界面的基石
- 10丨Widget中的State到底是什么?
- 11丨提到生命周期,我們是在說什么?
- 12丨經典控件(一):文本、圖片和按鈕在Flutter中怎么用?
- 13丨ListView在Flutter中是什么?
- 14 丨 經典布局:如何定義子控件在父容器中排版位置?
- 15 丨 組合與自繪,我該選用何種方式自定義Widget?
- 16 丨 從夜間模式說起,如何定制不同風格的App主題?
- 17丨依賴管理(一):圖片、配置和字體在Flutter中怎么用?
- 18丨依賴管理(二):第三方組件庫在Flutter中要如何管理?
- 19丨用戶交互事件該如何響應?
- 20丨關于跨組件傳遞數據,你只需要記住這三招
- 21丨路由與導航,Flutter是這樣實現頁面切換的
- Flutter進階
- 22丨如何構造炫酷的動畫效果?
- 23丨單線程模型怎么保證UI運行流暢?
- 24丨HTTP網絡編程與JSON解析
- 25丨本地存儲與數據庫的使用和優化
- 26丨如何在Dart層兼容Android-iOS平臺特定實現?(一)
- 27丨如何在Dart層兼容Android-iOS平臺特定實現?(二)
- 28丨如何在原生應用中混編Flutter工程?
- 29丨混合開發,該用何種方案管理導航棧?
- 30丨為什么需要做狀態管理,怎么做?
- 31丨如何實現原生推送能力?
- 32丨適配國際化,除了多語言我們還需要注意什么
- 33丨如何適配不同分辨率的手機屏幕?
- 34丨如何理解Flutter的編譯模式?
- 35丨HotReload是怎么做到的?
- 36丨如何通過工具鏈優化開發調試效率?
- 37丨如何檢測并優化FlutterApp的整體性能表現?
- 38丨如何通過自動化測試提高交付質量?
- Flutter綜合應用
- 39丨線上出現問題,該如何做好異常捕獲與信息采集?
- 40丨衡量FlutterApp線上質量,我們需要關注這三個指標
- 41丨組件化和平臺化,該如何組織合理穩定的Flutter工程結構?
- 42丨如何構建高效的FlutterApp打包發布環境?
- 43丨如何構建自己的Flutter混合開發框架(一)?
- 44丨如何構建自己的Flutter混合開發框架(二)?
- 結束語
- 結束語丨勿畏難,勿輕略