【发布时间】:2016-08-20 12:33:45
【问题描述】:
所以过去几个月我一直在从事 IOS 开发工作,并且广泛使用了 Core Data 和 NSFetchedResultsController。
Core Data + NSFetchedResultsController :自动检测数据库的变化并相应地更新表格元素。
现在我已经切换到 Android 开发,我一直在寻找与上述类似的东西。我查看了各种可用的不同课程,有点困惑。
不同类型的类:
- 光标:提供对数据库查询结果的访问。
- CursorAdapter :用光标链接(列表、回收器)视图并显示对象。
- ContentProvider:提供对数据库对象的访问。需要使用 LoaderManager。
- LoaderManager:由 Activity 或 Fragment 实现,在不阻塞 UI 的情况下加载数据
- Loader:当内容改变时生成光标对象的Loader。
我觉得还值得一提的是,我使用的是greendao,我使用的是它生成的ContentProvider。
更新流程
这是我有点不确定的地方。这是我的假设。
- Content Provider 维护一个游标——LoaderManager 也可能使用 CursorAdapter。
- 当数据库发生变化时,Loader 有一个 ForceLoadContentObserver 观察游标并在其内容发生变化时调用 onLoadFinished。
- 通常游标适配器会在
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 实现上述所有类的。让我知道是否有更好的方法可以做到这一点,但我觉得这已经足够了。
-
在GreenDaoGenerator类中加入如下代码sn-p,生成一个ContentProvider子类
Entity entity = new Entity(); entity.addContentProvider(); -
检查是否正确调用了 ContentProvider 子类方法。将 javadoc 部分添加到 AndroidManifest.xml。对我来说,我还必须将生成的 ContentProvider 类
BASE_PATH变量更改为另一个变量,如下所示。public static final String BASE_PATH = "MYTABLENAME"; -
像我上面写的一样,将LoaderManager,Loader添加到Activity/Fragment类中。您还必须在使用 ContentProvider 之前设置
daoSession对象。我已将下面的 sn-p 移至另一个初始化程序类,以便其他 Activity/Fragment 类也可以使用它。ContentProvider.daoSession = Main.daoSession; 我正在使用 RecyclerView,因此我将 https://gist.github.com/skyfishjy/443b7448f59be978bc59 此处提供的 CursorRecyclerViewAdapter 类进行了一些自定义。如果您使用 ListView,您应该能够使用简单的 CursorAdapter。由于
Loader正在监听更新,所以Adapter不需要监听更新。-
由于这一行,现在 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