如何使用 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);
        
    

它有效,但我不知道如何正确复制AsyncTaskpublishProgress()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()的主要内容,如果未能解决你的问题,请参考以下文章

使用 Runnable 作为回调/子程序的坏习惯?

来自 Java Runnable 的 Py4J 回调

有没有办法将参数传递给Runnable? [复制]

如何使用android延迟不扩展或实现Thread或Runnable

如何安全地停止我的“类实现 Runnable”?

如何将正则表达式参数传递给 PHP 中的 preg 替换回调?