如何从无法访问 lifeCycleOwner 来观察它的存储库中获取 LiveData 的价值?

How to get value of LiveData from repository that don't access to lifeCycleOwner for observing it?

我在 app.According 中使用了 MVVMROOM 以及 databindig 应用程序架构指南,我想使用 room.In RecyclerView 项目的 xml 布局来兑现数据,我使用 CategoryViewModel variable.I 获取列表Room 数据库中具有 LiveData 类型的类别。我想将 LiveData<list<CategoryItem>> 类型更改为 MutableLiveData<ArrayList<CategoryViewModel>> 类型。因为最终我的适配器使用 ArrayList<CategoryViewModel> 数据 type.How 来获取 LiveData 的值?当我调用 getValue() 方法时,returns 为空。 这是 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 class:

     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 class:

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

这是 recyclerView 项的 xml 布局:

 <?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 class:

@Dao

public 接口 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);
        }
    });
}

您的 items=categoryDao.loadCategoryItem().getValue() 将没有任何价值,除非您在其上调用 observe。

您正在尝试从错误的位置加载数据tablecourse_table

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

应该是category_table

你的问题就在这里,对吧?

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

这是因为您的 categoryDao.loadCategoryItem() 方法 returns LiveData 对象。这意味着方法调用将在后台线程中执行。因此,当您调用 getValue() 方法时,该值在那一刻仍为空。

要摆脱这种情况,您可以做两件坏事。

1. 提前调用 loadCategoryItem(),以便稍后在调用 getValue() 时获得值;

您的存储库class

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

你的视图模型class

  public class CategoryViewModel extends AndroidViewModel {

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

这可以工作,但我们有 2 个问题。首先是仍然不能保证值不会为空。第二个问题是您无法观察到您的项目变化。即使你返回的是 arrayListMutableLiveData 对象,它是 livedata,你手动设置它的值一次,除非你再次调用 getCategory(),否则它的值不会改变。

2. 第二个技巧是同步加载类别项目

    public interface CategoryDao {

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

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

在这种情况下,您的 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);
...
 

也许你可以使用转换?

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

转换可用于将一个实时数据转换为另一个。检查:https://developer.android.com/topic/libraries/architecture/livedata#transform_livedata