Skip to content

Commit ac089e5

Browse files
fix(messaging, android): Replace deprecated AsyncTask API and other deprecated API (#12580)
1 parent 097fe75 commit ac089e5

File tree

7 files changed

+94
-76
lines changed

7 files changed

+94
-76
lines changed

packages/firebase_messaging/firebase_messaging/android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ android {
6565
implementation platform("com.google.firebase:firebase-bom:${getRootProjectExtOrCoreProperty("FirebaseSDKVersion", firebaseCoreProject)}")
6666
implementation 'com.google.firebase:firebase-messaging'
6767
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
68-
implementation 'androidx.annotation:annotation:1.7.0'
68+
implementation 'androidx.annotation:annotation:1.7.1'
6969
implementation ('com.google.firebase:firebase-iid:21.1.0') {
7070
transitive = true
7171
}

packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingBackgroundExecutor.java

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import android.content.res.AssetManager;
1111
import android.os.Handler;
1212
import android.os.Looper;
13+
import android.os.Parcel;
1314
import android.util.Log;
1415
import androidx.annotation.NonNull;
1516
import com.google.firebase.messaging.RemoteMessage;
@@ -227,29 +228,38 @@ public void notImplemented() {
227228
}
228229
};
229230
}
231+
// RemoteMessage is passed as byte array. Check it exists first
232+
byte[] parcelBytes =
233+
intent.getByteArrayExtra(FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE);
234+
if (parcelBytes != null) {
235+
Parcel parcel = Parcel.obtain();
236+
try {
237+
// This converts raw byte array into data and request this happens on the entire array
238+
parcel.unmarshall(parcelBytes, 0, parcelBytes.length);
239+
// Sets the starting position of the data which is 0 on array
240+
parcel.setDataPosition(0);
230241

231-
// Handle the message event in Dart.
232-
RemoteMessage remoteMessage;
233-
// Using android >= 33 API causes sporadic crashes. See: https://github.com/firebase/flutterfire/issues/11142
234-
// remoteMessage =
235-
// intent.getParcelableExtra(
236-
// FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE, RemoteMessage.class);
237-
remoteMessage = intent.getParcelableExtra(FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE);
242+
// Now recreate the RemoteMessage from the Parcel
243+
RemoteMessage remoteMessage = RemoteMessage.CREATOR.createFromParcel(parcel);
244+
Map<String, Object> remoteMessageMap =
245+
FlutterFirebaseMessagingUtils.remoteMessageToMap(remoteMessage);
238246

239-
if (remoteMessage != null) {
240-
Map<String, Object> remoteMessageMap =
241-
FlutterFirebaseMessagingUtils.remoteMessageToMap(remoteMessage);
242-
backgroundChannel.invokeMethod(
243-
"MessagingBackground#onMessage",
244-
new HashMap<String, Object>() {
245-
{
246-
put("userCallbackHandle", getUserCallbackHandle());
247-
put("message", remoteMessageMap);
248-
}
249-
},
250-
result);
247+
backgroundChannel.invokeMethod(
248+
"MessagingBackground#onMessage",
249+
new HashMap<String, Object>() {
250+
{
251+
put("userCallbackHandle", getUserCallbackHandle());
252+
put("message", remoteMessageMap);
253+
}
254+
},
255+
result);
256+
257+
} finally {
258+
// Recycle the Parcel when done
259+
parcel.recycle();
260+
}
251261
} else {
252-
Log.e(TAG, "RemoteMessage instance not found in Intent.");
262+
Log.e(TAG, "RemoteMessage byte array not found in Intent.");
253263
}
254264
}
255265

packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingBackgroundService.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@
99
import android.os.Handler;
1010
import android.util.Log;
1111
import androidx.annotation.NonNull;
12-
import com.google.firebase.messaging.RemoteMessage;
1312
import io.flutter.embedding.engine.FlutterShellArgs;
1413
import java.util.Collections;
1514
import java.util.LinkedList;
1615
import java.util.List;
17-
import java.util.Objects;
1816
import java.util.concurrent.CountDownLatch;
1917

