【问题标题】:CursorLoader usage without ContentProvider没有 ContentProvider 的 CursorLoader 使用
【发布时间】:2023-03-23 17:47:01
【问题描述】:

Android SDK 文档说 startManagingCursor() 方法已弃用:

此方法已弃用。将新的 CursorLoader 类与 LoaderManager 一起使用;这也可以通过 Android 兼容包在旧平台上使用。此方法允许活动根据活动的生命周期为您管理给定游标的生命周期。也就是说,当活动停止时,它会自动在给定的光标上调用 deactivate(),当它稍后重新启动时,它会为你调用 requery()。当活动被销毁时,所有托管游标将自动关闭。如果您的目标是 HONEYCOMB 或更高版本,请考虑改用 LoaderManager,可通过 getLoaderManager() 获得

所以我想使用CursorLoader。但是,当我需要 CursorLoader 的构造函数中的 URI 时,如何将它与自定义 CursorAdapter 和没有 ContentProvider 一起使用?

【问题讨论】:

    标签: android android-contentprovider android-cursorloader android-loadermanager


    【解决方案1】:

    我写了一个不需要内容提供者的simple CursorLoader

    import android.content.Context;
    import android.database.Cursor;
    import android.support.v4.content.AsyncTaskLoader;
    
    /**
     * Used to write apps that run on platforms prior to Android 3.0. When running
     * on Android 3.0 or above, this implementation is still used; it does not try
     * to switch to the framework's implementation. See the framework SDK
     * documentation for a class overview.
     *
     * This was based on the CursorLoader class
     */
    public abstract class SimpleCursorLoader extends AsyncTaskLoader<Cursor> {
        private Cursor mCursor;
    
        public SimpleCursorLoader(Context context) {
            super(context);
        }
    
        /* Runs on a worker thread */
        @Override
        public abstract Cursor loadInBackground();
    
        /* Runs on the UI thread */
        @Override
        public void deliverResult(Cursor cursor) {
            if (isReset()) {
                // An async query came in while the loader is stopped
                if (cursor != null) {
                    cursor.close();
                }
                return;
            }
            Cursor oldCursor = mCursor;
            mCursor = cursor;
    
            if (isStarted()) {
                super.deliverResult(cursor);
            }
    
            if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
                oldCursor.close();
            }
        }
    
        /**
         * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
         * will be called on the UI thread. If a previous load has been completed and is still valid
         * the result may be passed to the callbacks immediately.
         * <p/>
         * Must be called from the UI thread
         */
        @Override
        protected void onStartLoading() {
            if (mCursor != null) {
                deliverResult(mCursor);
            }
            if (takeContentChanged() || mCursor == null) {
                forceLoad();
            }
        }
    
        /**
         * Must be called from the UI thread
         */
        @Override
        protected void onStopLoading() {
            // Attempt to cancel the current load task if possible.
            cancelLoad();
        }
    
        @Override
        public void onCanceled(Cursor cursor) {
            if (cursor != null && !cursor.isClosed()) {
                cursor.close();
            }
        }
    
        @Override
        protected void onReset() {
            super.onReset();
    
            // Ensure the loader is stopped
            onStopLoading();
    
            if (mCursor != null && !mCursor.isClosed()) {
                mCursor.close();
            }
            mCursor = null;
        }
    }
    

    它只需要AsyncTaskLoader 类。 Android 3.0 或更高版本的,或者兼容包自带的。

    我也是wrote a ListLoader,它与LoadManager 兼容,用于检索通用java.util.List 集合。

    【讨论】:

    • 找到一个很好的代码示例,使用它 - bitbucket.org/ssutee/418496_mobileapp/src/fc5ee705a2fd/demo/… - 发现它非常有用!
    • @Cristian 感谢您的示例。与您的课程相关的许可证是什么。如何重复使用?
    • 许可证是 Apache 2.0;您可以随时随地重复使用它。如果您有任何改进,请告诉我。
    • 好东西!用户应该知道一个限制,那就是它没有刷新数据更改的机制(就像加载器应该做的那样)
    • @Jadeye 这里有男人:ListLoaderSupportListLoader
    【解决方案2】:

    编写您自己的加载器,该加载器使用您的数据库类而不是内容提供程序。最简单的方法是从兼容性库中获取CursorLoader 类的源代码,并将提供程序查询替换为对您自己的数据库助手类的查询。

    【讨论】:

    • 这是我认为最简单的方法。在我的应用程序中,我创建了一个 CursorLoader 后代来管理 SQLite 游标,除了构造函数之外,我只需要覆盖 loadInBackground 方法即可用我的游标查询替换提供程序查询
    【解决方案3】:

    SimpleCursorLoader 是一个简单的解决方案,但是它不支持在数据更改时更新加载器。 CommonsWare 有一个 loaderex 库,它添加了一个 SQLiteCursorLoader 并支持对数据更改的重新查询。

    https://github.com/commonsguy/cwac-loaderex

    【讨论】:

    • 但是,要使用自动重新查询,您需要为 UI 和更新使用相同的加载器,从而限制了其对后台服务的可用性。
    【解决方案4】:

    第三种选择是简单地覆盖loadInBackground

    public class CustomCursorLoader extends CursorLoader {
        private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
    
        @Override
        public Cursor loadInBackground() {
            Cursor cursor = ... // get your cursor from wherever you like
    
            if (cursor != null) {
                // Ensure the cursor window is filled
                cursor.getCount();
                cursor.registerContentObserver(mObserver);
            }
    
            return cursor;
        }
    };
    

    这也将负责在数据库更改时重新查询您的光标。

    唯一需要注意的是:您必须定义另一个观察者,因为谷歌以其无限的智慧决定将他们的包设为私有。如果您将类放入与原始类(或兼容类)相同的包中,您实际上可以使用原始观察者。观察者是一个非常轻量级的对象,不会在其他任何地方使用,所以这并没有太大的区别。

    【讨论】:

    • 我在快速测试中的观察是,只有当光标指向内容提供者时,才会针对光标调用 registerContentObserver。你能确认/否认吗?
    • 不一定是 ContentProvider。但是游标需要注册到通知uri(setNotificationUri),然后需要有人(通常是ContentProvider,但可以是任何东西)通过调用ContentResolver.notifyChange来通知它。
    • 是的。在您的 CustomLoader 的 loadInBackground() 上,在返回光标之前,说 cursor.setNotificationUri(getContext().getContentResolver(), uri); uri 可能只是来自随机字符串,如 Uri.parse("content://query_slot1")。似乎它不在乎uri是否真的存在。一旦我对数据库进行了操作。说getContentResolver().notifyChange(uri, null); 可以解决问题。然后我可能会在一个 contant 文件中为具有少量查询的应用程序创建几个“查询 uri 插槽”。我测试在运行时插入数据库记录,它似乎有效,但我仍然怀疑它是否是好的做法。有什么建议吗?
    • 我在@Yeung 的建议下使用这种方法,一切正常,包括在数据库更新时自动重新加载光标。
    • 不需要任何 unregisterContentObserver 吗?
    【解决方案5】:

    Timo Ohr 提出的第三个选项与 Yeung 的 cmets 一起提供了最简单的答案(奥卡姆剃刀)。下面是一个适合我的完整类的示例。使用这个类有两个规则。

    1. 扩展此抽象类并实现方法 getCursor() 和 getContentUri()。
    2. 只要底层数据库发生变化(例如,在插入或删除之后),请确保调用

      getContentResolver().notifyChange(myUri, null);
      

      其中 myUri 与您的方法 getContentUri() 的实现返回的相同。

    这是我使用的类的代码:

    package com.example.project;
    
    import android.content.Context;
    import android.database.Cursor;
    import android.content.CursorLoader;
    import android.content.Loader;
    
    public abstract class AbstractCustomCursorLoader extends CursorLoader
      {
        private final Loader.ForceLoadContentObserver mObserver = new Loader.ForceLoadContentObserver();
    
        public AbstractCustomCursorLoader(Context context)
          {
            super(context);
          }
    
        @Override
        public Cursor loadInBackground()
          {
            Cursor cursor = getCursor();
    
            if (cursor != null)
              {
                // Ensure the cursor window is filled
                cursor.getCount();
                cursor.registerContentObserver(mObserver);
              }
    
            cursor.setNotificationUri(getContext().getContentResolver(), getContentUri());
            return cursor;
          }
    
        protected abstract Cursor getCursor();
        protected abstract Uri getContentUri();
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-11
      • 1970-01-01
      • 2013-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多