Skip to content

Commit

Permalink
fix(messaging, android): Replace deprecated AsyncTask API and other d…
Browse files Browse the repository at this point in the history
…eprecated API (#12580)
  • Loading branch information
russellwheatley committed Apr 15, 2024
1 parent 097fe75 commit ac089e5
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 76 deletions.
Expand Up @@ -65,7 +65,7 @@ android {
implementation platform("com.google.firebase:firebase-bom:${getRootProjectExtOrCoreProperty("FirebaseSDKVersion", firebaseCoreProject)}")
implementation 'com.google.firebase:firebase-messaging'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
implementation 'androidx.annotation:annotation:1.7.0'
implementation 'androidx.annotation:annotation:1.7.1'
implementation ('com.google.firebase:firebase-iid:21.1.0') {
transitive = true
}
Expand Down
Expand Up @@ -10,6 +10,7 @@
import android.content.res.AssetManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.firebase.messaging.RemoteMessage;
Expand Down Expand Up @@ -227,29 +228,38 @@ public void notImplemented() {
}
};
}
// RemoteMessage is passed as byte array. Check it exists first
byte[] parcelBytes =
intent.getByteArrayExtra(FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE);
if (parcelBytes != null) {
Parcel parcel = Parcel.obtain();
try {
// This converts raw byte array into data and request this happens on the entire array
parcel.unmarshall(parcelBytes, 0, parcelBytes.length);
// Sets the starting position of the data which is 0 on array
parcel.setDataPosition(0);

// Handle the message event in Dart.
RemoteMessage remoteMessage;
// Using android >= 33 API causes sporadic crashes. See: https://github.com/firebase/flutterfire/issues/11142
// remoteMessage =
// intent.getParcelableExtra(
// FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE, RemoteMessage.class);
remoteMessage = intent.getParcelableExtra(FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE);
// Now recreate the RemoteMessage from the Parcel
RemoteMessage remoteMessage = RemoteMessage.CREATOR.createFromParcel(parcel);
Map<String, Object> remoteMessageMap =
FlutterFirebaseMessagingUtils.remoteMessageToMap(remoteMessage);

if (remoteMessage != null) {
Map<String, Object> remoteMessageMap =
FlutterFirebaseMessagingUtils.remoteMessageToMap(remoteMessage);
backgroundChannel.invokeMethod(
"MessagingBackground#onMessage",
new HashMap<String, Object>() {
{
put("userCallbackHandle", getUserCallbackHandle());
put("message", remoteMessageMap);
}
},
result);
backgroundChannel.invokeMethod(
"MessagingBackground#onMessage",
new HashMap<String, Object>() {
{
put("userCallbackHandle", getUserCallbackHandle());
put("message", remoteMessageMap);
}
},
result);

} finally {
// Recycle the Parcel when done
parcel.recycle();
}
} else {
Log.e(TAG, "RemoteMessage instance not found in Intent.");
Log.e(TAG, "RemoteMessage byte array not found in Intent.");
}
}

Expand Down
Expand Up @@ -9,12 +9,10 @@
import android.os.Handler;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.firebase.messaging.RemoteMessage;
import io.flutter.embedding.engine.FlutterShellArgs;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;