2018
public class FlutterFirebaseMessagingBackgroundService extends JobIntentService {
@@ -29,17 +27,14 @@ public class FlutterFirebaseMessagingBackgroundService extends JobIntentService
2927
/**
3028
* Schedule the message to be handled by the {@link FlutterFirebaseMessagingBackgroundService}.
3129
*/
32-
public static void enqueueMessageProcessing(Context context, Intent messageIntent) {
33-
RemoteMessage message =
34-
(RemoteMessage) Objects.requireNonNull(messageIntent.getExtras()).get("notification");
35-
36-
assert message != null;
30+
public static void enqueueMessageProcessing(
31+
Context context, Intent messageIntent, boolean isHighPriority) {
3732
enqueueWork(
3833
context,
3934
FlutterFirebaseMessagingBackgroundService.class,
4035
FlutterFirebaseMessagingUtils.JOB_ID,
4136
messageIntent,
42-
message.getOriginalPriority() == RemoteMessage.PRIORITY_HIGH);
37+
isHighPriority);
4338
}
4439

4540
/**

packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingPlugin.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,9 @@ private Task<Map<String, Object>> getInitialMessage() {
320320
FlutterFirebaseMessagingUtils.getRemoteMessageForArguments(messageMap);
321321

322322
if (messageMap.get("notification") != null) {
323-
// noinspection unchecked
324-
notificationMap = (Map<String, Object>) messageMap.get("notification");
323+
// noinspection
324+
notificationMap =
325+
(Map<String, Object>) uncheckedCastToMap(messageMap.get("notification"));
325326
}
326327
}
327328
FlutterFirebaseMessagingStore.getInstance().removeFirebaseMessage(messageId);
@@ -613,4 +614,10 @@ public Task<Void> didReinitializeFirebaseCore() {
613614

614615
return taskCompletionSource.getTask();
615616
}
617+
618+
private Map<String, Object> uncheckedCastToMap(Object obj) {
619+
@SuppressWarnings("unchecked")
620+
Map<String, Object> result = (Map<String, Object>) obj;
621+
return result;
622+
}
616623
}

packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingReceiver.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import android.content.BroadcastReceiver;
88
import android.content.Context;
99
import android.content.Intent;
10+
import android.os.Parcel;
1011
import android.util.Log;
1112
import com.google.firebase.messaging.RemoteMessage;
1213
import java.util.HashMap;
@@ -50,9 +51,17 @@ public void onReceive(Context context, Intent intent) {
5051
// ------------------------
5152
Intent onBackgroundMessageIntent =
5253
new Intent(context, FlutterFirebaseMessagingBackgroundService.class);
54+
55+
Parcel parcel = Parcel.obtain();
56+
remoteMessage.writeToParcel(parcel, 0);
57+
// We write to parcel using RemoteMessage.writeToParcel() to pass entire RemoteMessage as array of bytes
58+
// Which can be read using RemoteMessage.createFromParcel(parcel) API
5359
onBackgroundMessageIntent.putExtra(
54-
FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE, remoteMessage);
60+
FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE, parcel.marshall());
61+
5562
FlutterFirebaseMessagingBackgroundService.enqueueMessageProcessing(
56-
context, onBackgroundMessageIntent);
63+
context,
64+
onBackgroundMessageIntent,
65+
remoteMessage.getOriginalPriority() == RemoteMessage.PRIORITY_HIGH);
5766
}
5867
}

packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingUtils.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@ interface ErrorCallback {
2424
class FlutterFirebaseMessagingUtils {
2525
static final String IS_AUTO_INIT_ENABLED = "isAutoInitEnabled";
2626
static final String SHARED_PREFERENCES_KEY = "io.flutter.firebase.messaging.callback";
27-
static final String ACTION_REMOTE_MESSAGE = "io.flutter.plugins.firebase.messaging.NOTIFICATION";
2827
static final String EXTRA_REMOTE_MESSAGE = "notification";
29-
static final String ACTION_TOKEN = "io.flutter.plugins.firebase.messaging.TOKEN";
30-
static final String EXTRA_TOKEN = "token";
3128
static final int JOB_ID = 2020;
3229
private static final String KEY_COLLAPSE_KEY = "collapseKey";
3330
private static final String KEY_DATA = "data";

packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/JobIntentService.java

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,19 @@
1515
import android.content.ComponentName;
1616
import android.content.Context;
1717
import android.content.Intent;
18-
import android.os.AsyncTask;
1918
import android.os.Build;
19+
import android.os.Handler;
2020
import android.os.IBinder;
21+
import android.os.Looper;
2122
import android.os.PowerManager;
2223
import android.util.Log;
2324
import androidx.annotation.NonNull;
2425
import androidx.annotation.Nullable;
2526
import androidx.annotation.RequiresApi;
2627
import java.util.ArrayList;
2728
import java.util.HashMap;
29+
import java.util.concurrent.Executor;
30+
import java.util.concurrent.Executors;
2831

2932
// Issue added for this file, we will migrate this in the future
3033
@SuppressWarnings("all")
@@ -36,7 +39,6 @@ abstract class JobIntentService extends Service {
3639
CompatJobEngine mJobImpl;
3740
WorkEnqueuer mCompatWorkEnqueuer;
3841
CommandProcessor mCurProcessor;
39-
boolean mInterruptIfStopped = false;
4042
boolean mStopped = false;
4143
boolean mDestroyed = false;
4244

@@ -342,32 +344,42 @@ public void complete() {
342344
}
343345

344346
/** This is a task to dequeue and process work in the background. */
345-
final class CommandProcessor extends AsyncTask<Void, Void, Void> {
346-
@Override
347-
protected Void doInBackground(Void... params) {
348-
GenericWorkItem work;
349-
350-
if (DEBUG) Log.d(TAG, "Starting to dequeue work...");
351-
352-
while ((work = dequeueWork()) != null) {
353-
if (DEBUG) Log.d(TAG, "Processing next work: " + work);
354-
onHandleWork(work.getIntent());
355-
if (DEBUG) Log.d(TAG, "Completing work: " + work);
356-
work.complete();
357-
}
358-
359-
if (DEBUG) Log.d(TAG, "Done processing work!");
360-
361-
return null;
362-
}
363-
364-
@Override
365-
protected void onCancelled(Void aVoid) {
366-
processorFinished();
347+
final class CommandProcessor {
348+
private final Executor executor = Executors.newSingleThreadExecutor(); // Background thread
349+
private final Handler handler = new Handler(Looper.getMainLooper()); // UI Thread
350+
351+
public void execute() {
352+
executor.execute(
353+
new Runnable() {
354+
@Override
355+
public void run() {
356+
// This replaces doInBackground method
357+
GenericWorkItem work;
358+
359+
if (DEBUG) Log.d(TAG, "Starting to dequeue work...");
360+
361+
while ((work = dequeueWork()) != null) {
362+
if (DEBUG) Log.d(TAG, "Processing next work: " + work);
363+
onHandleWork(work.getIntent());
364+
if (DEBUG) Log.d(TAG, "Completing work: " + work);
365+
work.complete();
366+
}
367+
368+
if (DEBUG) Log.d(TAG, "Done processing work!");
369+
370+
// This replaces onPostExecute method
371+
handler.post(
372+
new Runnable() {
373+
@Override
374+
public void run() {
375+
processorFinished();
376+
}
377+
});
378+
}
379+
});
367380
}
368381

369-
@Override
370-
protected void onPostExecute(Void aVoid) {
382+
public void cancel() {
371383
processorFinished();
372384
}
373385
}
@@ -522,18 +534,6 @@ static WorkEnqueuer getWorkEnqueuer(
522534
*/
523535
protected abstract void onHandleWork(@NonNull Intent intent);
524536

525-
/**
526-
* Control whether code executing in {@link #onHandleWork(Intent)} will be interrupted if the job
527-
* is stopped. By default this is false. If called and set to true, any time {@link
528-
* #onStopCurrentWork()} is called, the class will first call {@link AsyncTask#cancel(boolean)
529-
* AsyncTask.cancel(true)} to interrupt the running task.
530-
*
531-
* @param interruptIfStopped Set to true to allow the system to interrupt actively running work.
532-
*/
533-
public void setInterruptIfStopped(boolean interruptIfStopped) {
534-
mInterruptIfStopped = interruptIfStopped;
535-
}
536-
537537
/**
538538
* Returns true if {@link #onStopCurrentWork()} has been called. You can use this, while executing
539539
* your work, to see if it should be stopped.
@@ -558,7 +558,7 @@ public boolean onStopCurrentWork() {
558558

559559
boolean doStopCurrentWork() {
560560
if (mCurProcessor != null) {
561-
mCurProcessor.cancel(mInterruptIfStopped);
561+
mCurProcessor.cancel();
562562
}
563563
mStopped = true;
564564
return onStopCurrentWork();
@@ -571,7 +571,7 @@ void ensureProcessorRunningLocked(boolean reportStarted) {
571571
mCompatWorkEnqueuer.serviceProcessingStarted();
572572
}
573573
if (DEBUG) Log.d(TAG, "Starting processor: " + mCurProcessor);
574-
mCurProcessor.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
574+
mCurProcessor.execute();
575575
}
576576
}
577577

0 commit comments

Comments
 (0)