android Loader——装载器


android Loader——装载器

一、Loader简介:
(一)、Loader的概念:

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

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

(二)、装载器API概述:

        在使用装载器时,会涉及很多类和接口们,在下表中对它们总结一下:

Class/Interface

说明

LoaderManager

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

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

LoaderManager.LoaderCallbacks

一个用于客户端与LoaderManager交互的回调接口。例如,你使用回调方法onCreateLoader()来创建一个新的装载器。

Loader(装载器)

一个执行异步数据加载的抽象类。它是加载器的基类。你可以使用典型的CursorLoader,但是你也可以实现你自己的子类。一旦装载器被激活,它们将监视它们的数据源并且在数据改变时发送新的结果

AsyncTaskLoader

提供一个AsyncTask来执行异步加载工作的抽象类。

CursorLoader

AsyncTaskLoader的子类,它查询ContentResolver然后返回一个Cursor。这个类为查询cursor以标准的方式实现了装载器的协议,它的游标查询是通过AsyncTaskLoader在后台线程中执行,从而不会阻塞界面。使用这个装载器是从一个ContentProvider异步加载数据的最好方式。相比之下,通过fragmentactivityAPI来执行一个被管理的查询就不行了。


(三)、类目录结构:

1、API11中开始加入Loader:

java.lang.Object

   ↳ android.content.Loader<D>

      ↳ android.content.AsyncTaskLoader<D> 


子类:

java.lang.Object

   ↳ android.content.Loader<D>

      ↳ android.content.AsyncTaskLoader<D>

         ↳ android.content.CursorLoader


2、为了兼容1.6以下版本:

java.lang.Object

   ↳ android.support.v4.content.Loader<D>

      ↳ android.support.v4.content.AsyncTaskLoader<D>


        子类:

java.lang.Object

   ↳ android.support.v4.content.Loader<D>

      ↳ android.support.v4.content.AsyncTaskLoader<D>

         ↳ android.support.v4.content.CursorLoader 


二、AsyncTaskLoader实例:

(一)、AsyncTaskLoader 实现数据加载的步骤:

1、窗体Activity要实现LoaderManager.LoaderCallbacks<Cursor>接口。至于继承于Activity还是FragmentActivity要看是否需要支持3.0以下版本。如果需要兼容则继承于FragmentActivity。

2、创建LoaderManager对象:通过getLoaderManager()或getSupportLoaderManager()方法来实现。如果是继承于FragmentActivity类,则使用getSupportLoaderManager()方法来创建LoaderManger对象,否则使用前者创建即可;

3、初始化LoaderManager对象:调用initLoader()方法来初始化;

  • initLoader()方法有以下参数:
    •     一个唯一ID来标志装载器;
    •     可选的参数,用于装载器初始化时;
    •     一个LoaderManager.LoaderCallbacks的实现。被LoaderManager调用以报告装载器的事件。一般窗体都实现了这个接口,所以传的是它自己:this;
    initLoader()保证一个装载器被初始化并激活.它具有两种可能的结果:
  • 如果ID所指的装载器已经存在,那么这个装载器将被重用;
  • 如果装载器不存在,initLoader()就触发LoaderManager.LoaderCallbacks中的回调方法onCreateLoader()。这是实例化并返回一个新Loader的地方。
4、操作ListView控件对象:先findViewById(),然后setAdapter();
【备注:】此时必须使用SimpleCursorAdater适配器,而构建适配器的时候,第三个参数Cursor设置为null,最后一个参数:flags必须是:CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER。


5、自定义Loader,作为onCreateLoader()的返回值(也就是说onCreateLoader()方法必须返回自定义Loader的实例);

    • 自定义Loader要继承于AsyncTaskLoader<Cursor>;
    • 必须要有构造方法;
    • 必须重写onStartLoading()、loadInBackground() 、deliverResult()。而且要在onStartLoading中调用forceLoad()才能依次调用下一个即将执行的方法。
    • 在loadInBackground()方法中执行数据库查询,返回Cursor;
    • 在deliverResult()方法中执行跟适配器交换数据的操作。adapter.swapCursor(data)。

【知识回顾:】SimpleCursorAdapter构造方法的参数讲解。
方法原型:new SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to , int flags) ;

