Home > database >  Update recyclerview item from background thread after changing configuration
Update recyclerview item from background thread after changing configuration

Time:04-15

I have a RecyclerView in a Fragment. Also I have an AsyncTask which updates items of RecyclerView from background thread. The problem is when I rotate the device (or cause any other configuration change), the AsyncTask cannot update views.

Here you can see the simplified code:

HomeFragment:

public class HomeFragment extends Fragment {

    public HomeFragment() {
    }

    public static HomeFragment newInstance() {
        return new HomeFragment();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_home, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        RecyclerView mRecyclerView = requireView().findViewById(R.id.recyclerview);
        Activity activity = requireActivity();
        ArrayList<String> items = new ArrayList<>(Arrays.asList("Item1", "Item2", "Item3"));
        ListAdapter adapter = new ListAdapter(activity, items);
        mRecyclerView.setAdapter(adapter);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(activity));
    }
}

The adapter of RecyclerView and its inner AsyncTask class:

public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder> {

    private final ArrayList<String> items;
    private final LayoutInflater mInflater;
    private RecyclerView mRecyclerView;

    public ListAdapter(Context context, ArrayList<String> items) {
        mInflater = LayoutInflater.from(context);
        this.items = items;
    }

    @Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        mRecyclerView = recyclerView;
    }

    @NonNull
    @Override
    public ListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View mItemView = mInflater.inflate(R.layout.item, parent, false);
        return new ViewHolder(mItemView, this);
    }

    @Override
    public void onBindViewHolder(@NonNull ListAdapter.ViewHolder holder, int position) {
        holder.name.setText(items.get(position));
    }

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

    public class ViewHolder extends RecyclerView.ViewHolder {
        final ListAdapter mAdapter;
        final TextView name;

        public ViewHolder(@NonNull View itemView, ListAdapter mAdapter) {
            super(itemView);
            this.mAdapter = mAdapter;
            this.name = itemView.findViewById(R.id.file_name);
            itemView.setOnClickListener(v -> {
                Task task = new Task();
                task.execute();
            });
        }
    }

    class Task extends AsyncTask<Void, Integer, Void> {

        @Override
        protected void onProgressUpdate(Integer... values) {
            mRecyclerView.post(() ->
                    ((ViewHolder) Objects.requireNonNull(mRecyclerView.findViewHolderForLayoutPosition(0))).
                            name.setText(String.valueOf(values[0])));
            mRecyclerView.invalidate();
        }

        @Override
        protected Void doInBackground(Void... voids) {
            for (int i = 0; i <= 30; i  ) {
                publishProgress(i);
                SystemClock.sleep(500);
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void unused) {
            ((ViewHolder) Objects.requireNonNull(mRecyclerView.findViewHolderForLayoutPosition(0))).name.setText("task is finished");
        }
    }
}

As you can see I used mRecyclerView.post() method to avoid touching views from non UI thread. How I can update items after rotating the device?

What I tried before:

  • changing data set of adapter and calling notifyDataSetChanged()
  • creating a method in Fragment for updating items and calling it from AsyncTask
  • resetting adapter for RecyclerView
  • ...

CodePudding user response:

You can use below code for update recycler view in screen rotation.

@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {

    super.onConfigurationChanged(newConfig);

    if(adapter!=null){
        adapter.notifyDataSetChanged();
    }
}

Call setRetainInstance(true) in either of onCreate(), onCreateView() or onActivityCreated() methods.

CodePudding user response:

Async task by default work on background thread, we cannot update our ui from background thread it's used for intensive works such as network calls etc. Please try running a

runOnUiThread(new Runnable() {
        @Override
        public void run() {
            // WORK on UI thread here
        }
    });

statement in your async task's onPostExecute method because it indicates your background processing is completed. put code that update views inside this run() method

  • Related