Android应用Loaders全面详解及源码浅析

加载器

LoaderCursor

public class LoaderCursor extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);FragmentManager fm = getFragmentManager();// Create the list fragment and add it as our sole content.if (fm.findFragmentById(android.R.id.content) == null) {CursorLoaderListFragment list = new CursorLoaderListFragment();fm.beginTransaction().add(android.R.id.content, list).commit();}}public static class CursorLoaderListFragment extends ListFragmentimplements OnQueryTextListener, OnCloseListener,LoaderManager.LoaderCallbacks<Cursor> {// This is the Adapter being used to display the list's data.SimpleCursorAdapter mAdapter;// The SearchView for doing filtering.SearchView mSearchView;// If non-null, this is the current filter the user has provided.String mCurFilter;@Override public void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);// Give some text to display if there is no data.  In a real// application this would come from a resource.setEmptyText("No phone numbers");// We have a menu item to show in action bar.setHasOptionsMenu(true);// Create an empty adapter we will use to display the loaded data.mAdapter = new SimpleCursorAdapter(getActivity(),android.R.layout.simple_list_item_2, null,new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },new int[] { android.R.id.text1, android.R.id.text2 }, 0);setListAdapter(mAdapter);// Start out with a progress indicator.setListShown(false);// Prepare the loader.  Either re-connect with an existing one,// or start a new one.getLoaderManager().initLoader(0, null, this);}public static class MySearchView extends SearchView {public MySearchView(Context context) {super(context);}// The normal SearchView doesn't clear its search text when// collapsed, so we will do this for it.@Overridepublic void onActionViewCollapsed() {setQuery("", false);super.onActionViewCollapsed();}}@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {// Place an action bar item for searching.MenuItem item = menu.add("Search");item.setIcon(android.R.drawable.ic_menu_search);item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM| MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);mSearchView = new MySearchView(getActivity());mSearchView.setOnQueryTextListener(this);mSearchView.setOnCloseListener(this);mSearchView.setIconifiedByDefault(true);item.setActionView(mSearchView);}public boolean onQueryTextChange(String newText) {// Called when the action bar search text has changed.  Update// the search filter, and restart the loader to do a new query// with this filter.String newFilter = !TextUtils.isEmpty(newText) ? newText : null;// Don't do anything if the filter hasn't actually changed.// Prevents restarting the loader when restoring state.if (mCurFilter == null && newFilter == null) {return true;}if (mCurFilter != null && mCurFilter.equals(newFilter)) {return true;}mCurFilter = newFilter;getLoaderManager().restartLoader(0, null, this);return true;}@Override public boolean onQueryTextSubmit(String query) {// Don't care about this.return true;}@Overridepublic boolean onClose() {if (!TextUtils.isEmpty(mSearchView.getQuery())) {mSearchView.setQuery(null, true);}return true;}@Override public void onListItemClick(ListView l, View v, int position, long id) {// Insert desired behavior here.Log.i("FragmentComplexList", "Item clicked: " + id);}// These are the Contacts rows that we will retrieve.static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {Contacts._ID,Contacts.DISPLAY_NAME,Contacts.CONTACT_STATUS,Contacts.CONTACT_PRESENCE,Contacts.PHOTO_ID,Contacts.LOOKUP_KEY,};public Loader<Cursor> onCreateLoader(int id, Bundle args) {// This is called when a new Loader needs to be created.  This// sample only has one Loader, so we don't care about the ID.// First, pick the base URI to use depending on whether we are// currently filtering.Uri baseUri;if (mCurFilter != null) {baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,Uri.encode(mCurFilter));} else {baseUri = Contacts.CONTENT_URI;}// Now create and return a CursorLoader that will take care of// creating a Cursor for the data being displayed.String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("+ Contacts.DISPLAY_NAME + " != '' ))";return new CursorLoader(getActivity(), baseUri,CONTACTS_SUMMARY_PROJECTION, select, null,Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");}public void onLoadFinished(Loader<Cursor> loader, Cursor data) {// Swap the new cursor in.  (The framework will take care of closing the// old cursor once we return.)mAdapter.swapCursor(data);// The list should now be shown.if (isResumed()) {setListShown(true);} else {setListShownNoAnimation(true);}}public void onLoaderReset(Loader<Cursor> loader) {// This is called when the last Cursor provided to onLoadFinished()// above is about to be closed.  We need to make sure we are no// longer using it.mAdapter.swapCursor(null);}}}

