Android Content Provider Tutorial--安卓内容提供者系列5--Loader用法

  • 上一篇链接: Android Content Provider Tutorial--安卓内容提供者系列4--如何创建内容提供者


    Loaders(加载器)

    What’s a Loader?(什么是加载器)

    • Loaders make it easy to load data asynchronously in an activity or fragment. Loaders have these characteristics:

      • They are available to every Activity and Fragment
      • They provide asynchronous loading of data.
      • They monitor the source of their data and deliver new results when the content changes.
      • They automatically reconnect to the last loader’s cursor when being recreated after a configuration change. Thus, they don’t need to re-query their data.

    加载器可以使在Activity或Fragment中异步加载数据变得很容易。加载器有下面这些特点:

    • 在每个Activity 和Fragment中都可以获取的到加载器。
    • 他们提供数据的异步加载。
    • 他们监视他们的数据的来源,并且在内容更改时提供新的结果。
    • 当程序的配置更改后加载器的游标会重新创建,加载器能自动重新连接到最新的那个游标。因此,他们不需要重新查询他们的数据。
    • Loaders were introduced in Honeycomb (API 11).

      • The Android Support Package includes support for loaders. By including the support package in your application, you can use loaders even if your application for a minSdkVersion of 4 or later.

    • Android 3.0(API 11)版本才推出加载器。

      • Android支持包包含了对加载器的支持。通过在应用程序中放入支持包,你可以即使最小版本是api 4或更高版本中使用加载器

    The Loader Classes and Interfaces(加载器涉及到的类及接口)

    There are several classes and interfaces that implement the loader functionality:

    有下面这几个类和接口来实现加载器的功能:

    LoaderManager

    An abstract class associated with an Activity or Fragment for managing one or more Loader instances. There is only one LoaderManager per activity or fragment. But a LoaderManager can have multiple loaders.

    它是一个与 Activity 或 Fragment关联,用来管理一个或多个加载器实例的抽象类。Activity 或 Fragment只有一个LoaderManager。但一个LoaderManager可以有多个加载器。

    LoaderManager.LoaderCallbacks

    A callback interface for a client to interact with the LoaderManager.

    这是客户端用来和LoaderManager交互的一个回调接口。

    Loader

    An abstract class that performs asynchronous loading of data.

    一个用来异步加载数据的抽象类

    AsyncTaskLoader

    Abstract loader class that provides an AsyncTask to do the work.

    一个可以提供AsyncTask来工作的抽象loader类

    CursorLoader

    A concrete subclass of AsyncTaskLoader that queries the ContentResolver and returns a Cursor. This class implements the Loader protocol in a standard way for querying cursors, building on AsyncTaskLoader to perform the cursor query on a background thread so that it does not block the application’s UI.

    这是AsyncTaskLoader的一个具体子类,他查询ContentResolver并返回一个游标。这个类以查询游标的标准方式实现了Loader协议,他基于AsyncTaskLoader在后台线程中执行游标查询操作,这样它就不会阻塞应用程序的UI线程。

    As of API 15, the only concrete Loader implementation is the CursorLoader, which can query only a content provider; it cannot run a query directly against a SQLite database.
    Android到了API 15,加载器的唯一具体实现就只有CursorLoader了,它只可以查询ContentProvider,不能直接对SQLite数据库进行访问。


    Using Loaders in an Application(在应用中使用加载器)

    • An application that uses loaders typically includes the following:

      • An Activity or Fragment.
      • An instance of the LoaderManager.
      • A CursorLoader to load data backed by a ContentProvider. Alternatively, you can implement your own subclass of Loader or AsyncTaskLoader to load data from some other source.
      • A data source, such as a ContentProvider, when using a CursorLoader.
      • An implementation for LoaderManager.LoaderCallbacks. This is where you create new loader instances and manage your references to existing loaders.
      • A way of displaying the loader’s data, such as a SimpleCursorAdapter.

    一个使用加载器的应用程序通常包括以下部分:

    • 一个Activity 或者 Fragment。
    • LoaderManager的实例。
    • 一个能够加载ContentProvider提供的数据的CursorLoader。或者,你也可以自己实现一个Loader或AsyncTaskLoader的子类来加载源于其他地方的数据。
    • 一个数据源,比如ContentProvider,当使用CursorLoader的时候。
    • LoaderManager.LoaderCallbacks一个实现类。这就是你创建新的加载器实例和管理现有加载器引用的地方。
    • 一种显示加载器的数据的方式,比如用SimpleCursorAdapter。


    Accessing the LoaderManager(访问LoaderManager)

    • To use loaders in an activity or fragment, you need an instance of LoaderManager.

      • There is only one LoaderManager per activity or fragment.
    在 activity 或 fragment中使用加载器,你需要一个LoaderManager实例。
    • 每个activity 或 fragment中只有一个LoaderManager实例。
    • If you’re using the standard APIs, invoke getLoaderManager() as provided in the Activity and Fragment classes.

      If you’re using the Support Package, you must use android.support.v4.app.FragmentActivity as the base class for your activity.

      • Invoke FragmentActivity.getSupportLoaderManager() to obtain a LoaderManager for the activity.
      • Invoke android.support.v4.app.Fragment.getLoaderManager() to obtain a LoaderManager for a fragment.

    如果你使用标准的api,可以调用 Activity 和 Fragment中提供  的getLoaderManager()方法来获得LoaderManager实例。

    如果你使用支持包,就必须使用android.support.v4.app.FragmentActivity作为Activity的基类。

    • 可以调用FragmentActivity.getSupportLoaderManager()方法来为activity获取一个LoaderManager实例。
    • 可以调用android.support.v4.app.Fragment.getLoaderManager()方法来为Fragment获取一个LoaderManager实例。

    Starting a Loader(启动加载器)

    Invoke FragmentManager.initLoader() to initialize a specified Loader:

    可以调用FragmentManager.initLoader()方法去初始化一个加载器。

    // Prepare the loader.  Either re-connect with an existing one,// or start a new one.
    getLoaderManager().initLoader(0, null, this);


      • The initLoader() method takes the following parameters:

        • A unique integer ID that identifies the loader. In this example, the ID is 0.
        • Optional arguments in the form of a Bundle to supply to the loader at construction (null in this example).
        • A LoaderManager.LoaderCallbacks implementation, which the LoaderManager calls to report loader events. In this example, the local class implements the LoaderManager.LoaderCallbacks interface, so it passes a reference to itself, this.

      initLoader()方法接受以下参数:

      • 一个用来标识加载器的唯一整数ID。在这个例子中,ID为0。
      • 还有一个bundle形式的可选参数,主要用来构造loader的(在这个例子中该参数为null)。
      • 还要实现一个LoaderManager.LoaderCallbacks,这个是LoaderManager用来回调加载事件的。在这个例子中,本地类实现了LoaderManager.LoaderCallbacks接口,所以它给自己传递了一个引用。
    • The initLoader() call ensures that a loader is initialized and active. It has two possible outcomes:

      • If the loader specified by the ID already exists, the last created loader is reused.
      • If the loader specified by the ID does not exist, initLoader() triggers the LoaderManager.LoaderCallbacks method onCreateLoader(). This is where you implement the code to instantiate and return a new loader.

      You typically initialize a Loader within an activity’s onCreate() method, or within a fragment’s onActivityCreated() method.

    调用initLoader()确保了加载器的初始化和激活状态。它有两个可能的结果:

    • 如果ID指定的加载器已经存在,新创造的加载器就是重用的。
    • 如果ID指定的加载器不存在,initLoader()就会触发LoaderManager.LoaderCallbacks中的 onCreateLoader()方法。这就是你写代码实例化并返回一个新的加载器的地方。

    您通常会在一个Activity的onCreate()方法中初始化一个加载器,或在一个Fragment的onActivityCreated()方法中做这个事。


    Restarting a Loader(重启一个加载器)

    If you want to discard the the old data returned by a Loader and have it load fresh data, invoke the FragmentManager.resetLoader() method.

    如果你想丢弃掉加载器返回的旧的数据,并让其加载新数据,那就调用FragmentManager.resetLoader()方法。

    // Refresh the loader with new data
    getLoaderManager().resetLoader(0, null, this);
    • The resetLoader() method accepts the same arguments as initLoader():

      • The integer ID of a Loader
      • Optional arguments in the form of a Bundle
      • A LoaderManager.LoaderCallbacks implementation

    resetLoader()方法接收的参数和initLoader()接收的一样:

    • 一个标识加载器的整数ID
    • bundle形式的可选参数
    • LoaderManager.LoaderCallbacks的实现

    Destroying a Loader(销毁一个加载器)

    If you want to destroy a Loader and have it discard the data that it loaded, invoke the FragmentManager.destroy() method.

    The destroyLoader() method accepts only the integer ID of a Loader to destroy.

    如果你想销毁一个加载器,并丢弃掉它加载的数据,那就调用FragmentManager.destroyLoader()方法。

  • destroyLoader()方法只接受一个参数,即加载器的整型ID。

  • // Destroy a loader and release its data
    getLoaderManager().destroyLoader(0);


    The LoaderManager.LoaderCallbacks Listener(LoaderManager.LoaderCallbacks回调监听器)

    The LoaderManager.LoaderCallbacks listener must implement the following interface:

    LoaderManager.LoaderCallbacks回调监听器必须实现以下接口:

    public interface LoaderCallbacks<DataType> {
          
          
            public Loader<DataType> onCreateLoader(int id, Bundle args);
            public void onLoadFinished(Loader<DataType> loader, DataType data);
            public void onLoaderReset(Loader<DataType> loader);
    }


    onCreateLoader()

    The LoaderManager invokes this method if it needs to create the Loader with the specified id. The LoaderManager also passes along the Bundle argument it received in the LoaderManager.initLoader() method.

    The listener must instantiate the specified loader and return a reference to it. The LoaderManager then manages interaction with the Loader as required.

    LoaderManager如果需要根据指定id创建加载器,就必须调用这个方法。LoaderManager也会把在LoaderManager.initLoader()方法Bundle参数继续传递。

    监听器必须实例化指定加载程序并返回一个引用。然后LoaderManager管理与装载机的交互。


    onLoadFinished()

    The LoaderManager invokes this method when the loader has finished loading the requested data. It provides a reference to the data and the loader that generated it.

    The listener should then use the data in whatever way the fragment or activity requires. For example, if a CursorLoader provides a Cursor as a result of a query, the onLoadFinished() method might hook up the Cursor to a CursorAdapter to display the data.

    当加载器完成了对请求数据的加载,LoaderManager就会调用这个方法。它提供了一个数据的引用和一个产生该数据的加载器的引用。

    然后监听器就可以以任何Activity或Fragment想要的方式去使用这些数据。例如,如果一个CursorLoader提供一个游标作为查询的结果,那onLoadFinished()方法会把游标链接到CursorAdapter,以此来显示数据。


    onLoaderReset()

    The LoaderManager invokes this method when a loader is being reset or destroyed. It indicates that the data provided by the loader is becoming unavailable. At this point, the listener should remove any references it has to the loader’s data.

    当加载器被重置或销毁时,LoaderManager会调用该方法。这表示加载器提供的数据变得不再可用。在这一点上,监听器应该删除它所有的任何关于加载器的数据的引用。

    The loader "owns" the data it provides. The listener should not attempt to release or delete the data. For example, a CursorLoader provides a Cursor, the consumer of the Cursor should not attempt to close it; it should simply release its reference to the Cursor in response to the onLoaderReset() method call.
    加载器“拥有”它所提供的数据。监听器不应试图释放或删除数据。例如,CursorLoader提供了一个游标,游标的使用者不应试图关闭它;而应该只是简单地释放其游标的引用,在onLoaderReset()方法被调用的时候。

    The CursorLoader

    • The CursorLoader is a concrete loader implementation that queries the ContentResolver and returns a Cursor.

      • Typically, the only interaction your listener implementation needs to have with a CursorLoader is to instantiate it in response to an onCreateLoader() call.
      • The CursorLoader constructor take a Context followed by the same arguments as ContentResolver.query():
        uri

        The URI, using the content:// scheme, for the content to retrieve.

        projection

        A list of which columns to return. Passing null returns all columns, which is inefficient.

        selection

        A filter declaring which rows to return, formatted as an SQL WHERE clause (excluding the WHERE itself). Passing null returns all rows for the given URI.

        selectionArgs

        You may include ?s in selection, which will be replaced by the values from selectionArgs, in the order that they appear in the selection.

        sortOrder

        How to order the rows, formatted as an SQL ORDER BY clause (excluding the ORDER BY itself). Passing null use the default sort order.

    CursorLoader是加载器的具体实现,它可以查询ContentResolver并返回一个游标。

    • 一般来说,在onCreateLoader()被调用的时候,你实现的监听器和CursorLoader唯一需要做的交互就是实例化CursorLoader。
    • CursorLoader构造函数接受一个和ContentResolver.query()跟随的一样的上下文参数:
      uri

      URI,使用格式: content:/ /scheme,表示要检索的内容。

      projection

      一个装有要返回的列的集合。传null表示要返回所有列,当然这是低效的。

      selection

      一个声明哪些行可以被返回的过滤器,格式类似于一个SQL语句中的 WHERE子句(不包括WHERE单词 自身)。通传null表示要返回所有和给定URI对应的行。

      selectionArgs

      selection参数中包含一些筛选条件,这些条件会被selectionArgs参数中的值所替代,selectionArgs参数顺序和seletion中的参数顺序一一对应。

      sortOrder

      这个参数表示如何给返回的行排序,它的格式类似于一个SQL语言中的ORDER BY从句(不含ORDER BY这个关键词本身)。如果传null的话就使用默认的排序顺序,这可能是无序的。

    A Simple Example(举个简单例子)

    public static class CursorLoaderListFragment extends ListFragment
            implements MenuItem.OnMenuItemClickListener,
                    LoaderManager.LoaderCallbacks<Cursor> {
          
          
    
        // This is the Adapter being used to display the list's data.
        SimpleCursorAdapter mAdapter;
    
        @Override public void onActivityCreated(Bundle savedInstanceState) {
          
          
            super.onActivityCreated(savedInstanceState);
    
            // Give some text to display if there is no data.  In a real
            // application this would come from a resource.
            setEmptyText("No phone numbers");
    
            // Create an empty adapter we will use to display the loaded data.
            mAdapter = new SimpleCursorAdapter(getActivity(),
                    android.R.layout.simple_list_item_2, null,
                    new String[] {
          
           Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
                    new int[] {
          
           android.R.id.text1, android.R.id.text2 }, 0);
            setListAdapter(mAdapter);
    
            // Prepare the loader.  Either re-connect with an existing one,
            // or start a new one.
            getLoaderManager().initLoader(0, null, this);
        }
    
        @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
          
          
            // Place an action bar item for searching.
            MenuItem item = menu.add("Refresh");
            item.setIcon(android.R.drawable.ic_menu_rotate);
            item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
                    item.setOnMenuItemClickListener(this);
        }
    
        public boolean onMenuItemClick(String newText) {
          
          
            // Called when the user clicks the refresh button.
            getLoaderManager().restartLoader(0, null, this);
            return true;
        }
    
        // These are the Contacts rows that we will retrieve.
        static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
          
          
            Contacts._ID,
            Contacts.DISPLAY_NAME,
            Contacts.CONTACT_STATUS,
        };
    
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
          
          
            // This is called when a new Loader needs to be created.  This
            // sample only has one Loader, so we don't care about the ID.
            Uri baseUri = Contacts.CONTENT_URI;
    
            // Now create and return a CursorLoader that will take care of
            // creating a Cursor for the data being displayed.
            String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
                    + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
                    + Contacts.DISPLAY_NAME + " != '' ))";
            return new CursorLoader(getActivity(), baseUri,
                    CONTACTS_SUMMARY_PROJECTION, select, null,
                    Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
        }
    
        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
          
          
            // Swap the new cursor in.  (The framework will take care of closing the
            // old cursor once we return.)
            mAdapter.swapCursor(data);
        }
    
        public void onLoaderReset(Loader<Cursor> loader) {
          
          
            // This is called when the last Cursor provided to onLoadFinished()
            // above is about to be closed.  We need to make sure we are no
            // longer using it.
            mAdapter.swapCursor(null);
        }
    }

    Module Summary(总结)

    • You should now be able to:

      • Write client code that can read and modify the data managed by a content provider
      • Implement a basic content provider to expose structured data to other applications
      • Use loaders to retrieve a Cursor from a content provider without blocking your application’s main thread

    现在,您应该能够:

    • 编写客户端代码,用来可以读取和修改由ContentProvider管理的数据;
    • 实现一个基本的ContentProvider,将结构化数据暴露给其他应用程序;
    • 在不阻塞你的应用主线程的情况下,通过使用加载器来检索从ContentProvider中获取到的游标。

猜你喜欢

转载自blog.csdn.net/woshiwangbiao/article/details/52575426