Как получить значение LiveData из репозитория, который не имеет доступа к lifeCycleOwner для его наблюдения? - PullRequest
0 голосов
/ 27 января 2019

Я использовал MVVM и ROOM и databindig в моем приложении. Согласно Руководство по архитектуре приложения , я хочу получать данные, используя номер. В макете xml элемента RecyclerView я использую переменную CategoryViewModel. Я получаю список категорий из базы данных Room с типом LiveData , Я хочу изменить тип LiveData<list<CategoryItem>> на тип MutableLiveData<ArrayList<CategoryViewModel>>. Потому что, наконец, мой адаптер использует ArrayList<CategoryViewModel> тип данных. Как получить значение LiveData? Когда я вызываю getValue() метод, возвращает ноль. это CategoryItem модель:

    @Entity(tableName = "category_table")
public class CategoryItem implements Serializable {

    @PrimaryKey
    private int id;
    private String title;
    private String imagePath;
    @TypeConverters({SubCategoryConverter.class})
    private ArrayList<String> subCategory;
    @TypeConverters({DateConverter.class})
    private Date lastRefresh;

    public CategoryItem(int id, String title, String imagePath, ArrayList<String> subCategory, Date lastRefresh) {
        this.id = id;
        this.title = title;
        this.imagePath = imagePath;
        this.subCategory = subCategory;
        this.lastRefresh=lastRefresh;
    }

    public CategoryItem(int id, String title, String imagePath) {
        this.id = id;
        this.title = title;
        this.imagePath = imagePath;
    }

    public CategoryItem() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getImagePath() {
        return imagePath;
    }

    public void setImagePath(String imagePath) {
        this.imagePath = imagePath;
    }

    public ArrayList<String> getSubCategory() {
        return subCategory;
    }

    public void setSubCategory(ArrayList<String> subCategory) {
        this.subCategory = subCategory;
    }

    public Date getLastRefresh() {
        return lastRefresh;
    }

    public void setLastRefresh(Date lastRefresh) {
        this.lastRefresh = lastRefresh;
    }

}