参数:
1)、Context context, 这个与 SimpleListItemFactory 相关的 ListView 所处运行上下文(context)。   也就是这个 ListView 所在的 Activity。
2)、int layout, 显示 list item 的 布局文件。这个 layout 文件中至少要包含在 "to" 参数中命名的 views。
3)、Cursor c, 数据库的光标( Cursor )。如果 cursor 无效,则该参数可以为 null
4)、String[] from, 指定 column 中的哪些列的数据将绑定(显示)到 UI 中。如果 cursor 无效, 则该参数可为 null。
5)、int[] to, 指定用于显示 "from" 参数指定的数据列表的 views。 这些 views 必须都是 TextViews。 "from" 参数的前 N 个值(valus)和 "to" 参数的前 N 个 views 是一一对应的关系。如果 cursor 无效,则该参数可为 null。
6)、flags,用于定义适配器行为的标志位

Flags used to determine the behavior of the adapter; may be any combination of FLAG_AUTO_REQUERY and FLAG_REGISTER_CONTENT_OBSERVER。
FLAG_AUTO_REQUERY(常量值:1 )从 API11 开始已经废弃。因为他会在应用程序的 UI 线程中执行游标查询操作, 导致响应缓慢甚至应用程序无响应(ANR)的错误。作为替代方案,请使用 LoaderManager 和 AsyncTaskLoaderCursorLoader。
如果设置FLAG_REGISTER_CONTENT_OBSERVER(常量值:2),适配器会在Cursor上注册一个内容观测器,当通知到达时会调用 onContentChanged() 方法。
      
(二)、LoaderManager.LoaderCallbacks主要回调方法:

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

2、onLoadFinished():当一个装载器完成了它的装载过程后被调用;
这个方法是在前面已创建的装载器已经完成其加载过程后被调用.这个方法保证会在应用到装载器上的数据被释放之前被调用;

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

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



(四)、AsyncTaskLoader中各个方法的执行顺序:
04-01 04:00:25.477: MainActivity: ==onCreate
04-01 04:00:25.701: LoaderCallbacks: ==onCreateLoader
04-01 04:00:25.705: AsyncTaskLoader: ==onStartLoading
04-01 04:00:25.709: AsyncTaskLoader: ==loadInBackground
04-01 04:00:25.721: AsyncTaskLoader: ==deliverResult
04-01 04:00:25.721: LoaderCallbacks: ==onLoadFinished
04-01 04:00:25.749: MainActivity: ==onStart
04-01 04:00:25.749: MainActivity: ==onResume
04-01 04:00:25.973: MainActivity: ==onCreateOptionsMenu



(五)、当另一个app修改了同一个数据源(如:共同使用的SDCard上的数据库)后    AsyncTaskLoader中各个方法的执行顺序:

04-01 04:01:06.693: MainActivity: ==onPause
04-01 04:01:08.413: MainActivity: ==onStop
04-01 04:01:15.721: AsyncTaskLoader: ==onStartLoading
04-01 04:01:15.721: AsyncTaskLoader: ==loadInBackground
04-01 04:01:15.721: AsyncTaskLoader: ==deliverResult
04-01 04:01:15.721: LoaderCallbacks: ==onLoadFinished
04-01 04:01:15.757: MainActivity: ==onStart
04-01 04:01:15.757: MainActivity: ==onResume


(三)、示例代码:


public class MainActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor>{
        private ListView listView_main;
        private LoaderManager manager;
        private static SimpleCursorAdapter adapter;


@Override
        protected void onCreate(Bundle arg0) {
                super.onCreate(arg0);
                setContentView(R.layout.activity_main);


                listView_main = (ListView) findViewById(R.id.listView_main);

                manager = getSupportLoaderManager();
                manager.initLoader(0, null, this);

                adapter = new SimpleCursorAdapter(this, R.layout.item_listview_main,
                                null, new String[] { "_id", "title" }, new int[] {
R.id.text_item_title, R.id.text_item_content }, 2);
       
 listView_main.setAdapter(adapter);
     }


