【问题标题】:Relationship between Cursor, CursorAdapter, LoaderManager, LoaderCursor、CursorAdapter、LoaderManager、Loader之间的关系
【发布时间】:2016-08-20 12:33:45
【问题描述】:

所以过去几个月我一直在从事 IOS 开发工作,并且广泛使用了 Core Data 和 NSFetchedResultsController。

Core Data + NSFetchedResultsController :自动检测数据库的变化并相应地更新表格元素。

现在我已经切换到 Android 开发,我一直在寻找与上述类似的东西。我查看了各种可用的不同课程,有点困惑。

不同类型的类:

  1. 光标:提供对数据库查询结果的访问。
  2. CursorAdapter :用光标链接(列表、回收器)视图并显示对象。
  3. ContentProvider:提供对数据库对象的访问。需要使用 LoaderManager。
  4. Lo​​aderManager:由 Activity 或 Fragment 实现,在不阻塞 UI 的情况下加载数据
  5. Lo​​ader:当内容改变时生成光标对象的Loader。

我觉得还值得一提的是,我使用的是greendao,我使用的是它生成的ContentProvider。

更新流程

这是我有点不确定的地方。这是我的假设。

  1. Content Provider 维护一个游标——LoaderManager 也可能使用 CursorAdapter。
  2. 当数据库发生变化时,Loader 有一个 ForceLoadContentObserver 观察游标并在其内容发生变化时调用 onLoadFinished。
  3. 通常游标适配器会在onLoadFinished() 内调用swap(),从而更新表。

牢记上述内容,我创建了一个 ContentProvider 并实现了 LoaderManager,但是当我持久化一个新对象(使用 greenDao)时,函数 onLoadFinished() 没有被调用——这让我开始质疑我是否理解正确处理。这是一个 sn-p 的代码,显示了我到目前为止一般编码的内容。

片段类

public class MissionPageFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {

    // Various initialization methods

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        getLoaderManager().initLoader(0, savedInstanceState, this);
    }

    @Override
    public Loader onCreateLoader(int id, Bundle args) {
        ContentProvider.daoSession = Main.daoSession;
        Uri uri = ContentProvider.CONTENT_URI;

        // Other initializations

        CursorLoader cursorLoader = new CursorLoader(getContext(), uri, projections, null, null, null);
        return cursorLoader;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        System.out.println("Should be called when a new item is persisted... but not called =(");
    }

    // Other methods
}

如果有人能确认我是否正确地考虑了这个过程和/或阐明可能出了什么问题,我将不胜感激。

编辑 1

这里是greenDao生成的ContentProvider子类中query()函数的sn-p。

@Override
public Cursor query(Uri uri, String[] projection, String selection,
                    String[] selectionArgs, String sortOrder) {

    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
    int uriType = sURIMatcher.match(uri);
    switch (uriType) {
        case MISSION_DIR:
            queryBuilder.setTables(TABLENAME);
            break;
        case MISSION_ID:
            queryBuilder.setTables(TABLENAME);
            queryBuilder.appendWhere(PK + "="
                    + uri.getLastPathSegment());
            break;
        default:
            throw new IllegalArgumentException("Unknown URI: " + uri);
    }

    SQLiteDatabase db = getDatabase();
    Cursor cursor = queryBuilder.query(db, projection, selection,
            selectionArgs, null, null, sortOrder);
    cursor.setNotificationUri(getContext().getContentResolver(), uri);

    return cursor;
}

解决方案

