Issue
The idea behind this project is to have a Progress bar running while uploading a Database to a webService
I have an AsyncTask that does some work, then calls a service and awaits for it to finish before calling 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();
}
}
I'm running into an issue with the DatabaseSyncService.enqueueWork() method trying to serialize the AsyncTask, which won't work. Any idea why it is trying to do this?
StackTrace
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
Here is the Callback
public interface SyncProgressCallback extends Serializable {
void onProgress(long progress);
void onFinish();
}
Solution
This line does not work:
intent.putExtra(DATABASE_ID, callback);
To put an object into an Intent
, the object must implement Serializable
/Parcelable
interface. Moreover, the same requirement applies to all the fields of the object.
Though your callback
object (which belongs to type DatabaseSyncService.SyncProgressCallback
) implements Serializable
, but it is instanced from an inner class callback = new DatabaseSyncService.SyncProgressCallback() {...}
. According to this, java.io.NotSerializableException
occurs in your case because:
- serializing such an inner class instance will result in serialization of its associated outer class instance as well
- Serialization of inner classes (i.e., nested classes that are not static member classes), including local and anonymous classes, is strongly discouraged
This means that you have to make your outer class UploadDatabaseAsyncTask
implements Serializable
which in turns must applies to all of its fields, and so on.
That's quite frustrating.
As a rule of thumb, please consider put something lightweight / pure data, not complex objects with complex dependencies, to an Intent
.
Back to your code, one easy fix could be passing the callback
object to the DatabaseSyncService.enqueueWork
method:
//intent.putExtra(DATABASE_ID, callback);
intent.putExtra("LOCATION", location);
DatabaseSyncService.enqueueWork(activity, intent, callback);
Inside enqueueWork()
use callback
directly instead of deserializing from the intent.
Answered By - Nghia Bui
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.