Android Fragments 使用AsyncTask将数据从Activity传递到Fragment- Android

ds97pgxw  于 2022-11-24  发布在  Android
关注(0)|答案(4)|浏览(186)

我尝试将一个ArrayList从MainActivity中的AsyncTask传递到一个片段,但是我得到了一个调用CategoryAdapter.getItemCount()NullPointerException,即使我在BroadCastReceiver调用之后传递数组也是如此。
我做错了什么?

主要活动

class GetBooksAsync extends AsyncTask<Void, Void, Void> {
        LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getApplicationContext());
    @Override
    protected Void doInBackground(Void... voids) {
        for (ECategories category : ECategories.values()) {
            try {
                categories.add(new Category(category.toString(), apiClient.getBooks(category)));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        Intent intent = new Intent("com.android.mainapp");
        intent.putExtra("categories", categories);
        manager.sendBroadcast(intent);
        replaceFragment(new HomeFragment());
    }
}

家庭片段

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    initBroadCastReceiver();
    categoryAdapter = new CategoryAdapter(categories,getContext());
    View view = inflater.inflate(R.layout.fragment_home, container, false);
    recyclerView = view.findViewById(R.id.parent_rv);
    recyclerView.setAdapter(categoryAdapter);
    recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
    categoryAdapter.notifyDataSetChanged();
    return view;
}

private void initBroadCastReceiver() {
    manager = LocalBroadcastManager.getInstance(getContext());
    MyBroadCastReceiver receiver = new MyBroadCastReceiver();
    IntentFilter filter = new IntentFilter();
    filter.addAction("com.android.mainapp");
    manager.registerReceiver(receiver,filter);
}

class MyBroadCastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        //get the categories from the intent
        categories = new ArrayList<Category>();
        categories = (ArrayList<Category>) intent.getSerializableExtra("categories");
    }
}

我也尝试过从OnReceive方法附加recyclerView,但是没有附加。提前感谢!

ljo96ir5

ljo96ir51#

我认为你的代码有几个问题:
1.您的任务正在UIThread以外的线程中运行(调度任务并处理结果)。这意味着它很可能在不同的处理器/内核上运行。(例如您的集合)缓存在处理器中,并且在执行后的某个时间将数据写回RAM。但这可能发生在调用onPostExecute方法之后,这也会把这个集合带到另一个处理器缓存中。但是当这个过程在这个集合从任务返回到RAM之前完成时,它仍然是空的。这被称为竞态条件
现在有几种方法可以解决这个问题。最简单的方法是使用Collections.synchronizedList(categories)。这可以防止处理器缓存列表值,并始终将其返回到RAM(或使用所有处理器/内核共享的三级缓存)。
1.我不确定你传递给集合的确切内容。Intent(以及它的数据)需要是可序列化的,而你添加到集合中的内容可能是不可序列化的。
然后使用AsyncTask参数:

class GetBooksAsync extends AsyncTask<ECategories, Void, Collection<Category>> {
        LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getApplicationContext());
    @Override
    protected Void doInBackground(ECategories... eCategories) {
        Collection<Category> categories = [whatever you want to use];
        for (ECategories category : eCategories) {
            try {
                categories.add(new Category(category.toString(), apiClient.getBooks(category)));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return categories;
    }
    @Override
    protected void onPostExecute(Collection<Category> categories) {
        super.onPostExecute(categories);
        Intent intent = new Intent("com.android.mainapp");
        intent.putExtra("categories", categories);
        manager.sendBroadcast(intent);
        replaceFragment(new HomeFragment());
    }
}

请注意,不建议使用AsyncTaskLocalBroadcastManager

kkbh8khc

kkbh8khc2#

  • 类别是否序列化?
  • 可以使用BroadcastReceiver作为内部类,然后在Adpater接收到数据时更新它的数据,因为代码运行速度非常快,不需要注册进行监控,就会立即处理。
yr9zkbsy

yr9zkbsy3#

我猜您将数据从MainActivity传递到HomeFragment的方式不正确。

您的期望

1.呼叫MainActivity#GetBooksAsync
1.等待onPostExecute被调用

  1. HomeFragment准备好接收广播消息,然后更新UI
    1.将消息从MainActivity广播到片段
    "这里发生了什么"
    1.呼叫MainActivity#GetBooksAsync
    1.等待onPostExecute被调用
    1.从MainActivity广播消息。没有接收者接收此消息!
  2. HomeFragment准备好接收广播消息,然后更新UI

那么您应该如何传递数据?

有几种办法。
1.在UI组件之间广播数据,就像你做的那样。但是你需要注意组件的生命周期。也就是说,当你广播数据时,接收器必须已经初始化并且UI组件处于活动状态。
1.构建一个单例类来存储数据。您的Activity和片段将单例类视为数据存储的公共位置。
1.如果数据大小足够小,请使用Intentextra属性传递数据。
1.使用LiveData。我相信这是社区推荐的最现代的方法。虽然我不知道它是如何工作的。
为了验证这是一个生命周期问题,
您可以尝试在发送广播留言之前添加延迟。

class GetBooksAsync extends AsyncTask<Void, Void, Void> {
...
    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        Intent intent = new Intent("com.android.mainapp");
        intent.putExtra("categories", categories);
        
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                manager.sendBroadcast(intent);
            }
        };

        Timer timer = new Timer();
        timer.schedule(task, 5 * 1000); // Delay the broadcast after 5 seconds

        
        replaceFragment(new HomeFragment());
    }
x4shl7ld

x4shl7ld4#

您的适配器应该这样编写。

class CategoryAdapter extends RecyclerView.Adapter<CategoryAdapter.VHolder>{
        
        private ArrayList<Category> list = new ArrayList<Category>();

        public void setList(ArrayList<Category> list) {
            this.list = list;
            notifyDataSetChanged();
        }

        public CategoryAdapter(Context context) {
            // Do not pass a list in the constructor, because the list may be empty
        }
         
        class VHolder extends RecyclerView.ViewHolder {

            public VHolder(@NonNull View itemView) {
                super(itemView);
            }
        }
        
        ......
}

您的片段应具有BroadcastReceiver的全局适配器以更新数据

public class Test extends Fragment {

    // Create a global Adapter for BroadcastReceiver to call and update data
    private CategoryAdapter adapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        adapter = new CategoryAdapter(getContext());
        initBroadCastReceiver();
        View view = inflater.inflate(R.layout.fragment_home, container, false);
        recyclerView = view.findViewById(R.id.parent_rv);
        recyclerView.setAdapter(categoryAdapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        return view;
    }

    private void initBroadCastReceiver() {
        manager = LocalBroadcastManager.getInstance(getContext());
        MyBroadCastReceiver receiver = new MyBroadCastReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.android.mainapp");
        manager.registerReceiver(receiver,filter);
    }

    class MyBroadCastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            //get the categories from the intent
            ArrayList<Category> categories = (ArrayList<Category>) intent.getSerializableExtra("categories");
            adapter.setList(categories);
        }
    }
}

相关问题