天天看点

Android Loader 异步加载数据注意

Loader的概念:

装载器从android3.0开始引进。它使得在activity或fragment中异步加载数据(数据库的数据,包括本地数据库,包括共享数据库)变得简单。

装载器具有如下特性:

  • 它们对每个Activity和Fragment都有效;
  • 他们提供了异步加载数据的能力;
  • 它拥有一个数据改变通知机制,当数据源做出改变时会及时通知。 也就是可以监听数据源,一旦数据源发生变化,Loader会感知这些变化;
  • 当Cursor 发生变化时,会自动加载数据,因此并不需要再重新进行数据查询。
  • android设计Loader的初衷是想让大家像CursorLoader的做法一样,通过loader去维护数据,每次启动loader时先检查有没有旧的数据并把旧的数据先deliver给用户,然后再考虑要不要重新加载新的数据。

装载器API概述:

Class/Interface 说明
LoaderManager

一个抽象类,关联到一个Activity或Fragment,管理一个或多个装载器的实例。这帮助一个应用管理那些与Activity或Fragment的生命周期相关的长时间运行的的操作。最常见的方式是与一个CursorLoader一起使用,然而应用是可以随便写它们自己的装载器以加载其它类型的数据。

每个activity或fragment只有一个LoaderManager。但是一个LoaderManager可以拥有多个装载器。

LoaderManager.LoaderCallbacks 一个用于客户端与LoaderManager交互的回调接口。例如,你使用回调方法onCreateLoader()来创建一个新的装载器。onCreate, onFinish,onRestart
Loader(装载器) 一个执行异步数据加载的抽象类。它是装载器的基类。你可以使用典型的CursorLoader,但是你也可以实现你自己的子类。一旦装载器被激活,它们将监视它们的数据源并且在数据改变时发送新的结果。
AsyncTaskLoader 提供一个AsyncTask来执行异步加载工作的抽象类。(可以处理任何的数据源,当然可以处理cursor)
CursorLoader AsyncTaskLoader的子类,它查询ContentResolver然后返回一个Cursor。这个类为查询cursor以标准的方式实现了装载器的协议,它的游标查询是通过AsyncTaskLoader在后台线程中执行,从而不会阻塞界面。使用这个装载器是从一个ContentProvider异步加载数据的最好方式。相比之下,通过fragment或activity的API来执行一个被管理的查询就不好了。

LoaderManager.LoaderCallbacks主要回调方法:

  1. onCreateLoader() :初始化并返回一个新的Loader

    当你试图去操作一个装载器时(比如,通过initLoader()),会检查是否指定ID的装载器已经存在.如果它不存在,将会触发LoaderManager.LoaderCallbacks 的方法onCreateLoader();

  2. onLoadFinished():当一个装载器完成了它的装载过程后被调用

    这个方法是在前面已创建的装载器已经完成其加载过程后被调用.这个方法保证会在应用到装载器上的数据被释放之前被调用;

  3. onLoaderReset() :当一个装载器被重置而其数据无效时被调用

    所谓Loader的重置,就是指Loader对象还保留,只是清除Loader中的数据,所以onLoaderReset()方法相当于Loader的销毁方法。因此在onLoaderReset()方法中会找到即将释放的数据的引用,并移除这些引用。移除引用后,GC才可以清除这些数据。

    当一个已创建的装载器被重置从而使其数据无效时,此方法被调用.此回调使你能发现什么时候数据将被释放。你可以释放对它的引用。

示例代码:

3.1 CursorLoader异步加载数据(联系人):

package com.noonecode.cursorloaderdemo;

import android.app.Activity;
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.widget.SimpleCursorAdapter;
import android.widget.ListView;

/**
 * 查询手机联系人,用CursorLoader
 * 
 * CursorLoader一般用于查询数据库,ContentProvider执行耗时操作时使用
 * 
 * @author NoOneCode
 *
 */
public class MainActivity extends Activity implements LoaderCallbacks<Cursor> {

    private ListView mLvShow;
    private SimpleCursorAdapter adapter;
    private LoaderManager loaderManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mLvShow = (ListView) findViewById(R.id.lv_show);

        adapter = new SimpleCursorAdapter(this, R.layout.contacts_item, null//
                , new String[] { "_id", "display_name" }//
                , new int[] { R.id.tv_id, R.id.tv_name }//
                , SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        mLvShow.setAdapter(adapter);
        // 获取LoaderManager
        loaderManager = getLoaderManager();
        loaderManager.initLoader(, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        if (id == ) {
            return new CursorLoader(this, ContactsContract.RawContacts.CONTENT_URI,
                    new String[] { "_id", "display_name" }, null, null, "_id asc");
        }
        return null;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        adapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        adapter.swapCursor(null);
    }
}
           

3.2 AsyncTaskLoader异步加载数据(联系人)

package com.noonecode.asynctaskloaderdemo;

import android.app.Activity;
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.AsyncTaskLoader;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;

public class MainActivity extends Activity implements LoaderCallbacks<Cursor> {

    private ListView mLvShow;
    private SimpleCursorAdapter adapter;
    private LoaderManager loaderManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mLvShow = (ListView) findViewById(R.id.lv_show);
        adapter = new SimpleCursorAdapter(this, R.layout.contacts_item, null//
                , new String[] { "_id", "display_name" }//
                , new int[] { R.id.tv_id, R.id.tv_name }//
                , SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        mLvShow.setAdapter(adapter);
        loaderManager = getLoaderManager();
        loaderManager.initLoader(, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new MyLoader(this);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        adapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        adapter.swapCursor(null);
    }


    public static class MyLoader extends AsyncTaskLoader<Cursor> {

        public MyLoader(Context context) {
            super(context);
        }

        @Override
        protected void onStartLoading() {
            super.onStartLoading();
            forceLoad();
        }

        @Override
        public Cursor loadInBackground() {
            ContentResolver resolver = getContext().getContentResolver();
            Cursor cursor = resolver.query(ContactsContract.RawContacts.CONTENT_URI,
                    new String[] { "_id", "display_name" }, null, null, null);
            return cursor;
        }

        @Override
        protected void onStopLoading() {
            super.onStopLoading();
        }
    }
}
           

注意

  • 本例主要讲解使用Loader异步加载数据过程,加载联系人其他信息不在本例研究范围之内,如有需求,请查看我的这篇文章 Android ContentResolver ContactsContract 获取手机联系人信息
  • 本例查询的手机联系人信息需要读取联系人的权限:

本例不复杂,有源码需求请留言!