LoaderThrottle

public class LoaderThrottle extends Activity {// Debugging.static final String TAG = "LoaderThrottle";/*** The authority we use to get to our sample provider.*/public static final String AUTHORITY = "com.example.android.apis.app.LoaderThrottle";/*** Definition of the contract for the main table of our provider.*/public static final class MainTable implements BaseColumns {// This class cannot be instantiatedprivate MainTable() {}/*** The table name offered by this provider*/public static final String TABLE_NAME = "main";/*** The content:// style URL for this table*/public static final Uri CONTENT_URI =  Uri.parse("content://" + AUTHORITY + "/main");/*** The content URI base for a single row of data. Callers must* append a numeric row id to this Uri to retrieve a row*/public static final Uri CONTENT_ID_URI_BASE= Uri.parse("content://" + AUTHORITY + "/main/");/*** The MIME type of {@link #CONTENT_URI}.*/public static final String CONTENT_TYPE= "vnd.android.cursor.dir/vnd.example.api-demos-throttle";/*** The MIME type of a {@link #CONTENT_URI} sub-directory of a single row.*/public static final String CONTENT_ITEM_TYPE= "vnd.android.cursor.item/vnd.example.api-demos-throttle";/*** The default sort order for this table*/public static final String DEFAULT_SORT_ORDER = "data COLLATE LOCALIZED ASC";/*** Column name for the single column holding our data.* <P>Type: TEXT</P>*/public static final String COLUMN_NAME_DATA = "data";}/*** This class helps open, create, and upgrade the database file.*/static class DatabaseHelper extends SQLiteOpenHelper {private static final String DATABASE_NAME = "loader_throttle.db";private static final int DATABASE_VERSION = 2;DatabaseHelper(Context context) {// calls the super constructor, requesting the default cursor factory.super(context, DATABASE_NAME, null, DATABASE_VERSION);}/**** Creates the underlying database with table name and column names taken from the* NotePad class.*/@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("CREATE TABLE " + MainTable.TABLE_NAME + " ("+ MainTable._ID + " INTEGER PRIMARY KEY,"+ MainTable.COLUMN_NAME_DATA + " TEXT"+ ");");}/**** Demonstrates that the provider must consider what happens when the* underlying datastore is changed. In this sample, the database is upgraded the database* by destroying the existing data.* A real application should upgrade the database in place.*/@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// Logs that the database is being upgradedLog.w(TAG, "Upgrading database from version " + oldVersion + " to "+ newVersion + ", which will destroy all old data");// Kills the table and existing datadb.execSQL("DROP TABLE IF EXISTS notes");// Recreates the database with a new versiononCreate(db);}}/*** A very simple implementation of a content provider.*/public static class SimpleProvider extends ContentProvider {// A projection map used to select columns from the databaseprivate final HashMap<String, String> mNotesProjectionMap;// Uri matcher to decode incoming URIs.private final UriMatcher mUriMatcher;// The incoming URI matches the main table URI patternprivate static final int MAIN = 1;// The incoming URI matches the main table row ID URI patternprivate static final int MAIN_ID = 2;// Handle to a new DatabaseHelper.private DatabaseHelper mOpenHelper;/*** Global provider initialization.*/public SimpleProvider() {// Create and initialize URI matcher.mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);mUriMatcher.addURI(AUTHORITY, MainTable.TABLE_NAME, MAIN);mUriMatcher.addURI(AUTHORITY, MainTable.TABLE_NAME + "/#", MAIN_ID);// Create and initialize projection map for all columns.  This is// simply an identity mapping.mNotesProjectionMap = new HashMap<String, String>();mNotesProjectionMap.put(MainTable._ID, MainTable._ID);mNotesProjectionMap.put(MainTable.COLUMN_NAME_DATA, MainTable.COLUMN_NAME_DATA);}/*** Perform provider creation.*/@Overridepublic boolean onCreate() {mOpenHelper = new DatabaseHelper(getContext());// Assumes that any failures will be reported by a thrown exception.return true;}/*** Handle incoming queries.*/@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {// Constructs a new query builder and sets its table nameSQLiteQueryBuilder qb = new SQLiteQueryBuilder();qb.setTables(MainTable.TABLE_NAME);switch (mUriMatcher.match(uri)) {case MAIN:// If the incoming URI is for main table.qb.setProjectionMap(mNotesProjectionMap);break;case MAIN_ID:// The incoming URI is for a single row.qb.setProjectionMap(mNotesProjectionMap);qb.appendWhere(MainTable._ID + "=?");selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs,new String[] { uri.getLastPathSegment() });break;default:throw new IllegalArgumentException("Unknown URI " + uri);}if (TextUtils.isEmpty(sortOrder)) {sortOrder = MainTable.DEFAULT_SORT_ORDER;}SQLiteDatabase db = mOpenHelper.getReadableDatabase();Cursor c = qb.query(db, projection, selection, selectionArgs,null /* no group */, null /* no filter */, sortOrder);c.setNotificationUri(getContext().getContentResolver(), uri);return c;}/*** Return the MIME type for an known URI in the provider.*/@Overridepublic String getType(Uri uri) {switch (mUriMatcher.match(uri)) {case MAIN:return MainTable.CONTENT_TYPE;case MAIN_ID:return MainTable.CONTENT_ITEM_TYPE;default:throw new IllegalArgumentException("Unknown URI " + uri);}}/*** Handler inserting new data.*/@Overridepublic Uri insert(Uri uri, ContentValues initialValues) {if (mUriMatcher.match(uri) != MAIN) {// Can only insert into to main URI.throw new IllegalArgumentException("Unknown URI " + uri);}ContentValues values;if (initialValues != null) {values = new ContentValues(initialValues);} else {values = new ContentValues();}if (values.containsKey(MainTable.COLUMN_NAME_DATA) == false) {values.put(MainTable.COLUMN_NAME_DATA, "");}SQLiteDatabase db = mOpenHelper.getWritableDatabase();long rowId = db.insert(MainTable.TABLE_NAME, null, values);// If the insert succeeded, the row ID exists.if (rowId > 0) {Uri noteUri = ContentUris.withAppendedId(MainTable.CONTENT_ID_URI_BASE, rowId);getContext().getContentResolver().notifyChange(noteUri, null);return noteUri;}throw new SQLException("Failed to insert row into " + uri);}/*** Handle deleting data.*/@Overridepublic int delete(Uri uri, String where, String[] whereArgs) {SQLiteDatabase db = mOpenHelper.getWritableDatabase();String finalWhere;int count;switch (mUriMatcher.match(uri)) {case MAIN:// If URI is main table, delete uses incoming where clause and args.count = db.delete(MainTable.TABLE_NAME, where, whereArgs);break;// If the incoming URI matches a single note ID, does the delete based on the// incoming data, but modifies the where clause to restrict it to the// particular note ID.case MAIN_ID:// If URI is for a particular row ID, delete is based on incoming// data but modified to restrict to the given ID.finalWhere = DatabaseUtils.concatenateWhere(MainTable._ID + " = " + ContentUris.parseId(uri), where);count = db.delete(MainTable.TABLE_NAME, finalWhere, whereArgs);break;default:throw new IllegalArgumentException("Unknown URI " + uri);}getContext().getContentResolver().notifyChange(uri, null);return count;}/*** Handle updating data.*/@Overridepublic int update(Uri uri, ContentValues values, String where, String[] whereArgs) {SQLiteDatabase db = mOpenHelper.getWritableDatabase();int count;String finalWhere;switch (mUriMatcher.match(uri)) {case MAIN:// If URI is main table, update uses incoming where clause and args.count = db.update(MainTable.TABLE_NAME, values, where, whereArgs);break;case MAIN_ID:// If URI is for a particular row ID, update is based on incoming// data but modified to restrict to the given ID.finalWhere = DatabaseUtils.concatenateWhere(MainTable._ID + " = " + ContentUris.parseId(uri), where);count = db.update(MainTable.TABLE_NAME, values, finalWhere, whereArgs);break;default:throw new IllegalArgumentException("Unknown URI " + uri);}getContext().getContentResolver().notifyChange(uri, null);return count;}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);FragmentManager fm = getFragmentManager();// Create the list fragment and add it as our sole content.if (fm.findFragmentById(android.R.id.content) == null) {ThrottledLoaderListFragment list = new ThrottledLoaderListFragment();fm.beginTransaction().add(android.R.id.content, list).commit();}}public static class ThrottledLoaderListFragment extends ListFragmentimplements LoaderManager.LoaderCallbacks<Cursor> {// Menu identifiersstatic final int POPULATE_ID = Menu.FIRST;static final int CLEAR_ID = Menu.FIRST+1;// This is the Adapter being used to display the list's data.SimpleCursorAdapter mAdapter;// If non-null, this is the current filter the user has provided.String mCurFilter;// Task we have running to populate the database.AsyncTask<Void, Void, Void> mPopulatingTask;@Override public void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);setEmptyText("No data.  Select 'Populate' to fill with data from Z to A at a rate of 4 per second.");setHasOptionsMenu(true);// Create an empty adapter we will use to display the loaded data.mAdapter = new SimpleCursorAdapter(getActivity(),android.R.layout.simple_list_item_1, null,new String[] { MainTable.COLUMN_NAME_DATA },new int[] { android.R.id.text1 }, 0);setListAdapter(mAdapter);// Start out with a progress indicator.setListShown(false);// Prepare the loader.  Either re-connect with an existing one,// or start a new one.getLoaderManager().initLoader(0, null, this);}@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {menu.add(Menu.NONE, POPULATE_ID, 0, "Populate").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);menu.add(Menu.NONE, CLEAR_ID, 0, "Clear").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);}@Override public boolean onOptionsItemSelected(MenuItem item) {final ContentResolver cr = getActivity().getContentResolver();switch (item.getItemId()) {case POPULATE_ID:if (mPopulatingTask != null) {mPopulatingTask.cancel(false);}mPopulatingTask = new AsyncTask<Void, Void, Void>() {@Override protected Void doInBackground(Void... params) {for (char c='Z'; c>='A'; c--) {if (isCancelled()) {break;}StringBuilder builder = new StringBuilder("Data ");builder.append(c);ContentValues values = new ContentValues();values.put(MainTable.COLUMN_NAME_DATA, builder.toString());cr.insert(MainTable.CONTENT_URI, values);// Wait a bit between each insert.try {Thread.sleep(250);} catch (InterruptedException e) {}}return null;}};mPopulatingTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);return true;case CLEAR_ID:if (mPopulatingTask != null) {mPopulatingTask.cancel(false);mPopulatingTask = null;}AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {@Override protected Void doInBackground(Void... params) {cr.delete(MainTable.CONTENT_URI, null, null);return null;}};task.execute((Void[])null);return true;default:return super.onOptionsItemSelected(item);}}@Override public void onListItemClick(ListView l, View v, int position, long id) {// Insert desired behavior here.Log.i(TAG, "Item clicked: " + id);}// These are the rows that we will retrieve.static final String[] PROJECTION = new String[] {MainTable._ID,MainTable.COLUMN_NAME_DATA,};public Loader<Cursor> onCreateLoader(int id, Bundle args) {CursorLoader cl = new CursorLoader(getActivity(), MainTable.CONTENT_URI,PROJECTION, null, null, null);cl.setUpdateThrottle(2000); // update at most every 2 seconds.return cl;}public void onLoadFinished(Loader<Cursor> loader, Cursor data) {mAdapter.swapCursor(data);// The list should now be shown.if (isResumed()) {setListShown(true);} else {setListShownNoAnimation(true);}}public void onLoaderReset(Loader<Cursor> loader) {mAdapter.swapCursor(null);}}
}

