• Loader异步装载器


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

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

            当成批显示数据的时候,为了使用户体验更好,需要进行异步装载。也就是说,让未显示数据的ListView等UI组件或控件先显示,避免出现白屏的尴尬现象,同时在后台下载数据,等下载完成后再更新ListView组件。这样尽管用户不会立刻看到数据,但是也不至于网络速度缓慢或服务器响应不及时而造成假死现象。


    装载器具有如下特性:

    • 它们对每个Activity和Fragment都有效;

    • 他们提供了异步加载数据的能力;

    • 它拥有一个数据改变通知机制,当数据源做出改变时会及时通知。 也就是可以监听数据源,一旦数据源发生变化,Loader会感知这些变化;

    • 当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 andFLAG_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() :当一个装载器被重置而其数据无效时被调用。
            所谓Loader的重置,就是指Loader对象还保留,只是清除Loader中的数据,所以onLoaderReset()方法相当于Loader的销毁方法。因此在onLoaderReset()方法中会找到即将释放的数据的引用,并移除这些引用。移除引用后,GC才可以清除这些数据。

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



    (四)、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

    (三)、示例代码:

    /**
     * 数据源:ContentProvider----》Sms 读取短信---》权限
     * 
     * ListVIew---》变化---》sv搜索内容的变化
     * */
    public class MainActivity extends Activity implements LoaderCallbacks<Cursor> {
    	private SearchView sv;
    	private ListView lv;
    
    	private SimpleCursorAdapter adapter;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    
    		sv = (SearchView) findViewById(R.id.sv);
    		// 添加文本内容改变监听
    		sv.setOnQueryTextListener(new OnQueryTextListener() {
    
    			@Override
    			public boolean onQueryTextSubmit(String query) {
    				return false;
    			}
    
    			// 文本内容改变过程中调用---》参数:最新的文本内容
    			@Override
    			public boolean onQueryTextChange(String newText) {
    				Bundle bundle =  new Bundle();
    				bundle.putString("newText", newText);
    
    				// 重启Loader--->参数2:传值
    				getLoaderManager().restartLoader(1, bundle, MainActivity.this);
    
    				return false;
    			}
    		});
    
    		lv = (ListView) findViewById(R.id.lv);
    		adapter = new SimpleCursorAdapter(MainActivity.this,
    				android.R.layout.simple_list_item_2, null, new String[] {
    						"address", "body" }, new int[] { android.R.id.text1,
    						android.R.id.text2 });
    		lv.setAdapter(adapter);
    
    		// 使用Loader数据加载---》实例化loader
    		// 参数1:实例化的Loader的id
    		// 参数2:传递参数onCreateLoader()方法的第二个参数
    		// 参数3:回调接口
    		getLoaderManager().initLoader(1, null, this);
    	}
    
    	// 创建Loader
    	@Override
    	public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
    		CursorLoader loader;
    
    		if (bundle != null) {// 有查询条件---》模糊匹配短信内容
    			// 条件值取出
    			String str = bundle.getString("newText");
    			//参数1:    参数2:Uri    参数3:要查询的列      参数4:查找条件    参数5:条件中的占位符的值    参数6:排序
    			loader = new CursorLoader(MainActivity.this,
    					Uri.parse("content://sms"), null, "body like '%" + str
    							+ "%'", null, null);
    		} else {
    			loader = new CursorLoader(MainActivity.this,
    					Uri.parse("content://sms"), null, null, null, null);
    		}
    		return loader;
    	}
    
    	// 数据加载完成---》参数2:加载到的新数据---》替换新数据
    	@Override
    	public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    		// 把Adapter中的数据替换为最新数据
    		adapter.swapCursor(data);
    	}
    
    	// Loader被重置的时候调用该方法---》释放最后加载的cursor
    	@Override
    	public void onLoaderReset(Loader<Cursor> loader) {
    		adapter.swapCursor(null);
    	}
    
    }
    

    三、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将使用默认顺序。

    (二)、示例代码:

    publicclass 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
    
    protectedvoid 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" }, newint[] {
    
    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");
    
    returnnew CursorLoader(this, uri, null, null, null, null);
    
    }
    
    
    
    
    @Override
    
    publicvoid onLoadFinished(Loader<Cursor> loader, Cursor data) {
    
    Log.i("Callbacks", "==onLoadFinished");
    
    adapter.swapCursor(data);
    
    }
    
    
    
    
    @Override
    
    publicvoid onLoaderReset(Loader<Cursor> loader) {
    
    Log.i("Callbacks", "==onLoaderReset");
    
    adapter.swapCursor(null);
    
    }
    
    
    
    
    @Override
    
    publicboolean onCreateOptionsMenu(Menu menu) {
    
    getMenuInflater().inflate(R.menu.main, menu);
    
    Log.i("MainActivity", "==onCreateOptionsMenu");
    
    returntrue;
    
    }
    
    
    
    }


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

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


    <LinearLayoutxmlns: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:


    <menuxmlns: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核心代码:


    publicclass MainActivity extends Activity implements LoaderCallbacks<Cursor> {

    private ListView listView_main;

    private TextView text_emptyinfo;




    private SimpleCursorAdapter adapter = null;

    private String uri_path;




    @Override

    protectedvoid 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" }, newint[] {

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

    }

    returnnew CursorLoader(this, baseUri, null, null, null, null);

    }




    @Override

    publicvoid onLoadFinished(Loader<Cursor> loader, Cursor data) {

    Log.i("Callbacks", "==onLoadFinished");

    adapter.swapCursor(data);

    }




    @Override

    publicvoid onLoaderReset(Loader<Cursor> loader) {

    Log.i("Callbacks", "==onLoaderReset");

    adapter.swapCursor(null);

    }




    @Override

    publicboolean 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

    publicboolean onQueryTextSubmit(String query) {

    returnfalse;

    }




    @Override

    publicboolean onQueryTextChange(String newText) {

    if (newText == null) {

    uri_path = null;

    } else {

    uri_path = newText;

    }

    // 更新搜索过滤,重新启动Loader进行新的查询.

    getLoaderManager().restartLoader(0, null, MainActivity.this);

    returntrue;

    }

    });




    returntrue;

    }

    }


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


    publicclass MyProvider extends ContentProvider {

    private MySQLiteOpenHelper dbHelper = null;

    private SQLiteDatabase db = null;

    privatestatic UriMatcher matcher = null;

    privatestatic 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

    publicboolean onCreate() {

    dbHelper = new MySQLiteOpenHelper(getContext());

    db = dbHelper.getReadableDatabase();

    returntrue;

    }




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

    }

    returnnull;

    }




    @Override

    public String getType(Uri uri) {

    returnnull;

    }




    @Override

    public Uri insert(Uri uri, ContentValues values) {

    returnnull;

    }




    @Override

    publicint delete(Uri uri, String selection, String[] selectionArgs) {

    return 0;

    }




    @Override

    publicint update(Uri uri, ContentValues values, String selection,

    String[] selectionArgs) {

    return 0;

    }

    }









  • 相关阅读:
    闭区间上的连续函数必定是一致连续的
    利用开区间覆盖的约简给出$\bf{Lindelöf}$覆盖定理的一个新证明
    $\mathbf{R}^n$中的紧集是闭有界集
    $\mathbf{R}^n$中的紧集是闭有界集
    陶哲轩实分析 习题10.2.7 导函数有界的函数一致连续
    $\mathbf{R}$上的离散点集是至多可数集
    利用开区间覆盖的约简给出$\bf{Lindelöf}$覆盖定理的一个新证明
    闭区间上的连续函数必定是一致连续的
    $\mathbf{R}$上的离散点集是至多可数集
    利用开区间覆盖的约简给出有限覆盖定理的一个新证明
  • 原文地址:https://www.cnblogs.com/merbn/p/4542242.html
Copyright © 2020-2023  润新知