Skip to content

Commit bfc666d

Browse files
authored
Merge pull request flutter#38 from collinjackson/flutter#1900
[firebase_messaging] Add Android support for handling messages in background
2 parents 9cd0e07 + e762755 commit bfc666d

File tree

7 files changed

+501
-7
lines changed

7 files changed

+501
-7
lines changed

packages/firebase_messaging/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 5.1.5
2+
3+
* Enable background message handling on Android.
4+
15
## 5.1.4
26

37
* Update documentation to reflect new repository location.

packages/firebase_messaging/README.md

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,82 @@ Note: When you are debugging on Android, use a device or AVD with Google Play se
5454
<category android:name="android.intent.category.DEFAULT" />
5555
</intent-filter>
5656
```
57-
57+
#### Optionally handle background messages
58+
59+
>Background message handling is intended to be performed quickly. Do not perform
60+
long running tasks as they may not be allowed to finish by the Android system.
61+
See [Background Execution Limits](https://developer.android.com/about/versions/oreo/background)
62+
for more.
63+
64+
By default background messaging is not enabled. To handle messages in the background:
65+
66+
1. Add an Application.java class to your app
67+
68+
```
69+
package io.flutter.plugins.firebasemessagingexample;
70+
71+
import io.flutter.app.FlutterApplication;
72+
import io.flutter.plugin.common.PluginRegistry;
73+
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
74+
import io.flutter.plugins.GeneratedPluginRegistrant;
75+
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService;
76+
77+
public class Application extends FlutterApplication implements PluginRegistrantCallback {
78+
@Override
79+
public void onCreate() {
80+
super.onCreate();
81+
FlutterFirebaseMessagingService.setPluginRegistrant(this);
82+
}
83+
84+
@Override
85+
public void registerWith(PluginRegistry registry) {
86+
GeneratedPluginRegistrant.registerWith(registry);
87+
}
88+
}
89+
```
90+
1. Set name property of application in `AndroidManifest.xml`
91+
```
92+
<application android:name=".Application" ...>
93+
```
94+
1. Define a top level Dart method to handle background messages
95+
```
96+
Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) {
97+
if (message.containsKey('data')) {
98+
// Handle data message
99+
final dynamic data = message['data'];
100+
}
101+
102+
if (message.containsKey('notification')) {
103+
// Handle notification message
104+
final dynamic notification = message['notification'];
105+
}
106+
107+
// Or do other work.
108+
}
109+
```
110+
Note: the protocol of `data` and `notification` are in line with the
111+
fields defined by a [RemoteMessage](https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/RemoteMessage).
112+
1. Set `onBackgroundMessage` handler when calling `configure`
113+
```
114+
_firebaseMessaging.configure(
115+
onMessage: (Map<String, dynamic> message) async {
116+
print("onMessage: $message");
117+
_showItemDialog(message);
118+
},
119+
onBackgroundMessage: myBackgroundMessageHandler,
120+
onLaunch: (Map<String, dynamic> message) async {
121+
print("onLaunch: $message");
122+
_navigateToItemDetail(message);
123+
},
124+
onResume: (Map<String, dynamic> message) async {
125+
print("onResume: $message");
126+
_navigateToItemDetail(message);
127+
},
128+
);
129+
```
130+
Note: `configure` should be called early in the lifecycle of your application
131+
so that it can be ready to receive messages as early as possible. See the
132+
example app for a demonstration.
58133
59134
### iOS Integration
60135

packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FirebaseMessagingPlugin.java

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,15 @@ public class FirebaseMessagingPlugin extends BroadcastReceiver
4141
public static void registerWith(Registrar registrar) {
4242
final MethodChannel channel =
4343
new MethodChannel(registrar.messenger(), "plugins.flutter.io/firebase_messaging");
44+
final MethodChannel backgroundCallbackChannel =
45+
new MethodChannel(
46+
registrar.messenger(), "plugins.flutter.io/firebase_messaging_background");
4447
final FirebaseMessagingPlugin plugin = new FirebaseMessagingPlugin(registrar, channel);
4548
registrar.addNewIntentListener(plugin);
4649
channel.setMethodCallHandler(plugin);
50+
backgroundCallbackChannel.setMethodCallHandler(plugin);
51+
52+
FlutterFirebaseMessagingService.setBackgroundChannel(backgroundCallbackChannel);
4753
}
4854

4955
private FirebaseMessagingPlugin(Registrar registrar, MethodChannel channel) {
@@ -99,7 +105,40 @@ private Map<String, Object> parseRemoteMessage(RemoteMessage message) {
99105

100106
@Override
101107
public void onMethodCall(final MethodCall call, final Result result) {
102-
if ("configure".equals(call.method)) {
108+
/* Even when the app is not active the `FirebaseMessagingService` extended by
109+
* `FlutterFirebaseMessagingService` allows incoming FCM messages to be handled.
110+
*
111+
* `FcmDartService#start` and `FcmDartService#initialized` are the two methods used
112+
* to optionally setup handling messages received while the app is not active.
113+
*
114+
* `FcmDartService#start` sets up the plumbing that allows messages received while
115+
* the app is not active to be handled by a background isolate.
116+
*
117+
* `FcmDartService#initialized` is called by the Dart side when the plumbing for
118+
* background message handling is complete.
119+
*/
120+
if ("FcmDartService#start".equals(call.method)) {
121+
long setupCallbackHandle = 0;
122+
long backgroundMessageHandle = 0;
123+
try {
124+
Map<String, Long> callbacks = ((Map<String, Long>) call.arguments);
125+
setupCallbackHandle = callbacks.get("setupHandle");
126+
backgroundMessageHandle = callbacks.get("backgroundHandle");
127+
} catch (Exception e) {
128+
Log.e(TAG, "There was an exception when getting callback handle from Dart side");
129+
e.printStackTrace();
130+
}
131+
FlutterFirebaseMessagingService.setBackgroundSetupHandle(
132+
this.registrar.context(), setupCallbackHandle);
133+
FlutterFirebaseMessagingService.startBackgroundIsolate(
134+
this.registrar.context(), setupCallbackHandle);
135+
FlutterFirebaseMessagingService.setBackgroundMessageHandle(
136+
this.registrar.context(), backgroundMessageHandle);
137+
result.success(true);
138+
} else if ("FcmDartService#initialized".equals(call.method)) {
139+
FlutterFirebaseMessagingService.onInitialized();
140+
result.success(true);
141+
} else if ("configure".equals(call.method)) {
103142
FirebaseInstanceId.getInstance()
104143
.getInstanceId()
105144
.addOnCompleteListener(

0 commit comments

Comments
 (0)