詳細講解Android中使用LoaderManager加載數(shù)據(jù)的方法
Android的設計之中,任何耗時的操作都不能放在UI主線程之中。所以類似于網絡操作等等耗時的操作都需要使用異步的實現(xiàn)。而在ContentProvider之中,也有可能存在耗時的操作(當查詢的數(shù)據(jù)量很大的時候),這個時候我們也需要使用異步的調用來完成數(shù)據(jù)的查詢。
當使用異步的query的時候,我們就需要使用LoaderManager了。使用LoaderManager就可以在不阻塞UI主線程的情況下完成數(shù)據(jù)的加載。
(1)獲取loaderManger:activity.getLoaderManager()
(2)loaderManager的事件回調接口, LoaderManager.LoaderCallbacks<D>
下面是一個demo,從contentprovider中query數(shù)據(jù)添加到listview中,是異步執(zhí)行的。
MySQLiteOpenHeleper.java:
package com.app.loadermanager; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; public class MySQLiteOpenHelper extends SQLiteOpenHelper { public static final String db_name = "test.db3"; public static final int version = 1; public MySQLiteOpenHelper(Context context) { super(context, db_name, null, version); } @Override public void onCreate(SQLiteDatabase db) { String create_sql = "create table tb_student(_id integer primary key autoincrement,name varchar(20),age integer)"; db.execSQL(create_sql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
MyContentProvider.java
package com.app.loadermanager; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; public class MyContentProvider extends ContentProvider { private MySQLiteOpenHelper helper = null; private static final UriMatcher matcher = new UriMatcher( UriMatcher.NO_MATCH); private static final int students = 1; static { matcher.addURI("com.app.contentprovider", "tb_student", students); } @Override public int delete(Uri arg0, String arg1, String[] arg2) { return 0; } @Override public String getType(Uri arg0) { return null; } @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = helper.getWritableDatabase(); int flag = matcher.match(uri); switch (flag) { case students: long id = db.insert("tb_student", null, values); return ContentUris.withAppendedId(uri, id); } return null; } @Override public boolean onCreate() { helper = new MySQLiteOpenHelper(this.getContext()); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase db = helper.getWritableDatabase(); Cursor cursor=db.query("tb_student", projection, selection, selectionArgs, null, null, null); return cursor; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } }
MainActivity.java:
package com.app.loadermanager; import java.util.ArrayList; import android.annotation.SuppressLint; import android.app.Activity; import android.app.LoaderManager; import android.content.CursorLoader; import android.content.Loader; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; @SuppressLint("NewApi") public class MainActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> { LoaderManager manager = null; ListView listView = null; @SuppressLint("NewApi") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = (ListView) this.findViewById(R.id.listview); manager = this.getLoaderManager(); manager.initLoader(1000, null, this); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle bundle) { CursorLoader loader = new CursorLoader(this, Uri.parse("content://com.app.contentprovider"), null, null, null, null); return loader; } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { ArrayList<String> al = new ArrayList<String>(); while (cursor.moveToNext()) { String name = cursor.getString(cursor.getColumnIndex("name")); al.add(name); } ArrayAdapter adapter=new ArrayAdapter(this,android.R.layout.simple_list_item_1,al); listView.setAdapter(adapter); adapter.notifyDataSetChanged(); } @Override public void onLoaderReset(Loader<Cursor> loader) { } }
LoaderManager與生命周期
Activity和Fragment都擁有getLoaderManager的方法,其實Fragment的getLoaderManager去獲取的就是Activity所管理的眾多LoaderManager之一。
1.Who標簽
先來看一下Activity的getLoaderManager方法:
LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) { if (mAllLoaderManagers == null) { mAllLoaderManagers = new ArrayMap<String, LoaderManagerImpl>(); } LoaderManagerImpl lm = mAllLoaderManagers.get(who); if (lm == null) { if (create) { lm = new LoaderManagerImpl(who, this, started); mAllLoaderManagers.put(who, lm); } } else { lm.updateActivity(this); } return lm; }
mAllLoaderManagers保存著一個Activity所擁有的所有LoaderManager,其key為String類型的who變量。若從Activity調用getLoaderManager,那么所得LoaderManager的who標簽為(root):
public LoaderManager getLoaderManager() { if (mLoaderManager != null) { return mLoaderManager; } mCheckedForLoaderManager = true; mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true); return mLoaderManager; }
若從Fragment中使用getLoaderManager,則所得LoaderManager的who標簽會根據(jù)Fragment的層級不同而不同,在Activity中處于最頂級的Fragment的who標簽為:
android:fragment:X
X為序號。
而屬于Fragment的Fragment所活的的LoaderManager who標簽就成為了:
android:fragment:X:Y
甚至更大的深度。
2.LoaderManager狀態(tài)量mLoadersStarted
在開篇分析的時候分析過LoaderManager內部保存了一個mStarted狀態(tài),很多操作會根據(jù)這個狀態(tài)做不同處理。通過上邊的分析也能看出,F(xiàn)ragment中獲取LoaderManager最終是通過Activity獲取的,在LoaderManager構造時,就將一個狀態(tài)量mLoadersStarted傳遞了進去,這個狀態(tài)量交給LoaderManager自行管理。
而無論是Fragment還是Activity,都有mLoadersStarted這樣一個狀態(tài)量,在onStart生命周期后為true,onStop后為false。所以在onStart生命周期后做initLoader操作就會使Loader一經初始化就開始運行了。
3.Fragment和Activity的生命周期
Fragment和Activity能夠對LoaderManager產生影響的生命周期是一樣的。
onStart
系統(tǒng)在onStart階段會獲取LoaderManager,如果成功獲取,則調用LoaderManager的doStart(),這里需要特別說明的是,雖然所有LoaderManager都是保存在Activity中,但是在Activity的onStart生命周期其也只是會獲取屬于自己的(root)標簽LoaderManager,而并不是將所有保存在mAllLoaderManagers里的Manager全部遍歷一遍。
onStop(performStop)
處于onStop生命周期,但是系統(tǒng)內部是通過performStop調用的。在這里,同樣會獲取屬于自己的LoaderManager,如果Activity是因為配置改變出發(fā)的onStop(旋轉屏幕),則調用LoaderManager的doRetain()接口,如果不是,就調用LoaderManager的doStop()接口。
onDestroy(performDestroy)
調用LoaderManager的doDestroy()接口銷毀LoaderManager。
4.LoaderManager的生命周期
因為LoaderManager與Fragment/Activity的生命周期緊密相連,所以想要用好LoaderManager就必須了解其自身的生命周期,這樣就能把握數(shù)據(jù)的完整變化規(guī)律了。
正常的從出生到銷毀:
doStart() -> doReportStart() -> doStop() -> doDestroy()
Activity配置發(fā)生變化:
doStart() -> doRetain() -> finishRetain() -> doReportStart() -> doStart() -> doStop() -> doDestroy()
Fragment在onDestroyView()之后還會執(zhí)行LoaderManager的doReportNextStart(), 即:
doStart() -> doRetain() -> doReportNextStart() -> finishRetain() -> doReportStart() -> doStart() -> doStop() -> doDestroy()
doStart()會將LoaderManager中保存的所有Loader都啟動。最終是運行每一個Loader的onStartLoading()方法。只要是通過initLoader使用過的Loader都會記錄在LoaderManager的mLoaders中,那么問題來了:
怎樣在Fragment/Activity不銷毀的前提下從LoaderManager中移除某個使用過的Loader呢?
答案就是使用LoaderManager的接口去除指定ID的Loader:
public void destroyLoader(int id)
這樣就能在mLoaders中移除掉了,下次onStart的時候就沒有這個Loader什么事了。
doReportStart()。如果Fragment上一次在銷毀并重做,而且數(shù)據(jù)有效的話會在這里主動上報數(shù)據(jù),最終走到callback的onLoadFinished中。
doStop()會停止mLoaders保存的所有Loader。最終是運行每一個Loader的onStopLoading()方法。
doDestroy()會清空所有有效和無效Loader,LoaderManager中不再存在任何Loader。
doRetain()會將LoaderManager的mRetaining狀態(tài)置位true,并且保存retain時LoaderInfo的mStarted狀態(tài)
finishRetain()如果之前所保存的mStarted與現(xiàn)在的不一樣而且新的狀態(tài)是停止的話,就停止掉這個Loader。否則若有數(shù)據(jù)并且不是要下次再上報(沒有call doReportNextStart)的話就上報給callback的onLoadFinished。
doReportNextStart(),根據(jù)第6條,已經能夠理解了。當Fragment執(zhí)行到onDestroyView生命周期時,對自己的LoaderManager發(fā)出請求:即使現(xiàn)在有數(shù)據(jù)也不要進行上報,等我重做再到onStart生命周期時再給我。
- Android 開發(fā) 使用WebUploader解決安卓微信瀏覽器上傳圖片中遇到的bug
- Android利用CursorLoader實現(xiàn)短信驗證碼自動填寫
- Android ImageLoader第三方框架解析
- Android開發(fā)中類加載器DexClassLoader的簡單使用講解
- 全面解析Android的開源圖片框架Universal-Image-Loader
- Android開發(fā)之ImageLoader本地緩存
- Android Universal ImageLoader 緩存圖片
- 從源代碼分析Android Universal ImageLoader的緩存處理機制
- Android開發(fā)之ImageLoader使用詳解
- android CursorLoader用法介紹
- Android Loader詳細介紹及實例代碼
相關文章
Flutter StatefulBuilder實現(xiàn)局部刷新實例詳解
這篇文章主要為大家介紹了Flutter StatefulBuilder實現(xiàn)局部刷新實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-08-08loadavg數(shù)據(jù)異常引發(fā)問題起源分析
這篇文章主要為大家介紹了loadavg數(shù)據(jù)異常引發(fā)問題起源分析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11Android實現(xiàn)類似iOS風格的對話框實例代碼
通過本文給大家分享一個簡單的常用的對話框類,關于Android實現(xiàn)類似iOS風格的對話框實例代碼大家通過本文學習下吧2017-09-09Android自定義TextView仿微信朋友圈文字展開全文功能
這篇文章主要為大家詳細介紹了Android自定義TextView仿微信朋友圈文字展開全文功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-06-06Android使用DrawerLayout實現(xiàn)側滑菜單效果
這篇文章主要為大家詳細介紹了Android使用DrawerLayout實現(xiàn)側滑菜單效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08