Android Loader机制相关推荐

  1. android loader使用教程,Android Loader 机制,让你的数据加载更加轻松

    前言 在 Android 中,任何耗时的操作都不能放在 UI 线程中,所以耗时的操作都需要使用异步加载来实现.其实,加载耗时数据的常用方式其实也挺多的,就让我们来看一下 1.Thread + Hand ...

  2. Android Loader机制全面详解及源码浅析

    原文出处:csdn@工匠若水,http://blog.csdn.net/yanbober/article/details/48861457 一.概述 在Android中任何耗时的操作都不能放在UI主线 ...

  3. Android Loader 异步加载详解二:探寻Loader内部机制

    Android Loader 异步加载详解二:探寻Loader内部机制 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/7025991 ...

  4. 【Java 虚拟机原理】Android 类加载机制 ( 双亲委派机制 | BootClassLoader | PathClassLoader | DexClassLoader )

    文章目录 一.Android 类加载机制 二.双亲委派机制 一.Android 类加载机制 Android 中的类加载 使用了 双亲委派 机制 , 如下图所示 : 在 Android 中提供了 333 ...

  5. Android Loader 异步加载详解一:基础概念

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/70241844 本文出自[赵彦军的博客] Android Loader 异步加载详解 ...

  6. android的kotlin通讯录,Android Loader的使用以及手机通讯录的获取方法

    Android的主线程中执行长时间操作,导致界面无响应,会引起ANR.如果需要执行较长时间的操作,一般会在另一个线程处理,然后将数据转交给主线程进行显示,Android本身为我们提供了一些机制处理这种 ...

  7. Android消息机制Handler用法

    这篇文章介绍了Android消息机制Handler用法总结,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 1.简述 Handler消息机制主要包括: Messa ...

  8. 【腾讯Bugly干货分享】经典随机Crash之二:Android消息机制

    为什么80%的码农都做不了架构师?>>>    本文作者:鲁可--腾讯SNG专项测试组 测试工程师 背景 承上经典随机Crash之一:线程安全 问题的模型 好几次灰度top1.top ...

  9. Android刷新机制-View绘制原理

    Android刷新机制-View绘制原理 Android刷新机制-SurfaceFlinger原理 Android刷新机制-Choreographer原理 一.概述 本文将从startActivity ...

