AsyncTask会阻塞UI线程并延迟显示进度条

5

我的AsyncTask在下载图像时会阻塞按钮元素,进度对话框会有延迟——在图像显示之前会短暂地显示,但是下载需要很长时间,按钮被阻止(变成橙色),对话框不会显示。

 public  Bitmap download(String url, ProgressBar progressbar) throws InterruptedException, ExecutionException {
     BitmapDownloaderTask task = new BitmapDownloaderTask(progressbar);
     task.execute(url);
     return task.get();
}

class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {



    public BitmapDownloaderTask(ProgressBar progressbar) {

    }
    @Override
    protected void onPreExecute() {
        dialog = new ProgressDialog(ShowActivity.this);
        dialog.setMessage("Loading");
        dialog.setIndeterminate(true);
        dialog.setCancelable(false);
        dialog.show();
    }

    @Override
    protected Bitmap doInBackground(String... Params) {
        return imageLoader.getBitmap(params[0]);

    }
    @Override
    protected void onPostExecute(Bitmap bitmap) {
         dialog.dismiss();


    }
}    

在按钮监听器中,只需调用下载函数,进度参数是因为我在imageview中有进度条圆圈 - 对话框仅用于测试,以找出为什么会有延迟和阻塞。在另一个应用程序中,我使用runable和thread,元素没有被阻塞,但在教程中提到AsyncTask作为更好的解决方案。


这是正确的做法,请查看此链接:http://www.android-ios-tutorials.com/182/show-progressbar-while-downloading-image-using-asynctask-in-android/ - Houcine
3个回答

13
图像下载确实在后台线程中执行,但是使用 return task.get(); 将阻塞您的主线程,直到它完成。
您应该使用 onPostExecute() 作为任务完成时的回调,不仅用于关闭对话框,还要处理由 doInBackground() 返回的位图。

2
因为您正在调用 AsyncTask#get 方法:

等待必要的时间以完成计算,然后检索其结果。

您应该在 onPostExecute() 方法中执行需要使用图像的任何操作。

从Android文档中直接引用的那个描述可能需要更清晰地说明,“等待”是指在非常技术化的意义上,即“调用线程中无法发生任何事情,例如UI更新”。 - John Perry

0

虽然这是一个老问题,但我也遇到过这个问题。 是的,AsyncTask.get会阻塞UI线程,直到结果到达,不给处理空间让您的对话框显示,我面临着同样的问题,通过更通用的解决方案来解决。基本上,我使用构造函数创建我的AsyncTasks,该构造函数接收一个AsyncTaskData(AsyncTaskData是我创建的一个类,它在下面),其中包含所有必要的数据和回调。我留下了我的类和一个示例。

AsyncTaskData.java

public class AsyncTaskData {

    private Context activityContext;
    private Object inputData; // whatever you want
    private AsyncTaskCallback callback;

    public AsyncTaskData(Context activityContext, Object inputData, AsyncTaskCallback callback) {
        this.activityContext = activityContext;
        this.inputData = inputData;
        this.callback = callback;
    }

    // getters and setters
}

AsyncTaskCallback.java

public interface AsyncTaskCallback {
    // method to be called when the task is finished (onPostExecute)
    void onAsyncTaskTerminated(Object result);
}

例如,如果我有一个用于登录的AsyncTask,它将被声明如下:

public class LoginAsyncTask extends AsyncTask<Void, Void, Boolean> {

    private AsyncTaskData asyncTaskData;
    private ProgressDialog progressDialog;

    public LoginAsyncTask (AsyncTaskData asyncTaskData) {
        this.asyncTaskData = asyncTaskData;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();

        progressDialog = new ProgressDialog(asyncTaskData.getActivityContext(), R.style.AppTheme);
        progressDialog.show();
    }

    @Override
    protected Boolean doInBackground(Void... voids) {
        // if needed, get your data with asyncTaskData.getInputData()
        AuthenticationData authData = (AuthenticationData) asyncTaskData.getInputData();
        boolean loggedIn = // your heavy authentication process goes here
        return loggedIn;
    }

    @Override
    protected void onPostExecute(Boolean loggedIn) {
        super.onPostExecute(searchResponse);

        progressDialog.dismiss();

        if(asyncTaskData.getCallback() == null) return;

        asyncTaskData.getCallback().onAsyncTaskTerminated(loggedIn);
    }
}

最后,在您的Fragment/Activity中使用它,只需执行以下操作:

AsyncTaskData asyncTaskData = new AsyncTaskData(
    getActivity(), // or this if you are calling from an Activity
    new AuthenticationData("username", "password"), 
    result -> {
        boolean loggedIn = (boolean) result;
        // your login logic goeas here
    });

new SearchAsyncTask(asyncTaskData)
    .execute();

经过多次尝试,我最终选择了这种方法,因为它是通用的,可以保持代码的清洁,并解决了调用.get()方法和阻塞UI线程的问题。希望能对你有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接