это CategoryViewModel класс:

     public class CategoryViewModel extends AndroidViewModel {

        private String title;
        private String imagePath;
        private MutableLiveData<ArrayList<CategoryViewModel>> allCategories=new MutableLiveData<>();
        private CategoryRepository repository;

        public CategoryViewModel(@NonNull Application application) {
            super(application);
            repository=new CategoryRepository(application, Executors.newSingleThreadExecutor());
        }

        public void init(CategoryItem categoryItem){
            this.title=categoryItem.getTitle();
            this.imagePath=categoryItem.getImagePath();
        }

        public MutableLiveData<ArrayList<CategoryViewModel>> getAllCategories(){

            allCategories=repository.getCategory();
            return allCategories;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public String getImagePath() {
            return imagePath;
        }
    }

Это CategoryRepository класс:

    public class CategoryRepository {

    private static final String TAG="CategoryRepository";
    private static int FRESH_TIMEOUT_IN_MINUTES = 1;

    private final Executor executor;

    private APIInterface apiInterface;
    public MutableLiveData<ArrayList<CategoryViewModel>> arrayListMutableLiveData=new MutableLiveData<>();


    private CategoryDao categoryDao;
    private Application application;

    public CategoryRepository(Application application,Executor executor) {
        this.executor = executor;
        this.application = application;
        apiInterface= APIClient.getClient().create(APIInterface.class);
        LearnDatabase database= LearnDatabase.getInstance(application);
        categoryDao=database.categoryDao();
    }

    public MutableLiveData<ArrayList<CategoryViewModel>>  getCategory(){

        refreshCategory();

        List<CategoryItem> items;
        categoryDao.loadCategoryItem();

        items=categoryDao.loadCategoryItem().getValue(); // return null
        CategoryItem category;
        ArrayList<CategoryViewModel> arrayList=new ArrayList<>();

        for(int i=0;i<items.size();i++){

            category=items.get(i);
            CategoryViewModel categoryViewModel=new CategoryViewModel(application);
            categoryViewModel.init(category);
            arrayList.add(categoryViewModel);
        }


        arrayListMutableLiveData.setValue(arrayList);

        return arrayListMutableLiveData;
    }

    private void refreshCategory(){

        executor.execute(() -> {
            String lastRefresh=getMaxRefreshTime(new Date()).toString();
            boolean sliderExists =(!(categoryDao.hasCategory(lastRefresh)).isEmpty());
            Log.e(TAG,"sliderExist: "+sliderExists);
            Log.e(TAG,"lastrefresh: "+lastRefresh);
            Log.e(TAG,"hasSlider: "+categoryDao.hasCategory(lastRefresh).toString());
            // If user have to be updated
            if (!sliderExists) {
                Log.e(TAG,"in if");
                apiInterface.getCategory().enqueue(new Callback<List<CategoryItem>>() {
                    @Override
                    public void onResponse(Call<List<CategoryItem>> call, Response<List<CategoryItem>> response) {

                        executor.execute(() -> {
                            List<CategoryItem> categories=response.body();
                            for (int i=0;i<categories.size();i++){
                                categories.get(i).setLastRefresh(new Date());
                                categoryDao.saveCategory(categories.get(i));
                            }

                        });
                    }

                    @Override
                    public void onFailure(Call<List<CategoryItem>> call, Throwable t) {

                        Log.e(TAG,"onFailure "+t.toString());
                    }
                });
            }

        });
    }

    private Date getMaxRefreshTime(Date currentDate){
        Calendar cal = Calendar.getInstance();
        cal.setTime(currentDate);
        cal.add(Calendar.MINUTE, -FRESH_TIMEOUT_IN_MINUTES);
        return cal.getTime();
    }
   }

Это xml макет элемента recyclerView:

 <?xml version="1.0" encoding="utf-8"?>
<layout>
    <data class="CategoryDataBinding">
        <variable
            name="category"
            type="com.struct.red.alltolearn.viewmodel.CategoryViewModel"/>
    </data>

    <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="200dp"
        android:layout_height="150dp"
        app:cardCornerRadius="15dp">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <ImageView
                android:id="@+id/imgItemCategory"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                app:imageUrl="@{category.imagePath}" />

            <TextView
                android:id="@+id/txtTitleItemCategory"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:text="@{category.title}"
                android:textColor="#FFFFFF"
                android:textSize="20sp"
                android:textStyle="bold" />
        </RelativeLayout>

    </android.support.v7.widget.CardView>
</layout>

Это CategoryDao класс:

@Dao

открытый интерфейс CategoryDao {

@Query("SELECT * FROM course_table")
LiveData<List<CategoryItem>> loadCategoryItem();


@Insert(onConflict = OnConflictStrategy.REPLACE)
void saveCategory(CategoryItem category);

@Query("SELECT * FROM category_table WHERE lastRefresh > Date(:lastRefreshMax)")
List<CategoryItem> hasCategory(String lastRefreshMax);

}

И, наконец, я наблюдаю MutableLiveData в своем фрагменте:

    private void setupCategoryRecycler() {
    categoryViewModel = ViewModelProviders.of(this).get(CategoryViewModel.class);
    categoryViewModel.getAllCategories().observe(this, new Observer<ArrayList<CategoryViewModel>>() {
        @Override
        public void onChanged(@Nullable ArrayList<CategoryViewModel> categoryViewModels) {
            Log.e(TAG, "categoryitem: " + categoryViewModels.toString());
            categoryAdapter = new CategoryAdapter(getContext(), categoryViewModels);
            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, true);
            linearLayoutManager.setReverseLayout(true);
            CategoryRecy.setLayoutManager(linearLayoutManager);
            CategoryRecy.setAdapter(categoryAdapter);
        }
    });
}

Ответы [ 4 ]

0 голосов
/ 05 февраля 2019

Может быть, вы можете использовать trasnformation?

//this is returned to the observer in setupCategoryRecycler()
return Transformations.switchMap(repository.getCategory()) { result -> 
    //do any other stuff you need here           
    allCategories.setValue(result)
}

Преобразование можно использовать для преобразования одного liveData в другой.Проверить: https://developer.android.com/topic/libraries/architecture/livedata#transform_livedata

0 голосов
/ 04 февраля 2019

Вы пытаетесь загрузить данные из неправильной таблицы course_table

@ Query ("SELECT * FROM course_table") LiveData> loadCategoryItem ();