        @Override
        public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
            Log.i ("Activity", "===onCreateLoader");
            return new MyLoader(this);
        }

        @Override
        public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
            Log.i ("Activity", "===onLoaderFinished");
            adapter.swapCursor(arg1);
        }

       @Override
        public void onLoaderReset(Loader<Cursor> arg0) {
            Log.i ("Activity", "===onLoaderReset");
        }

        static class MyLoader extends AsyncTaskLoader<Cursor> {
                private Context context;
                public MyLoader(Context context) {
                        super(context);
                        this.context = context;
                }

       @Override
                protected void onStartLoading() {
                        super.onStartLoading();
                Log.i ("MyLoader", "===onStartLoading");
                        forceLoad();
                }

               @Override
                public Cursor loadInBackground() {
                        Log.i ("MyLoader", "===loadInBackground");
                        MySQLiteDatabaseHelper dbHelper = new MySQLiteDatabaseHelper();
                        Cursor cursor = dbHelper.selectCursor("select id as _id , title from android_info limit 0,20",
null);


                        deliverResult(cursor);
                        return cursor;
                }

               @Override
                public void deliverResult(Cursor data) {
                        Log.i ("MyLoader", "===deliverResult");
                        super.deliverResult(data);
                        adapter.swapCursor(data);
                }
        }
}

三、CursorLoader实例:

(一)、CursorLoader 实现数据加载的步骤:

1、窗体Activity要实现LoaderManager.LoaderCallbacks<Cursor>接口。至于继承于Activity还是FragmentActivity要看是否需要支持3.0以下版本。如果需要兼容则继承于FragmentActivity。

2、创建LoaderManager对象:通过getLoaderManager()或getSupportLoaderManager()方法来实现。如果是继承于FragmentActivity类,则使用getSupportLoaderManager()方法来创建LoaderManger对象,否则使用前者创建即可;

3、初始化LoaderManager对象:调用initLoader()方法来初始化;

    - initLoader()方法有以下参数:
        -     一个唯一ID来标志装载器
        -     可选的参数,用于装载器初始化时
        -     一个LoaderManager.LoaderCallbacks的实现。被LoaderManager调用以报告装载器的事件。一般窗体都实现了这个接口,所以传的是它自己:this;

    - initLoader()保证一个装载器被初始化并激活.它具有两种可能的结果:
        - 如果ID所指的装载器已经存在,那么这个装载器将被重用;
        - 如果装载器不存在,initLoader()就触发LoaderManager.LoaderCallbacks中的回调方法
onCreateLoader()。这是实例化并返回一个新Loader的地方。

4、操作ListView控件对象:先findViewById(),然后setAdapter();
【备注:】此时必须使用SimpleCursorAdater适配器,而构建适配器的时候,第三个参数Cursor设置为null,最后一个参数:flags必须是:CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER。

5、重写onCreateLoader()方法。返回CursorLoader的构造方法。
例如: return new CursorLoader(this, uri, null, null, null, null);

【备注:】new CursorLoader()的参数:

1、 uri —要获取的内容的URI;

2、projection —要返回的列组成的数组。传入null 将会返回所有的列,但这样会导致低效;

3、selection —表明哪些行将被返回,相当于SQL语句中的WHERE条件 (不包括WHERE关键词)。传入null 将返回所有的行;

4、selectionArgs —Where语句中的'?’组成的数组。

5、sortOrder —如何排序,相当于SQL语句中的 ORDER BY 语句(不包括ORDER BY关键词)。传入null将使用默认顺序。


(二)、示例代码:


public class MainActivity extends Activity implements LoaderCallbacks<Cursor> {
        private ListView listView_main;
        private Uri uri = Uri.parse ("content://com.steven.mywordsprovider/tb_words");
        private SimpleCursorAdapter adapter = null;

       @Override
        protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                Log.i ("MainActivity", "==onCreate");

                listView_main = (ListView) findViewById(R.id.listView_main);
                adapter = new SimpleCursorAdapter(this, R.layout.item_listview, null,
                                new String[] { "words", "detail" }, new int[] {
                                             R.id.text_item_listview_id, R.id.text_item_listview_title },
                             CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);

                listView_main.setAdapter(adapter);

                LoaderManager loaderManager = getLoaderManager();
                loaderManager.initLoader(2, null, this);
        }

       @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
                Log.i ("Callbacks", "==onCreateLoader");
                return new CursorLoader(this, uri, null, null, null, null);
        }

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

       @Override
        public void onLoaderReset(Loader<Cursor> loader) {
        Log.i ("Callbacks", "==onLoaderReset");
                adapter.swapCursor(null);
        }
}


四、CursorLoader结合ActionBar上的SearchView实现过滤搜索:

(一)、主窗体布局核心代码:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

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

    <TextView
        android:id="@+id/text_emptyinfo"
        android:gravity="center"
        android:textSize="24sp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="没有该词汇!" />


