Android中的数据加载机制-Loader(一)

Loader介绍

Loader 这个东西,平常开发中很少见人使用,但是假如看系统应用的源码,会发现好多系统应用都用到了Loader。这篇文章简单介绍一下Android中的Loader。

Loader 从 android3.0 开始引进。它使得在 activity 或 fragment 中异步加载数据变得
简单。Loader具有如下特性:

1、它们对每个 Activity 和 Fragment 都有效。
2、他们提供了异步加载数据的能力。
3、它们监视数据源的一举一动并在内容改变时传送新的结果。
4、当由于配置改变而被重新创建后,它们自动重连到上一个加载器的游标,所以不必
重新查询数据。

Loader 的数据源可以是磁盘、数据库、ContentProvider、网络或者另一个进程。
Loader 可以在不阻塞主线程的情况下获取并发送结果数据给接受者。(异步)

Loader 技术为我们提供的核心类有:
LoaderManager:用来对 Loader 进行管理,一个 Activity 或者 Fragment 只能有一个
LoaderManager。
LoaderManager.LoaderCallbacks:用于同 LoaderManager 进行交互,可以在其中创建
Loader 对象。
AsyncTaskLoader:抽象类,可以进行异步加载数据的 Loader,貌似内部也是通过
AsynTask 实现的,可以通过继承它构建自己的 Loader,也可以使用现有的子类,例如异步
查询数据库可以使用 CursorLoader。

CursorLoader 的用法

1、CursorLoader 的使用场景
比如来了一条短信,短信数据库改变了,用户没有离开短信列表界面的这种情况,应该
实时更新新到的短信数据到 UI 界面。

2、CursorLoader 的用法

第一步:得到 LoaderManager 对象
可以通过 Activity 或者的 Fragment 的 getLoaderManager()方法得到 LoaderManager,
LoaderManager lm = getLoaderManager();

第二步:初始化 Loader
lm .initLoader(intid, Bundle args, LoaderManager.LoaderCallbacks callback);

参数说明:
id:用来标识 Loader 的 ID,一个 Activity 或者 Fragment 只能有一个 LoaderManager,
但可以有多个 Loader,通过 ID 区分。在新建 Loader 时,如果发现已经有相同 ID 的 Loader
就会复用该 Loader,而不会重新创建。
args:传给新建 Loader 的参数,可以在回调接口对象里面的 onCreateLoader 方法中接
受到该参数,如果不需要可以为 null
Callback:回调接口。被 LoaderManager 调用,用来报告监听 Loader 事件。
^实现(LoaderManager.LoaderCallBacks中的方法):

    // 返回一个 Loader 对象.
    // id:Loader 被创建时,用户分配的 Id 标识;LoaderManager.initLoader 参数一。如
    // 果发现已经有相同 ID 的 Loader 就会复用该 Loader,而不会重新创建。
    // args:接收从 LoaderManager.initLoader 传递的参数二。
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    
    
        Log.e(TAG, "onCreateLoader id=" + id);
        //返回一个 Loader。我们先接触 Android 封装好的 CursorLoader。很简单。一句话搞定
        return new CursorLoader(MainActivity.this, Uri.parse("content://sms"), null, null,
                null, null);
    }

    // 如果数据库有数据更新,并且数据加载完毕会调用该方法。(如果用户跳转到其他界面,再回到当当前界面的时候,如果数据库有数据更新,该方法会在 OnResume 之后调用)
    // loader:来自于 onCreateLoader()方法返回值.
    // data:创建的 loader 加载数据的结果.
    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    
    
        //替换适配器的游标。
        simpleCursorAdapter.swapCursor(data);
        Log.e(TAG, "onLoadFinished ");
    }


    // 当一个已创建的 Loader 被重置从而使其数据无效时,此方法被调用。
    //当 Activity 或 Fragment 被销毁的时候调用。
    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
    
    
        simpleCursorAdapter.swapCursor(null);//释放 Cursor 资源
        Log.e(TAG, "onLoaderReset ");
    }