Должно быть category_table

0 голосов
/ 05 февраля 2019

Ваша проблема здесь, верно?

public MutableLiveData<ArrayList<CategoryViewModel>>  getCategory(){
    ...
    items=categoryDao.loadCategoryItem().getValue(); // returns null
    ...
}

Это потому, что ваш метод categoryDao.loadCategoryItem () возвращает объект LiveData.Это означает, что вызов метода будет выполнен в потоке backdround.Таким образом, в настоящий момент вы вызываете метод getValue (), но значение равно нулю.

Чтобы избежать этого, вы можете сделать две плохие вещи.

1. Вызовите loadCategoryItem ()ранее, чтобы иметь значения позже при вызове getValue ();

Ваш класс репозитория

public class CategoryRepository {
Livedata<List<CategoryItem>> items; // moved here
...

public void init () { 
     items=categoryDao.loadCategoryItem(); 
}

public MutableLiveData<ArrayList<CategoryViewModel>>  getCategory(){

    ArrayList<CategoryViewModel> arrayList=new ArrayList<>();
    List<CategoryItem> currentList = items.getValue(); 
    for(int i=0;i<currentList.size();i++){
          ...  
    }
    arrayListMutableLiveData.setValue(arrayList);
    return arrayListMutableLiveData;
}
}

Ваш класс ViewModel

  public class CategoryViewModel extends AndroidViewModel {

    public void init(CategoryItem categoryItem){
        repository.init();                          // added
        this.title=categoryItem.getTitle();
        this.imagePath=categoryItem.getImagePath();
    }

Это может работать, но у нас есть 2 проблемы,Во-первых, все еще нет гарантии, что значения не будут нулевыми.Вторая проблема заключается в том, что вы не можете наблюдать за изменениями вашего предмета.Даже если вы возвращаете объект arrayListMutableLiveData, который является livingata, вы устанавливаете его значение один раз вручную, и его значение не изменится, пока вы не вызовете getCategory () снова.

2. Secondвзломать элементы категории загрузки async

    public interface CategoryDao {

    @Query("SELECT * FROM category_table") LiveData<List<CategoryItem>>loadCategoryItem();

    @Query("SELECT * FROM category_table") List<CategoryItem> loadCategoryItemsAsync();

В этом случае ваши методы getAllCategories () и getCategory () также должны работать асинхронно.

Примерно так:

public void  getCategory(Listener listener){
executor.execute(() -> {
    ArrayList<CategoryViewModel> arrayList=new ArrayList<>();
    List<CategoryItem> currentList = items.getValue(); 
    for(int i=0;i<currentList.size();i++){
          ...  
    }
    arrayListMutableLiveData.setValue(arrayList);
    listener.onItemsLoaded(arrayListMutableLiveData);
}
}

В этом случае у нас также есть вторая проблема -> вы не можете наблюдать изменения вашего предмета.

Я написал это, чтобы лучше прояснить проблему.*

Реальная проблема заключается в том, что вы пытаетесь использовать CategoryViewModel для привязки данных.

Пожалуйста, используйте взамен CategoryItem

Я предлагаю удалить эти две строки изviewModel

private String title;
private String imagePath;

Попробуйте решить вашу проблему без анализа данных от List до ArrayList.

public LiveData<List<CategoryItem>> getAllCategories(){
        if (items == null) {
            items = categoryDao.loadCategoryItem()
        }
        return items;
    }

, затем попробуйте использовать CategoryItem в качестве объекта данных

<data class="CategoryDataBinding">
    <variable
        name="category"
        type="com.struct.red.alltolearn.///.CategoryItem "/>
</data>

и попробуйте изменить свой адаптер, чтобы сделать это возможным

categoryViewModel = ViewModelProviders.of(this).get(CategoryViewModel.class);
categoryViewModel.getAllCategories().observe(this, new Observer<List<CategoryItem >>() {
        @Override
        public void onChanged(@Nullable List<CategoryItem > categoryItems) {
            categoryAdapter = new CategoryAdapter(getContext(), categoryItems);
...
0 голосов
/ 30 января 2019

Ваш items=categoryDao.loadCategoryItem().getValue() не будет иметь никакого значения, если вы не вызовете наблюдение за ним.

...