</LinearLayout>


(二)、Menu菜单的xml:

<menu xmlns:android="http://schemas.android.com/apk/res/android" >   
    <item
        android:id="@+id/searchView_actionBar"
        android:showAsAction="always"
        android:actionViewClass="android.widget.SearchView"
        android:title="搜索"/>
</menu>

(三)、主窗体Java核心代码:


public class MainActivity extends Activity implements LoaderCallbacks<Cursor> {
        private ListView listView_main;
        private TextView text_emptyinfo;
        private SimpleCursorAdapter adapter = null;
        private String uri_path;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                Log.i ("MainActivity", "==onCreate");

                listView_main = (ListView) findViewById(R.id.listView_main);
                text_emptyinfo = (TextView) findViewById(R.id.text_emptyinfo);

                adapter = new SimpleCursorAdapter(this, R.layout.item_listview, null,
                                new String[] { "words", "detail" }, new int[] {R.id.text_item_listview_id,
                                R.id.text_item_listview_title },CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);

                listView_main.setAdapter(adapter);
                listView_main.setEmptyView(text_emptyinfo);

                LoaderManager loaderManager = getLoaderManager();
                loaderManager.initLoader(2, null, this);
        }

        @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
                Log.i ("Callbacks", "==onCreateLoader");
                Uri baseUri = Uri.parse ("content://com.steven.mywordsprovider/tb_words");

                if (uri_path != null) {
                        baseUri = Uri.withAppendedPath (baseUri, uri_path);
                }
                return new CursorLoader(this, baseUri, null, null, null, null);
        }

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

        @Override
        public void onLoaderReset(Loader<Cursor> loader) {
                Log.i ("Callbacks", "==onLoaderReset");
                adapter.swapCursor(null);
        }

       @Override
        public boolean onCreateOptionsMenu(Menu menu) {
                getMenuInflater().inflate(R.menu.main, menu);
                SearchView searchView_actionBar = (SearchView) menu.findItem(
                                R.id.searchView_actionBar).getActionView();

                searchView_actionBar.setOnQueryTextListener(new OnQueryTextListener() {
                       @Override
                        public boolean onQueryTextSubmit(String query) {
                               return false;
                        }

                       @Override
                        public boolean onQueryTextChange(String newText) {
                                if (newText == null) {
                                        uri_path = null;
                                } else {
                                        uri_path = newText;
                                }


                                // 更新搜索过滤,重新启动Loader进行新的查询.
                                getLoaderManager().restartLoader(0, null, MainActivity.this);
                                return true;
                        }
                });
           return true;
        }
}


(四)、ContentProvider中的核心代码:


public class MyProvider extends ContentProvider {
        private MySQLiteOpenHelper dbHelper = null;
        private SQLiteDatabase db = null;
        private static UriMatcher matcher = null;
        private static String AUTHORITY = "com.steven.mywordsprovider";

static {
                matcher = new UriMatcher(UriMatcher.NO_MATCH);
                matcher.addURI(AUTHORITY, "tb_words", 1);
                matcher.addURI(AUTHORITY, "tb_words/*", 2);
                matcher.addURI(AUTHORITY, "tb_words/#", 3);
        }

       @Override
        public boolean onCreate() {
                dbHelper = new MySQLiteOpenHelper(getContext());
                db = dbHelper.getReadableDatabase();
                return true;
        }

@Override
        public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {
                switch (matcher.match(uri)) {
                case 1:
                        Cursor cursor = db.query("tb_words", projection, selection,selectionArgs, null, null, sortOrder)
                        return cursor;
                case 2:
                        String search_str = uri.getPathSegments().get(1);
                        if (selection == null) {
                                selection = "words like '" + search_str + "%'";
                        } else {
                                selection += " and words like '" + search_str + "'%";
                        }


                        Cursor cursor2 = db.query("tb_words", projection, selection,
selectionArgs, null, null, sortOrder);
                        return cursor2;
                default:
                   break;
                }
           return null;
        }

        @Override
        public String getType(Uri uri) {
                return null;
        }

        @Override
        public Uri insert(Uri uri, ContentValues values) {
        return null;
        }

       @Override
        public int delete(Uri uri, String selection, String[] selectionArgs) {
                return 0;
        }

       @Override
        public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {
               return 0;
        }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章