java - 需要序列化的 EnqueueWork 导致 AsyncTask 崩溃
问题描述
这个项目背后的想法是在将数据库上传到 web 服务时运行进度条
我有一个 AsyncTask 做一些工作,然后调用一个服务并等待它完成,然后再调用 onPostExecute()
private class UploadDatabaseAsyncTask extends AsyncTask<Void, Long, Void> {
private AlertDialog dialog;
private Activity activity;
private TextView messageView;
private String location;
private long maxSize;
private ProgressBar progressBar;
UploadDatabaseAsyncTask(Activity activity, long maxSize, String location) {
super();
this.location = location;
this.maxSize = maxSize;
this.activity = activity;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
progressBar = new ProgressBar(activity);
progressBar.setMax(100);
progressBar.setProgress(0);
dialog = new AlertDialog.Builder(activity).setTitle("Uploading").setView(progressBar).create();
messageView = dialog.findViewById(android.R.id.message);
// dialog.show();
if (activity != null && activity.getView() != null)
{
activity.getView().setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
activity.getView().setBackgroundColor(ContextCompat.getColor(App.getAppContext(), R.color.grey));
}
}
@Override
protected void onProgressUpdate(Long... values) {
messageView.setText(values[0] + " / " + maxSize);
BigDecimal current = new BigDecimal(values[0]);
BigDecimal max = new BigDecimal(maxSize);
BigDecimal progress = current.divide(max, 3, RoundingMode.UP);
progressBar.setProgress(Integer.valueOf(progress.multiply(new BigDecimal(100)).toString()));
}
@Override
protected Void doInBackground(Void... voids) {
Intent intent = new Intent();
final Object lock = new Object();
final boolean[] active = new boolean[]{true};
DatabaseSyncService.SyncProgressCallback callback = new DatabaseSyncService.SyncProgressCallback() {
@Override
public void onProgress(long progress) {
onProgressUpdate(progress);
}
@Override
public void onFinish() {
active[0] = false;
lock.notify();
}
};
intent.putExtra(DATABASE_ID, callback);
intent.putExtra("LOCATION", location);
DatabaseSyncService.enqueueWork(activity, intent);
synchronized (lock)
{
while (active[0])
{
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return null;
}
@Override
protected void onCancelled(Void aVoid) {
if (activity != null && activity.getView() != null)
{
activity.getView().setOnTouchListener(null);
activity.getView().setBackgroundColor(ContextCompat.getColor(App.getAppContext(), R.color.transparent));
}
}
@Override
protected void onCancelled() {
if (activity != null && activity.getView() != null)
{
activity.getView().setOnTouchListener(null);
activity.getView().setBackgroundColor(ContextCompat.getColor(App.getAppContext(), R.color.transparent));
}
}
@Override
protected void onPostExecute(Void aVoid) {
if (activity != null && activity.getView() != null)
{
activity.getView().setOnTouchListener(null);
activity.getView().setBackgroundColor(ContextCompat.getColor(App.getAppContext(), R.color.transparent))
}
AlertDialog.Builder builder = new AlertDialog.Builder(App.getAppContext());
builder.setTitle("Upload Complete");
builder.setMessage("You upload was completed!");
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) { }
});
builder.show();
}
}
我在尝试序列化 AsyncTask 时遇到了 DatabaseSyncService.enqueueWork() 方法的问题,该方法不起作用。知道它为什么要这样做吗?
堆栈跟踪
E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #4
Process: com.app.app, PID: 15935
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:353)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
at java.util.concurrent.FutureTask.run(FutureTask.java:271)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = com.app.app.api.SettingApi$UploadDatabaseAsyncTask$2)
at android.os.Parcel.writeSerializable(Parcel.java:1786)
at android.os.Parcel.writeValue(Parcel.java:1734)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:801)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1506)
at android.os.Bundle.writeToParcel(Bundle.java:1181)
at android.os.Parcel.writeBundle(Parcel.java:841)
at android.content.Intent.writeToParcel(Intent.java:10199)
at android.app.job.JobWorkItem.writeToParcel(JobWorkItem.java:117)
at android.app.job.IJobScheduler$Stub$Proxy.enqueue(IJobScheduler.java:205)
at android.app.JobSchedulerImpl.enqueue(JobSchedulerImpl.java:53)
at androidx.core.app.JobIntentService$JobWorkEnqueuer.enqueueWork(JobIntentService.java:343)
at androidx.core.app.JobIntentService.enqueueWork(JobIntentService.java:523)
at androidx.core.app.JobIntentService.enqueueWork(JobIntentService.java:501)
at com.ascsoftware.ascora.sync.DatabaseSyncService.enqueueWork(DatabaseSyncService.java:37)
at com.ascsoftware.ascora.api.SettingApi$UploadDatabaseAsyncTask.doInBackground(SettingApi.java:291)
at com.ascsoftware.ascora.api.SettingApi$UploadDatabaseAsyncTask.doInBackground(SettingApi.java:212)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.io.NotSerializableException: com.app.app.api.SettingApi$UploadDatabaseAsyncTask
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1233)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1597)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1558)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1481)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1227)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)
at android.os.Parcel.writeSerializable(Parcel.java:1781)
at android.os.Parcel.writeValue(Parcel.java:1734)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:801)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1506)
at android.os.Bundle.writeToParcel(Bundle.java:1181)
at android.os.Parcel.writeBundle(Parcel.java:841)
at android.content.Intent.writeToParcel(Intent.java:10199)
at android.app.job.JobWorkItem.writeToParcel(JobWorkItem.java:117)
at android.app.job.IJobScheduler$Stub$Proxy.enqueue(IJobScheduler.java:205)
at android.app.JobSchedulerImpl.enqueue(JobSchedulerImpl.java:53)
at androidx.core.app.JobIntentService$JobWorkEnqueuer.enqueueWork(JobIntentService.java:343)
at androidx.core.app.JobIntentService.enqueueWork(JobIntentService.java:523)
at androidx.core.app.JobIntentService.enqueueWork(JobIntentService.java:501)
at com.app.app.sync.DatabaseSyncService.enqueueWork(DatabaseSyncService.java:37)
at com.app.app.api.SettingApi$UploadDatabaseAsyncTask.doInBackground(SettingApi.java:291)
at com.app.app.api.SettingApi$UploadDatabaseAsyncTask.doInBackground(SettingApi.java:212)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
(HTTPLog)-Static: isSBSettingEnabled false
这是回调
public interface SyncProgressCallback extends Serializable {
void onProgress(long progress);
void onFinish();
}
解决方案
此行不起作用:
intent.putExtra(DATABASE_ID, callback);
要将对象放入 中Intent
,该对象必须实现Serializable
/Parcelable
接口。此外,相同的要求适用于对象的所有字段。
尽管您的callback
对象(属于 type DatabaseSyncService.SyncProgressCallback
)实现Serializable
了,但它是从内部类实例化的callback = new DatabaseSyncService.SyncProgressCallback() {...}
。据此,在您的情况java.io.NotSerializableException
下发生,因为:
- 序列化这样的内部类实例也将导致其关联的外部类实例的序列化
- 强烈建议不要序列化内部类(即不是静态成员类的嵌套类),包括本地和匿名类
这意味着您必须使您的外部类UploadDatabaseAsyncTask
实现Serializable
,而这些实现又必须应用于其所有字段,依此类推。
这很令人沮丧。
根据经验,请考虑将一些轻量级/纯数据,而不是具有复杂依赖关系的复杂对象放入Intent
.
回到您的代码,一个简单的解决方法可能是将callback
对象传递给DatabaseSyncService.enqueueWork
方法:
//intent.putExtra(DATABASE_ID, callback);
intent.putExtra("LOCATION", location);
DatabaseSyncService.enqueueWork(activity, intent, callback);
内部直接enqueueWork()
使用,而不是从意图反序列化。callback
推荐阅读
- asp.net - 我尝试了所有答案来解决这个“解析器错误消息:无法加载类型'Inventory123.WebApiApplication'”。但没有成功
- javascript - 不一致的 Javascript 元素控件
- haskell - 操纵 Haskell Monad 状态
- asp.net-core - 如何解决 ASP.NET Core 中的 Redis Multiplexer 问题
- ios - 重新启动应用程序时,为什么我的 CoreData 更新没有持续存在?
- r - 如何删除R中列联表中的一行?
- android - 运行此 AVD 需要 Intel HAXM
- bash - 如何使用基于时间的一次性密码以非交互方式提供 ssh?
- javascript - Highcharts Treemap,向上钻取事件
- sql-server - 如何在 SQL Server 中使用存储过程创建动态触发器