[英]Passing data from Activity to Fragment using AsyncTask - Android
我正在尝试将 ArrayList 从 MainActivity 中的 AsyncTask 传递到一个片段,但我得到了一个NullPointerException用于调用CategoryAdapter.getItemCount()
即使我在 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,但它没有被附加。 先感谢您!
我想您将数据从MainActivity
传递到HomeFragment
的方式不正确。
你期待什么
MainActivity#GetBooksAsync
onPostExecute
被调用HomeFragment
准备接收广播消息,然后更新UIMainActivity
广播到片段这里发生了什么
MainActivity#GetBooksAsync
onPostExecute
被调用MainActivity
广播消息。 没有接收者可以接收此消息!HomeFragment
准备接收广播消息,然后更新UI那你应该如何传递数据呢?
有几种方法。
像您所做的那样在 UI 组件之间广播数据。 但是您需要注意组件的生命周期。 也就是说,当您广播数据时,接收器必须已经初始化并且 UI 组件处于活动状态。
建一个 singleton class 来存放数据。 您的活动和片段将 singleton class 视为数据存储的公共位置。
如果数据大小足够小,请使用Intent和额外属性来传递数据。
使用实时数据。 我相信这是社区推荐的最现代的方式。 虽然我不确定它是如何工作的。
为了验证这是一个生命周期问题,
您可以尝试在发送广播消息之前添加延迟。
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());
}
你的 Adapter 应该这样写。
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);
}
}
}
我认为您的代码存在几个问题:
onPostExecute
方法之后发生,该方法也会将集合带到另一个处理器缓存中。 但是,当在集合从任务返回到 RAM 之前完成此操作时,它仍然是空的。 这就是所谓的竞争条件。 现在有几种方法可以解决这个问题。 最简单的一种是使用Collections.synchronizedList(categories)
这可以防止处理器缓存列表值并始终将其返回到 RAM(或使用在所有处理器/内核之间共享的 L3 缓存)。
然后我会使用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());
}
}
并注意AsyncTask
和LocalBroadcastManager
已弃用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.