Не могу обновить адаптер RecyclerView данными, полученными из Livedata в другом потоке
Приложение падает с NPE в методе getItemCount(). Ошибка -
Process: com.example.fridge_project, PID: 18634
java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference.
На закомментированном участке по сути то же самое работает. Как можно поменять адаптер из другого потока?
public class ProductsActivity extends AppCompatActivity {
private RecyclerView productsRecycler ;
private ImageButton addProduct ;
private FridgeRepository fridgeRepository;
private MutableLiveData<List<FoodData>> foodList ;
private ArrayList<FoodData> testList ;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_products);
initTestList();
addProduct = findViewById(R.id.addProduct);
productsRecycler = findViewById(R.id.recyclerProducts);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
productsRecycler.setAdapter(new ProductsAdapter(testList));
productsRecycler.setLayoutManager(linearLayoutManager);
initRepo(this);
// foodList = new MutableLiveData<>();
// ArrayList<FoodData> m = new ArrayList<>();
// foodList.setValue(m);
// ArrayList<FoodData> n = (ArrayList<FoodData>) foodList.getValue();
// productsRecycler.setAdapter(new ProductsAdapter(n));
}
@Override
protected void onDestroy() {
super.onDestroy();
closeRepo();
}
private void initRepo(final Context context) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
fridgeRepository = new FridgeRepository(ProductsActivity.this) ;
foodList = fridgeRepository.getFoodDataList();
List<FoodData> foodData = new ArrayList<>() ;
MutableLiveData<List<FoodData>> k = new MutableLiveData<>();
k.postValue(foodData);
ArrayList<FoodData> foodData1 = (ArrayList<FoodData>) k.getValue() ;
productsRecycler.setAdapter(new ProductsAdapter(foodData1));
}
});
thread.start();
}
private void closeRepo() {
fridgeRepository = null ;
}
private void initTestList() {
FoodData foodData1 = new FoodData("Name1", 2.0) ;
FoodData foodData2 = new FoodData("Name2", 2.0) ;
FoodData foodData3 = new FoodData("Name3", 2.0) ;
testList = new ArrayList<>();
testList.add(foodData1) ;
testList.add(foodData2) ;
testList.add(foodData3) ;
}
class ProductsAdapter extends RecyclerView.Adapter<ProductsAdapter.ProductsViewHolder>{
List<FoodData> rFoodList ;
ProductsAdapter(List<FoodData> list) {
rFoodList = list ;
}
@NonNull
@Override
public ProductsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.product_element, parent, false) ;
return new ProductsViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ProductsViewHolder holder, int position) {
holder.bindData(rFoodList.get(position));
}
@Override
public int getItemCount() {
return rFoodList.size();
}
public void changeList(LiveData<ArrayList<FoodData>> newList) {
rFoodList = newList.getValue();
}
class ProductsViewHolder extends RecyclerView.ViewHolder {
private TextView productName ;
private TextView productAmount ;
public ProductsViewHolder(@NonNull View itemView) {
super(itemView);
productName = itemView.findViewById(R.id.productName) ;
productAmount = itemView.findViewById(R.id.productAmount) ;
}
void bindData(FoodData foodData) {
productName.setText(foodData.getName());
productAmount.setText(foodData.getAmount().toString());
}
}
}
}
Ответы (1 шт):
Если коллекция данных равна null, то нужно указать, что itemCount = 0. Либо rFoodList должен быть NonNull.
@Override
public int getItemCount() {
return rFoodList != null? rFoodList.size() : 0;
}
на foodList нужно подписаться в методе onCreate Activity, но перед этим необходимо инициализировать foodList, foodList = new MutableLiveData<>().
foodList.observe(this, new Observer<List<FoodData>>() {
@Override
public void onChanged(@Nullable List<FoodData> value) {
productsRecycler.setAdapter(new ProductsAdapter(value));
}
});
//задаем тестовые данные
private void initTestList() {
FoodData foodData1 = new FoodData("Name1", 2.0) ;
FoodData foodData2 = new FoodData("Name2", 2.0) ;
FoodData foodData3 = new FoodData("Name3", 2.0) ;
testList = new ArrayList<>();
testList.add(foodData1) ;
testList.add(foodData2) ;
testList.add(foodData3) ;
foodList.postValue(testList);
}
private void initRepo(final Context context) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
fridgeRepository = new FridgeRepository(ProductsActivity.this) ;
foodList.postValue(fridgeRepository.getFoodDataList());//если метод getFoodDataList возвращает List<FoodData>.
//если getFoodDataList возвращает LiveData то нужно подписаваться на него напрямую, а не использовать локальный foodList
}
});
thread.start();
}
Да и вообще, лучше с начало понять суть работы LiveData. Вот простой для понимания урок: https://startandroid.ru/ru/courses/architecture-components/27-course/architecture-components/525-urok-2-livedata.html
p.s. LayoutManager для RecyclerView нужно указывать до выполнения setAdapter.
productsRecycler.setLayoutManager(linearLayoutManager);
productsRecycler.setAdapter(new ProductsAdapter(testList));