Messages: Replace (Safe)AsyncTask
* AsyncTask is deprecated * Executors and Handlers can achieve the same thing and are not deprecated Change-Id: I5271bb73b848ce885eeaf5632c1da27853c56c4a
This commit is contained in:
@@ -39,7 +39,6 @@ import com.android.messaging.util.ContactUtil;
|
||||
import com.android.messaging.util.LogUtil;
|
||||
import com.android.messaging.util.OsUtil;
|
||||
import com.android.messaging.util.PhoneUtils;
|
||||
import com.android.messaging.util.SafeAsyncTask;
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -47,6 +46,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
@@ -146,7 +146,7 @@ public class ParticipantRefresh {
|
||||
if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
|
||||
LogUtil.v(TAG, "Started full participant refresh");
|
||||
}
|
||||
SafeAsyncTask.executeOnThreadPool(sFullRefreshRunnable);
|
||||
Executors.newSingleThreadExecutor().execute(sFullRefreshRunnable);
|
||||
} else if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
|
||||
LogUtil.v(TAG, "Skipped full participant refresh");
|
||||
}
|
||||
@@ -156,7 +156,7 @@ public class ParticipantRefresh {
|
||||
* Refresh self participants on subscription or settings change.
|
||||
*/
|
||||
public static void refreshSelfParticipants() {
|
||||
SafeAsyncTask.executeOnThreadPool(sSelfOnlyRefreshRunnable);
|
||||
Executors.newSingleThreadExecutor().execute(sSelfOnlyRefreshRunnable);
|
||||
}
|
||||
|
||||
private static boolean getNeedFullRefresh() {
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
package com.android.messaging.datamodel.data;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.messaging.datamodel.MessageTextStats;
|
||||
@@ -37,7 +39,6 @@ import com.android.messaging.util.Assert.RunsOnMainThread;
|
||||
import com.android.messaging.util.BugleGservicesKeys;
|
||||
import com.android.messaging.util.LogUtil;
|
||||
import com.android.messaging.util.PhoneUtils;
|
||||
import com.android.messaging.util.SafeAsyncTask;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -45,6 +46,11 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class DraftMessageData extends BindableData implements ReadDraftDataActionListener {
|
||||
|
||||
@@ -678,7 +684,7 @@ public class DraftMessageData extends BindableData implements ReadDraftDataActio
|
||||
// Any change in the draft will cancel any pending draft checking task, since the
|
||||
// size/status of the draft may have changed.
|
||||
if (mCheckDraftForSendTask != null) {
|
||||
mCheckDraftForSendTask.cancel(true /* mayInterruptIfRunning */);
|
||||
mCheckDraftForSendTask.cancel();
|
||||
mCheckDraftForSendTask = null;
|
||||
}
|
||||
mListeners.onDraftChanged(this, changeFlags);
|
||||
@@ -708,7 +714,7 @@ public class DraftMessageData extends BindableData implements ReadDraftDataActio
|
||||
public void checkDraftForAction(final boolean checkMessageSize, final int selfSubId,
|
||||
final CheckDraftTaskCallback callback, final Binding<DraftMessageData> binding) {
|
||||
new CheckDraftForSendTask(checkMessageSize, selfSubId, callback, binding)
|
||||
.executeOnThreadPool((Void) null);
|
||||
.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -750,7 +756,7 @@ public class DraftMessageData extends BindableData implements ReadDraftDataActio
|
||||
void onDraftChecked(DraftMessageData data, int result);
|
||||
}
|
||||
|
||||
public class CheckDraftForSendTask extends SafeAsyncTask<Void, Void, Integer> {
|
||||
public class CheckDraftForSendTask {
|
||||
public static final int RESULT_PASSED = 0;
|
||||
public static final int RESULT_HAS_PENDING_ATTACHMENTS = 1;
|
||||
public static final int RESULT_NO_SELF_PHONE_NUMBER_IN_GROUP_MMS = 2;
|
||||
@@ -762,6 +768,10 @@ public class DraftMessageData extends BindableData implements ReadDraftDataActio
|
||||
private final CheckDraftTaskCallback mCallback;
|
||||
private final String mBindingId;
|
||||
private final List<MessagePartData> mAttachmentsCopy;
|
||||
ScheduledExecutorService mExecutor = Executors.newScheduledThreadPool(2);
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private Future<?> mFuture;
|
||||
private boolean mCancelled;
|
||||
private int mPreExecuteResult = RESULT_PASSED;
|
||||
|
||||
public CheckDraftForSendTask(final boolean checkMessageSize, final int selfSubId,
|
||||
@@ -777,7 +787,51 @@ public class DraftMessageData extends BindableData implements ReadDraftDataActio
|
||||
mCheckDraftForSendTask = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
onPreExecute();
|
||||
|
||||
mFuture = mExecutor.submit(() -> {
|
||||
final int result;
|
||||
if (mPreExecuteResult != RESULT_PASSED) {
|
||||
result = mPreExecuteResult;
|
||||
} else if (mCheckMessageSize && getIsMessageOverLimit()) {
|
||||
result = RESULT_MESSAGE_OVER_LIMIT;
|
||||
} else {
|
||||
result = RESULT_PASSED;
|
||||
}
|
||||
|
||||
mHandler.post(() -> {
|
||||
mCheckDraftForSendTask = null;
|
||||
// Only call back if we are bound to the original binding.
|
||||
if (isBound(mBindingId) && !mCancelled) {
|
||||
mCallback.onDraftChecked(DraftMessageData.this, result);
|
||||
} else {
|
||||
if (!isBound(mBindingId)) {
|
||||
LogUtil.w(LogUtil.BUGLE_TAG, "Message can't be sent: draft not bound");
|
||||
}
|
||||
if (mCancelled) {
|
||||
LogUtil.w(LogUtil.BUGLE_TAG,
|
||||
"Message can't be sent: draft is cancelled");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
mExecutor.schedule(this::cancel, 10, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (!mFuture.isDone()) {
|
||||
mFuture.cancel(true);
|
||||
mCancelled = true;
|
||||
mCheckDraftForSendTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return mCancelled;
|
||||
}
|
||||
|
||||
protected void onPreExecute() {
|
||||
// Perform checking work that can happen on the main thread.
|
||||
if (hasPendingAttachments()) {
|
||||
@@ -803,39 +857,6 @@ public class DraftMessageData extends BindableData implements ReadDraftDataActio
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer doInBackgroundTimed(Void... params) {
|
||||
if (mPreExecuteResult != RESULT_PASSED) {
|
||||
return mPreExecuteResult;
|
||||
}
|
||||
|
||||
if (mCheckMessageSize && getIsMessageOverLimit()) {
|
||||
return RESULT_MESSAGE_OVER_LIMIT;
|
||||
}
|
||||
return RESULT_PASSED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Integer result) {
|
||||
mCheckDraftForSendTask = null;
|
||||
// Only call back if we are bound to the original binding.
|
||||
if (isBound(mBindingId) && !isCancelled()) {
|
||||
mCallback.onDraftChecked(DraftMessageData.this, result);
|
||||
} else {
|
||||
if (!isBound(mBindingId)) {
|
||||
LogUtil.w(LogUtil.BUGLE_TAG, "Message can't be sent: draft not bound");
|
||||
}
|
||||
if (isCancelled()) {
|
||||
LogUtil.w(LogUtil.BUGLE_TAG, "Message can't be sent: draft is cancelled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled() {
|
||||
mCheckDraftForSendTask = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Check if the draft message contains too many attachments to send
|
||||
* 2. Computes the minimum size that this message could be compressed/downsampled/encoded
|
||||
|
||||
@@ -43,10 +43,10 @@ import com.android.messaging.util.ContentType;
|
||||
import com.android.messaging.util.GifTranscoder;
|
||||
import com.android.messaging.util.ImageUtils;
|
||||
import com.android.messaging.util.LogUtil;
|
||||
import com.android.messaging.util.SafeAsyncTask;
|
||||
import com.android.messaging.util.UriUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Represents a single message part. Messages consist of one or more parts which may contain
|
||||
@@ -444,7 +444,7 @@ public class MessagePartData implements Parcelable {
|
||||
public void destroyAsync() {
|
||||
final Uri contentUri = shouldDestroy();
|
||||
if (contentUri != null) {
|
||||
SafeAsyncTask.executeOnThreadPool(() ->
|
||||
Executors.newSingleThreadExecutor().execute(() ->
|
||||
Factory.get().getApplicationContext().getContentResolver().delete(
|
||||
contentUri, null, null));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package com.android.messaging.datamodel.data;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -24,9 +26,13 @@ import androidx.annotation.NonNull;
|
||||
import com.android.messaging.util.Assert;
|
||||
import com.android.messaging.util.ContentType;
|
||||
import com.android.messaging.util.LogUtil;
|
||||
import com.android.messaging.util.SafeAsyncTask;
|
||||
import com.android.messaging.util.UriUtil;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Represents a "pending" message part that acts as a placeholder for the actual attachment being
|
||||
* loaded. It handles the task to load and persist the attachment from a Uri to local scratch
|
||||
@@ -98,37 +104,26 @@ public class PendingAttachmentData extends MessagePartData {
|
||||
}
|
||||
mCurrentState = STATE_LOADING;
|
||||
|
||||
// Kick off a SafeAsyncTask to load the content of the media and persist it locally.
|
||||
// Kick off loading the content of the media and persist it locally.
|
||||
// Note: we need to persist the media locally even if it's not remote, because we
|
||||
// want to be able to resend the media in case the message failed to send.
|
||||
new SafeAsyncTask<Void, Void, MessagePartData>(LOAD_MEDIA_TIME_LIMIT_MILLIS,
|
||||
true /* cancelExecutionOnTimeout */) {
|
||||
@Override
|
||||
protected MessagePartData doInBackgroundTimed(final Void... params) {
|
||||
final Uri contentUri = getContentUri();
|
||||
final Uri persistedUri = UriUtil.persistContentToScratchSpace(contentUri);
|
||||
if (persistedUri != null) {
|
||||
return MessagePartData.createMediaMessagePart(
|
||||
getText(),
|
||||
getContentType(),
|
||||
persistedUri,
|
||||
getWidth(),
|
||||
getHeight());
|
||||
}
|
||||
return null;
|
||||
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
final Future<?> future = executor.submit(() -> {
|
||||
MessagePartData data = null;
|
||||
final Uri contentUri = getContentUri();
|
||||
final Uri persistedUri = UriUtil.persistContentToScratchSpace(contentUri);
|
||||
if (persistedUri != null) {
|
||||
data = MessagePartData.createMediaMessagePart(
|
||||
getText(),
|
||||
getContentType(),
|
||||
persistedUri,
|
||||
getWidth(),
|
||||
getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled() {
|
||||
LogUtil.w(LogUtil.BUGLE_TAG, "Timeout while retrieving media");
|
||||
mCurrentState = STATE_FAILED;
|
||||
if (draftMessageData.isBound(bindingId)) {
|
||||
draftMessageData.removePendingAttachment(PendingAttachmentData.this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(final MessagePartData attachment) {
|
||||
final MessagePartData attachment = data;
|
||||
handler.post(() -> {
|
||||
if (attachment != null) {
|
||||
mCurrentState = STATE_LOADED;
|
||||
if (draftMessageData.isBound(bindingId)) {
|
||||
@@ -147,8 +142,18 @@ public class PendingAttachmentData extends MessagePartData {
|
||||
draftMessageData.removePendingAttachment(PendingAttachmentData.this);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
executor.schedule(() -> {
|
||||
if (!future.isDone()) {
|
||||
future.cancel(true);
|
||||
LogUtil.w(LogUtil.BUGLE_TAG, "Timeout while retrieving media");
|
||||
mCurrentState = STATE_FAILED;
|
||||
if (draftMessageData.isBound(bindingId)) {
|
||||
draftMessageData.removePendingAttachment(PendingAttachmentData.this);
|
||||
}
|
||||
}
|
||||
}.executeOnThreadPool();
|
||||
}, LOAD_MEDIA_TIME_LIMIT_MILLIS, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
protected PendingAttachmentData(final Parcel in) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* Copyright (C) 2024 The LineageOS Project
|
||||
* Copyright (C) 2024-2025 The LineageOS Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,8 @@
|
||||
*/
|
||||
package com.android.messaging.datamodel.media;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.android.messaging.Factory;
|
||||
import com.android.messaging.util.Assert;
|
||||
@@ -227,35 +228,30 @@ public class MediaResourceManager {
|
||||
if (bindableRequest != null && !bindableRequest.isBound()) {
|
||||
return; // Request is obsolete
|
||||
}
|
||||
// We don't use SafeAsyncTask here since it enforces the shared thread pool executor
|
||||
// whereas we want a dedicated thread pool executor.
|
||||
AsyncTask<Void, Void, MediaLoadingResult<T>> mediaLoadingTask = new AsyncTask<>() {
|
||||
private Exception mException;
|
||||
|
||||
@Override
|
||||
protected MediaLoadingResult<T> doInBackground(Void... params) {
|
||||
// Double check the request is still valid by the time we start processing it
|
||||
if (bindableRequest != null && !bindableRequest.isBound()) {
|
||||
return null; // Request is obsolete
|
||||
}
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
executor.execute(() -> {
|
||||
Exception exception = null;
|
||||
|
||||
MediaLoadingResult<T> tmpResult = null;
|
||||
// Double check the request is still valid by the time we start processing it
|
||||
if (bindableRequest != null && bindableRequest.isBound()) {
|
||||
try {
|
||||
return processMediaRequestInternal(mediaRequest);
|
||||
tmpResult = processMediaRequestInternal(mediaRequest);
|
||||
} catch (Exception e) {
|
||||
mException = e;
|
||||
return null;
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(final MediaLoadingResult<T> result) {
|
||||
final Exception mException = exception;
|
||||
final MediaLoadingResult<T> result = tmpResult;
|
||||
handler.post(() -> {
|
||||
if (result != null) {
|
||||
Assert.isNull(mException);
|
||||
Assert.isTrue(result.loadedResource.getRefCount() > 0);
|
||||
try {
|
||||
if (bindableRequest != null) {
|
||||
bindableRequest.onMediaResourceLoaded(
|
||||
bindableRequest, result.loadedResource, result.fromCache);
|
||||
}
|
||||
bindableRequest.onMediaResourceLoaded(
|
||||
bindableRequest, result.loadedResource, result.fromCache);
|
||||
} finally {
|
||||
result.loadedResource.release();
|
||||
result.scheduleChainedRequests();
|
||||
@@ -263,9 +259,7 @@ public class MediaResourceManager {
|
||||
} else if (mException != null) {
|
||||
LogUtil.e(LogUtil.BUGLE_TAG, "Asynchronous media loading failed, key=" +
|
||||
mediaRequest.getKey(), mException);
|
||||
if (bindableRequest != null) {
|
||||
bindableRequest.onMediaResourceLoadError(bindableRequest, mException);
|
||||
}
|
||||
bindableRequest.onMediaResourceLoadError(bindableRequest, mException);
|
||||
} else {
|
||||
Assert.isTrue(bindableRequest == null || !bindableRequest.isBound());
|
||||
if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
|
||||
@@ -273,9 +267,8 @@ public class MediaResourceManager {
|
||||
LogUtil.sanitizePII(mediaRequest.getKey()) /* key with phone# */);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
mediaLoadingTask.executeOnExecutor(executor, (Void) null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@RunsOnAnyThread
|
||||
|
||||
@@ -32,7 +32,6 @@ import com.android.messaging.datamodel.MediaScratchFileProvider;
|
||||
import com.android.messaging.datamodel.data.PersonItemData;
|
||||
import com.android.messaging.util.ContactUtil;
|
||||
import com.android.messaging.util.LogUtil;
|
||||
import com.android.messaging.util.SafeAsyncTask;
|
||||
import com.android.vcard.VCardEntry;
|
||||
import com.android.vcard.VCardEntry.EmailData;
|
||||
import com.android.vcard.VCardEntry.ImData;
|
||||
@@ -47,6 +46,7 @@ import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Holds one entry item (i.e. a single contact) within a VCard resource. It is able to take
|
||||
@@ -72,7 +72,7 @@ public class VCardResourceEntry {
|
||||
void close() {
|
||||
// If the avatar image was temporarily saved in the scratch folder, remove that.
|
||||
if (MediaScratchFileProvider.isMediaScratchSpaceUri(mAvatarUri)) {
|
||||
SafeAsyncTask.executeOnThreadPool(() ->
|
||||
Executors.newSingleThreadExecutor().execute(() ->
|
||||
Factory.get().getApplicationContext().getContentResolver().delete(
|
||||
mAvatarUri, null, null));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user