在任务期间显示进度对话框而不阻塞Android中的UI线程

Posted

技术标签:

【中文标题】在任务期间显示进度对话框而不阻塞Android中的UI线程【英文标题】:Show progress dialog during task without blocking UI thread in Android 【发布时间】:2021-09-21 18:17:40 【问题描述】:
@Override
protected void onCreate (Bundle savedInstanceState) 
    
    progressDialog.show();

    if (/* task that returns a boolean value */) 
        // Do stuff        
    
    else 
        // Do other stuff     
    

    progressDialog.dismiss();


这段代码应该显示进度对话框,等待任务产生结果,然后评估if 语句并关闭对话框。但这不会发生:UI 线程被阻塞,任务被执行,然后才显示进度对话框,然后立即关闭。

解决这个问题的正确方法是什么?

【问题讨论】:

【参考方案1】:

一个简单的worker thread。

public void onClick(View v) 
    new Thread(new Runnable() 
        public void run() 
            // a potentially time consuming task
        
    ).start();

根据answer 中提到的您的要求,可以考虑其他替代方案。

【讨论】:

似乎我必须将整个东西包装在其中一个中以将其与 UI 线程分开。谢谢! 你不应该直接使用线程,使用为后台任务设计的各种实用程序。官方文档推荐 Coroutines for Kotlin 和 Java Concurrent utility for Java。还有一些用于后台处理的库。【参考方案2】:

您可以为此目的使用 AsyncTask,但是,它在 Sdk 30 中已被弃用,建议直接使用 java.concurrent.* 实用程序docs。以下是使用 ExecutorService 的一种变通方法,虽然并不完美,但绝对满足您的功能:

在您的 Activity(例如 MyActivity)中,创建 ExecutorService 的成员并对其进行初始化。添加方法和回调如下,当你想执行一些后台任务时,只需调用它:

public class MyActivity extends AppCompatActivity 
    // You can use your preferred executor
    private final ExecutorService executor = new ThreadPoolExecutor(0, 1,
            3L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>());

    @Override
    protected void onCreate (Bundle savedInstanceState) 
        // Initiate the task
        executeParallel(new Callable<Boolean> 
            @Override
            public Boolean call() 
                // Perform your task and return boolean
                return trueOrFalse;
            
        , new Callback<Boolean>() 
            @Override
            public void onStart() 
                // Show progress dialog
                progressDialog.show();
            

            @Override
            public void onComplete(Boolean result) 
                if (result) 
                    // Do some tasks
                 else 
                    // Do other tasks
                
                // Remove dialog
                progressDialog.dismiss();
            
        , new Handler(Looper.getMainLooper()));
    

    public <R> void executeParallel(@NonNull Callable<R> callable, @Nullable Callback<R> callback, Handler handler) 
        executor.execute(() -> 
            handler.post(() -> 
                if (callback != null) 
                    callback.onStart();
                
            );
            R r = null;
            try 
                r = callable.call();
             catch (Exception e) 
                // Ignore
             finally 
                R result = r;
                handler.post(() -> 
                    if (callback != null) 
                        callback.onComplete(result);
                    
                );
            
        );
    

    public interface Callback<R> 
        void onStart();
        void onComplete(R result);
    

完成后,只需关闭 ExecutorService。如果您将其用于更好的设计,则可以将此 Executor 和相关方法移动到 ViewModel 中。请记住,您不应该直接使用 Thread 以避免潜在的内存泄漏。

【讨论】:

会发生什么样的内存泄漏? 起初,你不能从后台线程更新你的 UI 元素,为了解决这个问题,有线程之间交互的 Handler。其次,如果您运行一个线程来执行长时间运行的任务并且发生配置更改,您的活动会被破坏,但线程会保持活动状态,使用系统资源并可能导致意外行为,除非您正确处理它们。

以上是关于在任务期间显示进度对话框而不阻塞Android中的UI线程的主要内容,如果未能解决你的问题,请参考以下文章

异步任务进度对话框需要很长时间才能完成 android 中的简单任务

Android学习笔记——显示运行进度对话框

Android - 仅在 AsyncTask 未完成时单击按钮时显示进度对话框

非阻塞模式 Swing 进度对话框

如果异步任务被解除,则重新显示进度对话框

android中AsyncTask中的ProgressDialog