`
^ restartLoader(int id,Bundle args,LoaderCallbacks callbacks)
使用 restartLoader(intid, Bundle args, LoaderManager.LoaderCallbacks callback)
方法进行数据更新,和初始化一个,如果有相同 id 的 Loader 存在,会复用 Loader,并清空原有 Loader 中的数据,如果没有就新建一个。这个方法一般使用在需要更新数据时,例如下面例子中,在搜索关键改变时,需要调用这个方法,从新异步查询数据。
下面一个例子使用 CursorLoader 查询系统短信联系人以及短信内容,并显示在
ListView 中,在 activity_main 的 ListView 上放置了一个 SearchView 可以根据联系人姓
名关键字查询联系人。

例子

具体实现代码:

public class MainActivity extends AppCompatActivity {
    
    
    private SearchView searchView;
    private static ContentResolver contentResolver;
    private ListView lv_show;
    private SimpleCursorAdapter simpleCursorAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        searchView = (SearchView) findViewById(R.id.searchView);
        lv_show = (ListView) findViewById(R.id.lv_show);
        simpleCursorAdapter = new SimpleCursorAdapter(this, R.layout.view_list_item, null
                , new String[]{
    
    Telephony.Sms.ADDRESS, Telephony.Sms.BODY}
                , new int[]{
    
    R.id.tv_address, R.id.tv_body},
                SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        lv_show.setAdapter(simpleCursorAdapter);
        //监听 SearchView 的文本查询监听
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
    
    
            //文本提交的时候
            @Override
            public boolean onQueryTextSubmit(String query) {
    
    
                return true;//ture 自己处理。false 自己处完成后丢给系统处理
            }

            //文本改变的时候调用
            @Override
            public boolean onQueryTextChange(String newText) {
    
    
                //重新加载 Loader
                Bundle bundle = new Bundle();
                bundle.putString("keyword", newText);
                getLoaderManager().restartLoader(1, bundle, callbacks);
                return true;//ture 自己处理。false 自己处完成后丢给系统处理
            }
        });
        contentResolver = getContentResolver();
        //得到 LoaderManager,初始化 Loader
        getLoaderManager().initLoader(1, null, callbacks);
    }

    private LoaderManager.LoaderCallbacks<Cursor> callbacks = new LoaderManager.LoaderCallbacks<Cursor>() {
    
    
        @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    
    
            if (args == null)
                return new CursorLoader(MainActivity.this, Uri.parse("content://sms"), null, null,
                        null, null);
            else {
    
    
                String keyword = args.getString("keyword");
                //包含 keyword 关键字的联系人都查询出来
                return new CursorLoader(MainActivity.this, Uri.parse("content://sms"), null,
                        Telephony.Sms.ADDRESS + " like ?",
                        /
                        new String[]{
    
    "%" + keyword + "%"}
					,null);
            }
        }

        @Override
        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    
    
            //数据更新
            simpleCursorAdapter.swapCursor(data);
        }

        @Override
        public void onLoaderReset(Loader<Cursor> loader) {
    
    
            //释放 Cursor 资源
            simpleCursorAdapter.swapCursor(null);
        }
    };
}

MainActivity 的布局代码:
activity_main.xml

<?xml version="1.0"encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.let.loader.MainActivity">```

    <SearchView
        android:id="@+id/searchView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#acb5bc"
        android:iconifiedByDefault="true"
        android:queryHint="查询的关键字" />

    <ListView
        android:id="@+id/lv_show"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

ListView 的 Item 布局代码
view_list_item.xml

<?xml version="1.0"encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/tv_address"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center" />

    <TextView
        android:id="@+id/tv_body"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center" />
</LinearLayout>	

下面看看效果图:

AsyncTaskLoader 加载本地数据
1 AsyncTaskLoader 与 CursorLoader 对比
AsyncTaskLoader 是一个抽象的 Loader。他使用 AsyncTask 将数据加载任务转移到其他
线程上处理。几乎我们创建的有用的 Loader 类都是 AsyncTaskLoader 的子类,如
CursorLoader。
2 AsyncTaskLoader 的用法
第一步:创建一个静态内部类 继承与 AsyncTaskLoader。注:必须是静态内部类
第二步:子类必须重写 onStartLoading();并且调用 forceLoad();方法,表示强制加载
数据。否则数据显示不出来。
第三步:在 loadInBackground()方法 执行数据库查询返回一个 Cursor;
注意:这里一定要对 Cursor 对象注册一个内容观察者,否则数据库数据改变 Cursor
数据不会更新。

//cursor.registerContentObserver(mObserver);ForceLoadContentObserver

而且这个观察者(ForceLoadContentObserver)对象只能在 AsyncTaskLoader 子类构造
方法被创建,可以参照安卓封装好的 CursorAdapter 写法…
之后:其他使用和 CursorLoader 大同小异。

猜你喜欢

转载自blog.csdn.net/qq_36162336/article/details/128508234
今日推荐