我只是想分享一下我是如何使用 GreenDao 实现上述所有类的。让我知道是否有更好的方法可以做到这一点,但我觉得这已经足够了。

  1. 在GreenDaoGenerator类中加入如下代码sn-p,生成一个ContentProvider子类

    Entity entity = new Entity();
    entity.addContentProvider();
    
  2. 检查是否正确调用了 ContentProvider 子类方法。将 javadoc 部分添加到 AndroidManifest.xml。对我来说,我还必须将生成的 ContentProvider 类 BASE_PATH 变量更改为另一个变量,如下所示。

    public static final String BASE_PATH = "MYTABLENAME";
    
  3. 像我上面写的一样,将LoaderManager,Loader添加到Activity/Fragment类中。您还必须在使用 ContentProvider 之前设置 daoSession 对象。我已将下面的 sn-p 移至另一个初始化程序类,以便其他 Activity/Fragment 类也可以使用它。

    ContentProvider.daoSession = Main.daoSession;
    
  4. 我正在使用 RecyclerView,因此我将 https://gist.github.com/skyfishjy/443b7448f59be978bc59 此处提供的 CursorRecyclerViewAdapter 类进行了一些自定义。如果您使用 ListView,您应该能够使用简单的 CursorAdapter。由于Loader 正在监听更新,所以Adapter 不需要监听更新。

  5. 由于这一行,现在 ContentProvider 子类能够更新视图

    getContext().getContentResolver().notifyChange(uri, null);
    

    由GreenDao自动生成。这意味着您可以将 ContentProvider 用于所有 CRUD 操作,并且视图将相应地自动更新。这似乎违背了使用 ORM 的目的。因此,我现在进行正常的 GreenDao 操作,并在每次插入、删除、更新后手动调用 notifyChange

    GreenDaoObj obj = new GreenDaoObj();
    obj.insert();
    getContext.getContentResolver().notifyChange(MyContentProvider.CONTENT_URI, null);
    

我认为没有比这更好的方法了,因为我真的不想接触 GreenDao 为 Model 和 Dao 对象生成的代码。我想可以添加嵌套生成的 CRUD 方法的自定义 CRUD 函数,但这应该是微不足道的。

我一直在环顾四周,没有一个有据可查的方法来使用 GreenDao 和 Loader/LoaderManager,所以我想我会在这里组织一下。希望这对计划实施此功能的人有所帮助。

【问题讨论】:

  • Loader 和 LoaderManager 是加载内容的类。它可以是光标,但也可以是 Json 或其他东西。你可以在没有加载器的情况下从 SQLite 加载数据。但在您的示例中,ContentProvider 从 SQLite 加载数据并返回游标。为此,您必须在 ContentProvider 中覆盖 query(不要忘记将其添加到您的 AndroidManifest。
  • 我使用的是greendao生成的ContentProvider子类。我已经编辑并添加了一个对我来说看起来不错的生成代码的 sn-p(尽管我不是 ContentProvider 类的专家)。
  • 您是否已将 ContentProvider 添加到您的 AndroidManaifest 中? --> developer.android.com/guide/topics/providers/…
  • 是的,也这样做了。我还确认调用了 ContentProvider 子类(我假设如果我没有将 ContentProvider 添加到 AndroidManifest.xml 文件中,则不会调用子类函数)。

标签: java android android-contentprovider loader greendao


【解决方案1】:

只有几件事你没有完全正确。

  1. ContentProviders 是提供数据访问的 Android 组件。该数据可以存储在关系数据库、平面文件、远程服务器等中。提供程序具有通用的类似 REST 的接口,并作为您拥有的不同数据存储选项的抽象层。 ContentProviders 也可以从外部应用程序(如果配置正确)访问,这使它们成为应用程序之间共享数据的默认方式。访问它们不需要LoaderManager。应通过ContentResolver 访问它们。
  2. LoaderManager 负责 Loader 生命周期并与 ActivityFragment 生命周期协调。
  3. Loaders 提供了一种生命周期感知方式来将数据加载到活动或片段中。 CursorLoader 是一个特殊的实现,带有一些特性——它使用一个工作线程来保持 UI 线程的加载,它使用一个抽象的 ContentResolver 来访问一个 ContentProvider 并设置一个 @987654333 @。 ContentObserver 将侦听查询 URL 的更新并在需要时重新加载数据。

为了让ContentObserver 通知正常工作,您必须确保有几件事已经到位。首先,您的ContentProvider 应该将更改传播给观察者。这是使用getContext().getContentResolver().notifyChange(url, observer) 完成的。然后,如果您有一个 CursorLoader 对通知的 URL 执行查询,它将自动重新加载。

【讨论】:

  • 感谢您的反馈。我将开始查找 ContentObserver 并回复您。
  • 从您的提示中弄清楚了。 CursorLoader 在其 loadInBackground 方法中固有地调用 registerContentObserver。这意味着对于CursorLoader,无需声明额外的ContentObserver。我遇到的问题是我使用的是 greendao 的 insert 方法,而不是 ContentProviders insert 方法。谢谢楼主
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-24
  • 2014-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多