为什么AsyncTask在主线程上运行?

10

我试图使用 AsyncTask 在后台执行任务,当完成时再呈现它,但问题是直到任务完成之前页面上什么也没有展示。

我还尝试使用 execute 和 doInBackground 来调用它,但两者都会导致相同的问题,并且我必须等待活动启动并准备就绪,而不是显示一个带有加载进度条的页面,然后稍后添加列表。

代码:

private class listTask extends AsyncTask<Void,Void,Void> {
    @Override
    protected Void doInBackground(Void... voids) {
        recyclerview.setAdapter(new ItemAdapter(getInternalFileList()));
        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        progressbar.setVisibility(View.GONE);
    }

    List<GalleryItem> getInternalFileList(){
        String path = getActivity().getFilesDir().toString();
        File directory = new File(path);
        File[] files = directory.listFiles();
        List<GalleryItem> galleryItems = new ArrayList<>();

        MainDBRepository repo = new MainDBRepository(getActivity());
        HashMap<String,GalleryItem> itemsMap = repo.getItemsMap();

        for(File file : files) {
            if(itemsMap.containsKey(file.getName()))
                galleryItems.add(itemsMap.get(file.getName()));
        }

        Collections.reverse(galleryItems);

        return galleryItems;
    }

    private class ItemHolder extends RecyclerView.ViewHolder {
        ImageView mItemImageView;

        ItemHolder(View itemView) {
            super(itemView);
            mItemImageView = (ImageView) itemView.findViewById(R.id.image_view);
        }

        public void bindBackgroundImage(Bitmap backgroundImage){
            mItemImageView.setImageBitmap(backgroundImage);
        }

        public void bindImageViewer(final String path){
            mItemImageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = ImageViewerActivity.newIntent(getActivity(), path);
                    startActivity(intent);
                }
            });
        }
    }

    private class ItemAdapter extends RecyclerView.Adapter<ItemHolder> {
        private List<GalleryItem> galleryItems;

        ItemAdapter(List<GalleryItem> galleryItems) {
            galleryItems = galleryItems;
        }

        @NonNull
        @Override
        public ItemHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
            LayoutInflater inflater = LayoutInflater.from(getActivity());
            View v = inflater.inflate(R.layout.gallery_item, viewGroup, false);
            return new ItemHolder(v);
        }

        @Override
        public void onBindViewHolder(@NonNull ItemHolder itemHolder, int position) {
            GalleryItem galleryItem = galleryitems.get(position);
            String path = getActivity().getFilesDir() + "/" + galleryItem.getID();
            File file = new File(path);
            Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
            itemHolder.bindBackgroundImage(bitmap);
            itemHolder.bindImageViewer(path);
        }

        @Override
        public int getItemCount() {
            return galleryitems.size();
        }
    }
}

3
如果您想在AsyncTask正在执行的同时显示某些内容,您应该实现onProgressUpdate(Progress ... values)方法。该方法从publishProgress(Progress ... values)调用,并实现了后台任务进度下的逻辑。 - simo-r
2
第一次调用没问题,但第二次是被禁止的。绝对不要这样做! - greenapps
我希望在asynctask正在处理的同时,能够显示带有进度条的fragment。目前的情况是应用程序会在整个fragment完成之前(包括所有的asynctask)冻结。 - newbieandroid
(ItemAdapter(getInternalFileList())?这是一个非常奇怪的实例化方式。我会期望你使用类似于(new ItemAdapter(getInternalFileList())的形式。 - greenapps
1
你最好只在doInBackground()中获取文件列表,并在onPostExecute()中创建和设置适配器。 - greenapps
显示剩余9条评论
2个回答

9
如果您想在UI中展示一些内容,而AsyncTask正在计算后台任务,那么您应该实现onProgressUpdate(Progress... values)方法,该方法从publishProgress(Progress... values)调用并在后台任务进度下执行逻辑。在您的代码中,只需在onPostExecute()中显示最终结果即可。请注意,AsyncTask在与UI线程不同的线程中运行doInBackground()方法,而onProgressUpdate()onPostExecute()在UI线程上运行,因为它们应该更新UI。有关更多信息,请参阅Android文档关于AsyncTask

谢谢您的回答,我不确定我是否理解正确,但我的意思是,异步任务完成之前片段本身不会加载,片段默认具有进度条,我希望在异步任务完成之前首先显示它,但发生的情况是屏幕冻结,我必须等待片段出现并加载所有内容。使用异步任务不应该让我在它完成之前加载片段吗? - newbieandroid
3
片段有它们自己的生命周期,所以你只需要实现它们的“onSomething()”方法以正确加载UI部分。当您需要在后台执行耗时操作而避免UI冻结(不响应用户输入)时,可以使用AsyncTask。我认为在您的问题中使用AsyncTask并不是正确的方式。 - simo-r
我想我对Asynctask的理解有误,会再次阅读文档以寻找问题,谢谢。 - newbieandroid
好的,继续。八! - greenapps
有人能解释一下为什么这个与主题无关的回答会得到这么多赞吗? - greenapps

5

在这里,“后台”指的是非UI线程。如果想要在任务完成前获取结果,需要在后台执行任务的同时将结果传递到UI线程。如果想要在UI线程上获取结果,可以使用AsyncTask.onProgressUpdate(..),或者更好地使用Kotlin协程代替Asynctask。


谢谢您的回答,我不确定我是否理解正确,但我的意思是,异步任务完成之前片段本身不会加载,片段默认具有进度条,我希望在异步任务完成之前首先显示它,但发生的情况是屏幕冻结,我必须等待片段出现并加载所有内容。使用异步任务不应该让我在它完成之前加载片段吗? - newbieandroid

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