最新文章

  1. [JS]题解 | #岛屿数量#
  2. SpringBoot + Vue + nginx项目一起部署
  3. 设计企业网站大纲_哈尔滨企业网站设计费用,网站开发公司_华阳网络
  4. 虚拟列表控件---加载大数据行
  5. 华为云welink考试试题_华为内部开启WeLink项目,华为云是这样考虑的-通信/网络-与非网...
  6. SLAM 学习与开发经验分享
  7. 事件监听机制——鼠标事件MouseEvent
  8. mtk android手机 代码问题,MTK用户必备 Android手机也能系统恢复
  9. LeetCode(303)——区域和检索 - 数组不可变(JavaScript)
  10. [转载] 的士速递4
  11. fiddler https
  12. 常用的字符串对象方法
  13. 统计英文、数字、空格以及其他字符的数量,go语言
  14. 结构体、文件操作、指针
  15. 〖Linux〗以后台方式启动/结束指定程序/命令(不受 exit 或点击窗口关闭按钮等终端退出操作的影响)...
  16. 运行 Excel 脚本
  17. 免费直播编码软件应用技巧
  18. 计算机课玩游戏检讨书,上微机课玩游戏检讨书.doc
  19. 微信程序开发系列教程(二)微信订阅号+人工智能问答服务
  20. 电脑突然开机无反应,怎么办

热门文章

  1. asp.net使用httphandler打包多CSS或JS文件以加快页面加载速度
  2. 虚拟机生命周期八招巧管理
  3. 嵌入式入门要知道的五个小窍门-心得
  4. mongo中的游标与数据一致性的取舍
  5. MongoDB学习之路(三)
  6. cocos2x (c++/lua) spine 文件的预加载
  7. 使用 Visual Studio Online 进行协同开发
  8. HttpWebRequest WebResponse 对象简单了解
  9. 转]网络上收集的Visual Studio 2008的一些小技巧
  10. js 正则判断字符串是否为字母或数字