Home > Software design >  Passing data from Activity to Fragment using AsyncTask - Android
Passing data from Activity to Fragment using AsyncTask - Android

Time:11-22

I'm trying to pass an ArrayList from an AsyncTask in the MainActivity to a fragment, but I'm getting a NullPointerException for invoking CategoryAdapter.getItemCount() even if I'm passing the array after the BroadCastReceiver Invoke.

What Am I doing wrong?

MainActivity

 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());
    }
}

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");
    }
}

i've also tried attaching the recyclerView from the OnReceive Method, but it's not getting attached. Thank you in advance!

CodePudding user response:

  • Is Category serialized?
  • You can use BroadcastReceiver as an internal class, and then update the data of Adpater when it receives the data, because the code runs very fast, and it is not necessary to register for monitoring, and it will be processed immediately.

CodePudding user response:

I guess the way you pass the data from MainActivity to HomeFragment is incorrect.

WHAT YOU EXPECT

  1. Call MainActivity#GetBooksAsync
  2. Wait till onPostExecute has been called
  3. HomeFragment is ready to receive the broadcast message, then update UI
  4. Broadcast the message from MainActivity to the fragment

WHAT IS HAPPENING HERE

  1. Call MainActivity#GetBooksAsync
  2. Wait till onPostExecute has been called
  3. Broadcast the message from MainActivity. There is no receiver to receive this message!
  4. HomeFragment is ready to receive the broadcast message, then update UI

HOW SHALL YOU PASS THE DATA THEN?

There are several way.

  1. Broadcast data between the UI component like the things you did. But you will need to beaware the life cycle of the components. That is, when you broadcast the data, the receiver must already init and the UI component is in active.

  2. Build a singleton class to store the data. Your activity and fragment treats the singleton class as a common place for the data storage.

  3. Use Intent and the extra property to pass the data IF the data size is small enough.

  4. Use LiveData. I believe it is the most modern way recommended by the community. Though I am not sure how its work.


To verify the fact that it is an life cycle issue,

you can try to add a delay before you sending the broadcast message.

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());
    }



CodePudding user response:

Your Adapter should be written like this.

    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);
            }
        }
        
        ......
}

Your fragment should have a global Adapter for BroadcastReceiver to update data

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);
        }
    }
}
  • Related