如何使用 Runnable 回调替换 AsyncTask onProgressUpdate()
Posted
技术标签:
【中文标题】如何使用 Runnable 回调替换 AsyncTask onProgressUpdate()【英文标题】:How to replace AsyncTask onProgressUpdate() using Runnable callbacks 【发布时间】:2021-04-26 14:02:00 【问题描述】:我试图在不使用 Kotlin Coroutines
或其他库的情况下替换已弃用的 AsyncTask
,所以我有
MyTask
具有以下结构的对象
public abstract class MyTask<R> implements MyCallable<R>
@Override
public void setUiForLoading()
//runs on ui
@Override
public void setDataAfterLoading(R result)
//runs on ui
@Override
public R call() throws Exception
//runs in background
return null;
MyCallable
只是一个简单的界面
public interface MyCallable<R> extends Callable<R>
void setDataAfterLoading(R result);
void setUiForLoading();
并使用这个MyTaskRunner
来执行它们
public class MyTaskRunner
private final Handler handler = new Handler(Looper.getMainLooper());
private final Executor executor = Executors.newCachedThreadPool();
public <R> void executeAsync(MyCallable<R> callable)
try
callable.setUiForLoading();
executor.execute(new RunnableTask<R>(handler, callable));
catch (Exception e)
public static class RunnableTask<R> implements Runnable
private final Handler handler;
private final MyCallable<R> callable;
public RunnableTask(Handler handler, MyCallable<R> callable)
this.handler = handler;
this.callable = callable;
@Override
public void run()
try
final R result = callable.call();
handler.post(new RunnableTaskForHandler(callable, result));
catch (Exception e)
public static class RunnableTaskForHandler<R> implements Runnable
private MyCallable<R> callable;
private R result;
public RunnableTaskForHandler(MyCallable<R> callable, R result)
this.callable = callable;
this.result = result;
@Override
public void run()
callable.setDataAfterLoading(result);
它有效,但我不知道如何正确复制AsyncTask
的publishProgress()
和onProgressUpdate()
的行为,以显示实际进度而不是不确定
【问题讨论】:
我用Thread
替换了Asynctask
,但进度不确定
publishProgress
只是一种在 mainThread 上发布 Runnable
的方法。您可以使用类似的方法并在主线程处理程序上发布。即使使用AsyncTask
,您也必须计算进度。查看AsyncTask
源代码doInBackground
。
@ADM 我知道这只是一个在 mainThread 上发布的方法,但我还不清楚在调用运行期间如何执行此操作,因为其中的代码块位于后台线程和 onUpade应该在返回的主线程中运行,例如在 publishProgress(56) 中传递的 int 不会阻塞主线程。如果你能发布一个样本会很有帮助。尝试使 AsyncTask 代码实现本身适应我的代码看起来过于复杂。
【参考方案1】:
我无法提供与您相同的代码,但希望您能理解。 一切都在代码中自我解释。
import android.app.*;
import android.graphics.*;
import android.os.*;
import android.widget.*;
import java.lang.ref.*;
public class MainActivity extends Activity
private static final class HeavyJob implements Runnable
private final WeakReference<Handler> handler;
private final Thread thread;
private boolean isAlive;
private boolean state;
private int progress;
public final HeavyJob(final Handler handler)
this.handler = new WeakReference<Handler>(handler);
thread = new Thread(this);
isAlive = true;
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
@Override
public final void run()
while(isAlive)
try
synchronized(this)
while(!state) this.wait();
Thread.sleep(200L); //Let say this a heavy job which takes 200 m/s each round.
progress += 10;
final Handler hanRef = handler.get();
if(hanRef == null)
isAlive = false;
handler.clear();
break;
final Message msg = Message.obtain();
msg.what = 0;
msg.arg1 = progress;
hanRef.sendMessageAtTime(msg, SystemClock.uptimeMillis()); //Update its progress each round.
catch(final InterruptedException e)
//Finished ???
final Handler hanRef = handler.get();
if(hanRef != null)
final Message msg = Message.obtain();
msg.what = 1;
msg.arg1 = progress; //Make your progress is 100% completed and updated.
//msg.obj = bitmap;
hanRef.sendMessageAtTime(msg, SystemClock.uptimeMillis());
public final synchronized void resume()
if(isAlive)
state = true;
this.notify();
public final void suspend()
state = false;
thread.interrupt();
public final void stop()
isAlive = false; // In case interrupt() does nothing (Thread was not in sleep nor wait mode).
thread.interrupt();
handler.clear();
private static final class UIHandler extends Handler
private final WeakReference<MainActivity> activity;
public final UIHandler(final MainActivity activity)
super(Looper.getMainLooper());
this.activity = new WeakReference<MainActivity>(activity);
@Override
public final void handleMessage(final Message msg)
final MainActivity referent = activity.get();
if(referent != null)
switch(msg.what)
case 0: referent.onProgress(msg.arg1); break;
case 1: referent.onPostExecute(msg.arg1, (Bitmap)msg.obj); break;
private ProgressBar pb;
private ImageView iv;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
pb = findViewById(R.id.pb);
iv = findViewById(R.id.next);
UIHandler handler = new UIHandler(this);
//Initilize the object but will not run yet.
HeavyJob hj = new HeavyJob(handler);
//Run the job
hj.resume();
//Pause the job
hj.suspend();
//Resume the job
hj.resume();
//Stop the job
hj.stop();
//Multiple jobs
for(int i=0; i<10; i++)
new HeavyJob(handler);
public final void onProgress(final int progress)
pb.setProgress(progress);
public final void onPostExecute(final int progress, Bitmap bitmap)
pb.setProgress(progress);
if(bitmap != null) iv.setImageBitmap(bitmap);
【讨论】:
【参考方案2】:我创立的最佳做法是:
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends AppCompatActivity
Button btn_start;
TextView text;
ProgressBar progressBar1, progressBar2;
int num = 0;
ExecutorService service;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = findViewById(R.id.textHello);
btn_start = findViewById(R.id.btn_start);
progressBar1 = findViewById(R.id.progressbar1);
progressBar2 = findViewById(R.id.progressBar2);
btn_start.setOnClickListener(v -> toDo());
private void toDo()
service = Executors.newSingleThreadExecutor();
service.execute(() ->
runOnUiThread(() ->
// onPreExecute method of AsyncTask
progressBar1.setVisibility(View.VISIBLE);
progressBar2.setVisibility(View.VISIBLE);
);
// doInBackground of AsyncTask
for (int i = 1; i <= 10000; i++)
num = i;
runOnUiThread(() ->
// onProgressUpdate method of AsyncTask
progressUpdate(num);
);
runOnUiThread(() ->
// onPostExecute method of AsyncTask
progressBar1.setVisibility(View.GONE);
progressBar2.setVisibility(View.GONE);
);
);
public void progressUpdate(Integer i)
text.setText(String.valueOf(i));
progressBar2.setProgress(i);
【讨论】:
以上是关于如何使用 Runnable 回调替换 AsyncTask onProgressUpdate()的主要内容,如果未能解决你的问题,请参考以下文章