`
zhangyf1987hb
  • 浏览: 83491 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Loader 详解

阅读更多

Loaders

Introduced in Android 3.0, loaders make it easy to asynchronously load data 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.

Loader API Summary


There are multiple classes and interfaces that may be involved in using loaders in an application. They are summarized in this table:

Class/Interface Description
LoaderManager An abstract class associated with an Activity or Fragment for managing one or more Loader instances. This helps an application manage longer-running operations in conjunction with the Activity or Fragment lifecycle; the most common use of this is with a CursorLoader, however applications are free to write their own loaders for loading other types of data. 

There is only one LoaderManager per activity or fragment. But aLoaderManager can have multiple loaders.
LoaderManager.LoaderCallbacks A callback interface for a client to interact with theLoaderManager. For example, you use the onCreateLoader()callback method to create a new loader.
Loader An abstract class that performs asynchronous loading of data. This is the base class for a loader. You would typically useCursorLoader, but you can implement your own subclass. While loaders are active they should monitor the source of their data and deliver new results when the contents change.
AsyncTaskLoader Abstract loader that provides an AsyncTask to do the work.
CursorLoader A subclass of AsyncTaskLoader that queries theContentResolver 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. Using this loader is the best way to asynchronously load data from a ContentProvider, instead of performing a managed query through the fragment or activity's APIs.

The classes and interfaces in the above table are the essential components you'll use to implement a loader in your application. You won't need all of them for each loader you create, but you'll always need a reference to theLoaderManager in order to initialize a loader and an implementation of a Loader class such as CursorLoader. The following sections show you how to use these classes and interfaces in an application.

Using Loaders in an Application


This section describes how to use loaders in an Android application. An application that uses loaders typically includes the following:

Starting a Loader

The LoaderManager manages one or more Loader instances within an Activity or Fragment. There is only one LoaderManager per activity or fragment.

You typically initialize a Loader within the activity's onCreate() method, or within the fragment'sonActivityCreated() method. You do this as follows:

// 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 ID that identifies the loader. In this example, the ID is 0.
  • Optional arguments to supply to the loader at construction (null in this example).
  • 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.

The initLoader() call ensures that a loader is initialized and active. It has two possible outcomes:

In either case, the given LoaderManager.LoaderCallbacks implementation is associated with the loader, and will be called when the loader state changes. If at the point of this call the caller is in its started state, and the requested loader already exists and has generated its data, then the system calls onLoadFinished()immediately (during initLoader()), so you must be prepared for this to happen. See onLoadFinished for more discussion of this callback

Note that the initLoader() method returns the Loader that is created, but you don't need to capture a reference to it. The LoaderManager manages the life of the loader automatically. The LoaderManager starts and stops loading when necessary, and maintains the state of the loader and its associated content. As this implies, you rarely interact with loaders directly (though for an example of using loader methods to fine-tune a loader's behavior, see the LoaderThrottle sample). You most commonly use theLoaderManager.LoaderCallbacks methods to intervene in the loading process when particular events occur. For more discussion of this topic, see Using the LoaderManager Callbacks.

Restarting a Loader

When you use initLoader(), as shown above, it uses an existing loader with the specified ID if there is one. If there isn't, it creates one. But sometimes you want to discard your old data and start over.

To discard your old data, you use restartLoader(). For example, this implementation ofSearchView.OnQueryTextListener restarts the loader when the user's query changes. The loader needs to be restarted so that it can use the revised search filter to do a new query:

publicboolean onQueryTextChanged(String newText){// Called when the action bar search text has changed.  Update// the search filter, and restart the loader to do a new query// with this filter.
    mCurFilter =!TextUtils.isEmpty(newText)? newText :null;
    getLoaderManager().restartLoader(0,null,this);returntrue;}

Using the LoaderManager Callbacks

LoaderManager.LoaderCallbacks is a callback interface that lets a client interact with the LoaderManager.

Loaders, in particular CursorLoader, are expected to retain their data after being stopped. This allows applications to keep their data across the activity or fragment's onStop() and onStart() methods, so that when users return to an application, they don't have to wait for the data to reload. You use theLoaderManager.LoaderCallbacks methods when to know when to create a new loader, and to tell the application when it is time to stop using a loader's data.

LoaderManager.LoaderCallbacks includes these methods:

  • onLoadFinished() — Called when a previously created loader has finished its load.
  • onLoaderReset() — Called when a previously created loader is being reset, thus making its data unavailable.

These methods are described in more detail in the following sections.

onCreateLoader

When you attempt to access a loader (for example, through initLoader()), it checks to see whether the loader specified by the ID exists. If it doesn't, it triggers the LoaderManager.LoaderCallbacks methodonCreateLoader(). This is where you create a new loader. Typically this will be a CursorLoader, but you can implement your own Loader subclass.

In this example, the onCreateLoader() callback method creates a CursorLoader. You must build theCursorLoader using its constructor method, which requires the complete set of information needed to perform a query to the ContentProvider. Specifically, it needs:

  • uri — The URI for the content to retrieve.
  • projection — A list of which columns to return. Passing null will return 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 will return all rows for the given URI.
  • selectionArgs — You may include ?s in the selection, which will be replaced by the values from selectionArgs, in the order that they appear in the selection. The values will be bound as Strings.
  • sortOrder — How to order the rows, formatted as an SQL ORDER BY clause (excluding the ORDER BY itself). Passing null will use the default sort order, which may be unordered.

For example:

// If non-null, this is the current filter the user has provided.

String mCurFilter;...publicLoader<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.
// First, pick the base URI to use depending on whether we are
// currently filtering.

Uri baseUri;
    if(mCurFilter !=null){
        baseUri =Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                  Uri.encode(mCurFilter));
    }else{
        baseUri =Contacts.CONTENT_URI;
    }

    // Now create and return a CursorLoader that will take care of
    // creating a Cursor for the data being displayed.
    Stringselect="(("+Contacts.DISPLAY_NAME +" NOTNULL) AND ("
            +Contacts.HAS_PHONE_NUMBER +"=1) AND ("
            +Contacts.DISPLAY_NAME +" != '' ))";
    returnnewCursorLoader(getActivity(), baseUri,
            CONTACTS_SUMMARY_PROJECTION,select,null,
            Contacts.DISPLAY_NAME +" COLLATE LOCALIZED ASC");}

onLoadFinished

This method is called when a previously created loader has finished its load. This method is guaranteed to be called prior to the release of the last data that was supplied for this loader. At this point you should remove all use of the old data (since it will be released soon), but should not do your own release of the data since its loader owns it and will take care of that.

The loader will release the data once it knows the application is no longer using it. For example, if the data is a cursor from a CursorLoader, you should not call close() on it yourself. If the cursor is being placed in aCursorAdapter, you should use the swapCursor() method so that the old Cursor is not closed. For example:

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;

...

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

}

onLoaderReset

This method is called when a previously created loader is being reset, thus making its data unavailable. This callback lets you find out when the data is about to be released so you can remove your reference to it.  

This implementation calls swapCursor() with a value of null:

// This is the Adapter being used to display the list's data.

SimpleCursorAdapter mAdapter;
...
publicvoid 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);

}

Example


As an example, here is the full implementation of a Fragment that displays a ListView containing the results of a query against the contacts content provider. It uses a CursorLoader to manage the query on the provider.

For an application to access a user's contacts, as shown in this example, its manifest must include the permission READ_CONTACTS.

publicstaticclassCursorLoaderListFragmentextendsListFragment
        implementsOnQueryTextListener,LoaderManager.LoaderCallbacks<Cursor>{// This is the Adapter being used to display the list's data.
    SimpleCursorAdapter mAdapter;

    // If non-null, this is the current filter the user has provided.
    String mCurFilter;

    @Overridepublicvoid 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");

        // We have a menu item to show in action bar.
        setHasOptionsMenu(true);

        // Create an empty adapter we will use to display the loaded data.
        mAdapter =newSimpleCursorAdapter(getActivity(),
                android.R.layout.simple_list_item_2,null,
                newString[]{Contacts.DISPLAY_NAME,Contacts.CONTACT_STATUS },
                newint[]{ 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);
    }

    @Overridepublicvoid onCreateOptionsMenu(Menu menu,MenuInflater inflater){
        // Place an action bar item for searching.
        MenuItem item = menu.add("Search");
        item.setIcon(android.R.drawable.ic_menu_search);
        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
        SearchView sv =newSearchView(getActivity());
        sv.setOnQueryTextListener(this);
        item.setActionView(sv);
    }

    publicboolean onQueryTextChange(String newText){
        // Called when the action bar search text has changed.  Update
        // the search filter, and restart the loader to do a new query
        // with this filter.
        mCurFilter =!TextUtils.isEmpty(newText)? newText :null;
        getLoaderManager().restartLoader(0,null,this);
        returntrue;
    }

    @Overridepublicboolean onQueryTextSubmit(String query){
        // Don't care about this.
        returntrue;
    }

    @Overridepublicvoid onListItemClick(ListView l,View v,int position,long id){
        // Insert desired behavior here.
        Log.i("FragmentComplexList","Item clicked: "+ id);
    }

    // These are the Contacts rows that we will retrieve.
    staticfinalString[] CONTACTS_SUMMARY_PROJECTION =newString[]{
        Contacts._ID,
        Contacts.DISPLAY_NAME,
        Contacts.CONTACT_STATUS,
        Contacts.CONTACT_PRESENCE,
        Contacts.PHOTO_ID,
        Contacts.LOOKUP_KEY,
    };
    publicLoader<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.
        // First, pick the base URI to use depending on whether we are
        // currently filtering.
        Uri baseUri;
        if(mCurFilter !=null){
            baseUri =Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                    Uri.encode(mCurFilter));
        }else{
            baseUri =Contacts.CONTENT_URI;
        }

        // Now create and return a CursorLoader that will take care of
        // creating a Cursor for the data being displayed.
        Stringselect="(("+Contacts.DISPLAY_NAME +" NOTNULL) AND ("
                +Contacts.HAS_PHONE_NUMBER +"=1) AND ("
                +Contacts.DISPLAY_NAME +" != '' ))";
        returnnewCursorLoader(getActivity(), baseUri,
                CONTACTS_SUMMARY_PROJECTION,select,null,
                Contacts.DISPLAY_NAME +" COLLATE LOCALIZED ASC");
    }

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

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

More Examples

There are a few different samples in ApiDemos that illustrate how to use loaders:

  • LoaderCursor — A complete version of the snippet shown above.
  • LoaderThrottle — An example of how to use throttling to reduce the number of queries a content provider does when its data changes.

For information on downloading and installing the SDK samples, see Getting the Samples.

分享到:
评论

相关推荐

    sqlloader详解

    ### SQL*Loader详解 #### 一、SQL*Loader概述 SQL*Loader是Oracle数据库系统中一个强大而灵活的工具,其主要功能是从各种外部数据源批量导入数据到Oracle数据库。随着现代数据库规模的不断扩大,从GB级扩展到TB级...

    Boot_Loader 详解,非常详细

    ### Boot_Loader 详解 #### 一、BootLoader概述 BootLoader是计算机系统启动过程中的首个运行程序,它负责初始化硬件设备、建立内存空间映射,并为操作系统内核准备一个合适的运行环境。对于嵌入式系统而言,...

    sql loader详解

    SQL*LOADER是oracle的数据加载工具,在NT下sql*loader的命令为SQLLDR,在UNIX下一般为SQLLDR/SQLLOAD。通常用来将操作系统文件迁移到oracle数据库中。它是大型数据仓库选择使用的加载方法,因为它提供了最快速的途径...

    webpack的pitching loader详解

    如果某个 pitching loader 在其 `pitch` 方法中返回了一个结果,那么剩余的 loader 就会被跳过,转而直接调用更左边的 loader。这种设计允许 pitching loader 在早期阶段决定是否需要继续执行后续的 loader,从而...

    Lean Loader.rar

    《Unity中的Lean Loader详解》 在Unity游戏开发中,资源管理是至关重要的环节,而Lean Loader就是这样一个专门针对Unity资源加载优化的工具。它旨在提供高效、灵活且易用的解决方案,帮助开发者优化游戏性能,减少...

    LoaderRunner参数详解

    LoaderRunner 参数详解 LoaderRunner 是一款功能强大且灵活的性能测试工具,能够模拟大量用户同时访问系统,从而对系统的性能和可扩展性进行评估。在本文中,我们将详细讲解 LoaderRunner 的参数配置和使用方法。 ...

    Android-Universal-Image-Loader-master

    《Android Universal Image Loader详解:高效异步加载与缓存机制》 在移动应用开发中,尤其是在Android平台上,图片加载和管理是一项重要的任务。高效的图片加载不仅能够提升用户体验,还能优化应用性能,避免内存...

    DSP Executable Loader

    ### DSP Executable Loader详解 #### 一、概述 在深入探讨DSP Executable Loader之前,我们需要先了解几个关键概念。DSP(Digital Signal Processor)即数字信号处理器,是一种专门用于执行与信号处理相关的算法的...

    oracle sql loader 用法详解

    ### Oracle SQL Loader 用法详解 Oracle SQL Loader是一款强大的数据加载工具,被广泛应用于将大量数据从操作系统文件导入到Oracle数据库中。对于大型数据仓库项目,SQL Loader因其提供的高速加载方式(如Direct ...

    salseforce data loader

    ### Salesforce Data Loader详解 #### 数据加载器概述 Salesforce Data Loader 是一款专为 Salesforce 用户设计的强大工具,主要用于数据的大批量导入或导出操作。这款工具适用于 Salesforce 的 Enterprise、...

    sqlloader数据导入详解

    ### SQL*Loader 数据导入详解 #### 一、SQL*Loader简介 SQL*Loader 是 Oracle 数据库下用于数据导入的一种重要工具,它由 Oracle 客户端提供。此工具支持多种数据格式,能够高效地将外部文件中的数据加载到 Oracle ...

    前端开源库-vision-style-loader

    **Vision Style Loader详解** 1. **Webpack插件:** Vision Style Loader 是一个基于Webpack的加载器,它的主要任务是在Webpack构建过程中处理CSS和其他视觉相关的样式文件。Webpack加载器允许开发者自定义处理不同...

    Image-Loader

    《Android Universal Image Loader详解》 Android Universal Image Loader (简称UIL) 是一个强大的开源库,专为Android应用设计,用于在UI线程之外加载、缓存和显示网络图像。这个库被广泛应用于各种需要动态加载...

    Android-Universal-Image-Loader-master.zip

    《Android Universal Image Loader详解与应用》 在Android开发中,Bitmap对象是图像处理的核心,它在内存中的占用量巨大,不妥善管理很容易导致应用程序出现内存溢出(OutOfMemoryError)的问题。为此,开发者社区...

    Android-Universal-Image-Loader

    《Android Universal Image Loader详解》 在Android应用开发中,图片加载是一个常见的需求,尤其是在处理大量图片或者网络图片时,高效、稳定且具有缓存机制的图片加载库显得尤为重要。"Android-Universal-Image-...

Global site tag (gtag.js) - Google Analytics