public class FlutterFirebaseMessagingBackgroundService extends JobIntentService {
Expand All @@ -29,17 +27,14 @@ public class FlutterFirebaseMessagingBackgroundService extends JobIntentService
/**
* Schedule the message to be handled by the {@link FlutterFirebaseMessagingBackgroundService}.
*/
public static void enqueueMessageProcessing(Context context, Intent messageIntent) {
RemoteMessage message =
(RemoteMessage) Objects.requireNonNull(messageIntent.getExtras()).get("notification");

assert message != null;
public static void enqueueMessageProcessing(
Context context, Intent messageIntent, boolean isHighPriority) {
enqueueWork(
context,
FlutterFirebaseMessagingBackgroundService.class,
FlutterFirebaseMessagingUtils.JOB_ID,
messageIntent,
message.getOriginalPriority() == RemoteMessage.PRIORITY_HIGH);
isHighPriority);
}

/**
Expand Down
Expand Up @@ -320,8 +320,9 @@ private Task<Map<String, Object>> getInitialMessage() {
FlutterFirebaseMessagingUtils.getRemoteMessageForArguments(messageMap);

if (messageMap.get("notification") != null) {
// noinspection unchecked
notificationMap = (Map<String, Object>) messageMap.get("notification");
// noinspection
notificationMap =
(Map<String, Object>) uncheckedCastToMap(messageMap.get("notification"));
}
}
FlutterFirebaseMessagingStore.getInstance().removeFirebaseMessage(messageId);
Expand Down Expand Up @@ -613,4 +614,10 @@ public Task<Void> didReinitializeFirebaseCore() {

return taskCompletionSource.getTask();
}

private Map<String, Object> uncheckedCastToMap(Object obj) {
@SuppressWarnings("unchecked")
Map<String, Object> result = (Map<String, Object>) obj;
return result;
}
}
Expand Up @@ -7,6 +7,7 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Parcel;
import android.util.Log;
import com.google.firebase.messaging.RemoteMessage;
import java.util.HashMap;
Expand Down Expand Up @@ -50,9 +51,17 @@ public void onReceive(Context context, Intent intent) {
// ------------------------
Intent onBackgroundMessageIntent =
new Intent(context, FlutterFirebaseMessagingBackgroundService.class);

Parcel parcel = Parcel.obtain();
remoteMessage.writeToParcel(parcel, 0);
// We write to parcel using RemoteMessage.writeToParcel() to pass entire RemoteMessage as array of bytes
// Which can be read using RemoteMessage.createFromParcel(parcel) API
onBackgroundMessageIntent.putExtra(
FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE, remoteMessage);
FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE, parcel.marshall());

FlutterFirebaseMessagingBackgroundService.enqueueMessageProcessing(
context, onBackgroundMessageIntent);
context,
onBackgroundMessageIntent,
remoteMessage.getOriginalPriority() == RemoteMessage.PRIORITY_HIGH);
}
}
Expand Up @@ -24,10 +24,7 @@ interface ErrorCallback {
class FlutterFirebaseMessagingUtils {
static final String IS_AUTO_INIT_ENABLED = "isAutoInitEnabled";
static final String SHARED_PREFERENCES_KEY = "io.flutter.firebase.messaging.callback";
static final String ACTION_REMOTE_MESSAGE = "io.flutter.plugins.firebase.messaging.NOTIFICATION";
static final String EXTRA_REMOTE_MESSAGE = "notification";
static final String ACTION_TOKEN = "io.flutter.plugins.firebase.messaging.TOKEN";
static final String EXTRA_TOKEN = "token";
static final int JOB_ID = 2020;
private static final String KEY_COLLAPSE_KEY = "collapseKey";
private static final String KEY_DATA = "data";
Expand Down
Expand Up @@ -15,16 +15,19 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

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

Expand Down Expand Up @@ -342,32 +344,42 @@ public void complete() {
}

/** This is a task to dequeue and process work in the background. */
final class CommandProcessor extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
GenericWorkItem work;

if (DEBUG) Log.d(TAG, "Starting to dequeue work...");

while ((work = dequeueWork()) != null) {
if (DEBUG) Log.d(TAG, "Processing next work: " + work);
onHandleWork(work.getIntent());
if (DEBUG) Log.d(TAG, "Completing work: " + work);
work.complete();
}

if (DEBUG) Log.d(TAG, "Done processing work!");

return null;
}

@Override
protected void onCancelled(Void aVoid) {
processorFinished();
final class CommandProcessor {
private final Executor executor = Executors.newSingleThreadExecutor(); // Background thread
private final Handler handler = new Handler(Looper.getMainLooper()); // UI Thread

public void execute() {
executor.execute(
new Runnable() {
@Override
public void run() {
// This replaces doInBackground method
GenericWorkItem work;

if (DEBUG) Log.d(TAG, "Starting to dequeue work...");

while ((work = dequeueWork()) != null) {
if (DEBUG) Log.d(TAG, "Processing next work: " + work);
onHandleWork(work.getIntent());
if (DEBUG) Log.d(TAG, "Completing work: " + work);
work.complete();
}

if (DEBUG) Log.d(TAG, "Done processing work!");

// This replaces onPostExecute method
handler.post(
new Runnable() {
@Override
public void run() {
processorFinished();
}
});
}
});
}

@Override
protected void onPostExecute(Void aVoid) {
public void cancel() {
processorFinished();
}
}
Expand Down Expand Up @@ -522,18 +534,6 @@ static WorkEnqueuer getWorkEnqueuer(
*/
protected abstract void onHandleWork(@NonNull Intent intent);

/**
* Control whether code executing in {@link #onHandleWork(Intent)} will be interrupted if the job
* is stopped. By default this is false. If called and set to true, any time {@link
* #onStopCurrentWork()} is called, the class will first call {@link AsyncTask#cancel(boolean)
* AsyncTask.cancel(true)} to interrupt the running task.
*
* @param interruptIfStopped Set to true to allow the system to interrupt actively running work.
*/
public void setInterruptIfStopped(boolean interruptIfStopped) {
mInterruptIfStopped = interruptIfStopped;
}

/**
* Returns true if {@link #onStopCurrentWork()} has been called. You can use this, while executing
* your work, to see if it should be stopped.
Expand All @@ -558,7 +558,7 @@ public boolean onStopCurrentWork() {

boolean doStopCurrentWork() {
if (mCurProcessor != null) {
mCurProcessor.cancel(mInterruptIfStopped);
mCurProcessor.cancel();
}
mStopped = true;
return onStopCurrentWork();
Expand All @@ -571,7 +571,7 @@ void ensureProcessorRunningLocked(boolean reportStarted) {
mCompatWorkEnqueuer.serviceProcessingStarted();
}
if (DEBUG) Log.d(TAG, "Starting processor: " + mCurProcessor);
mCurProcessor.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
mCurProcessor.execute();
}
}

Expand Down

0 comments on commit ac089e5

Please sign in to comment.