Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 高級開發 >> LoaderManager - Android 3.0新特性

LoaderManager - Android 3.0新特性

編輯:高級開發

 在android 3.0中提供了一個新概念Loaders,通過LoaderManager類可以很輕松的異步加載數據從Fragment或Activity中,Loaders提供了回調機制通知最終的運行結果,有點類似AsyncTask類,但由於Loader對於並發可以用過Loader管理器統一管理,所以更適合批量處理多個異步任務的處理(當然內部仍然是多線程)。下面就讓Android123一起和大家看下honeycomb中的新特性吧,對於解決多重異步I/O加快android平板應用的運行是十分有效的。

一、LoaderManager

LoaderManager類位於android.app.LoaderManager,提供了以下幾個方法

abstract void destroyLoader(int id) //停止並移除loader通過ID
abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) //打印LoaderManager的狀態到一個流中
static void enableDebugLogging(boolean enabled) //啟用debug記錄
abstract <D> Loader<D> getLoader(int id) //返回找到的ID或沒有匹配的在Loader中
abstract <D> Loader<D> initLoader(int id, Bundle args, LoaderCallbacks<D> callback) //初始化Loader使其成為活動狀態
abstract <D> Loader<D> restartLoader(int id, Bundle args, LoaderCallbacks<D> callback) //啟動一個新的或重啟一個存在的Loader在管理器中

同時LoaderManager還有一個回調接口android.app.LoaderManager.LoaderCallbacks<D> 用於和LoaderManager交互

abstract Loader<D> onCreateLoader(int id, Bundle args) //舉例並返回一個新Loader通過ID
abstract void onLoadFinished(Loader<D> loader, D data) //當前面一個Loader已經完成時回調
abstract void onLoaderReset(Loader<D> loader) //當一個新的loader或存在的loader重啟時回調

二、Loader

Loader類位於android.content.Loader<D>,整體比較復雜,主要成員有

1. 構造方法 Loader(Context context) //作為唯一實例化方法參數只有一個Context

2. Public Methods

void abandon() //高速Loader他在綁定
String dataToString(D data) //用於調試,轉換一個Loader數據類的實例為字符串用於打印
void deliverResult(D data) //發送一個load注冊的listener結果
void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) //打印loader狀態通過給定的流
void forceLoad() //強制一個異步載入
Context getContext() //返回Context實例
int getId()
boolean isAbandoned() //判斷是否已經綁定
boolean isReset() //判斷是否已經重啟
boolean isStarted() //判斷是否已經執行
void onContentChanged() //內容變化回調

registerListener(int id, OnLoadCompleteListener<D> listener)
void reset() //重置一個Loader的狀態
final void startLoading() //啟動一個異步的載入從Loader的數據
void stopLoading() //停止載入
boolean takeContentChanged() String toString()
void unregisterListener(OnLoadCompleteListener<D> listener)

提供的子類 android.content.Loader.ForceLoadContentObserver 和 接口 android.content.Loader.OnLoadCompleteListener<D>

為了更清晰的表達android開發網給出一個SDK例子完整代碼,來作分析

public class LoaderThrottle extends Activity {
static final String TAG = "LoaderThrottle";

public static final String AUTHORITY = "com.example.android.apis.app.LoaderThrottle";

public static final class MainTable implements BaseColumns {

// This class cannot be instantiated
private MainTable() {}

public static final String TABLE_NAME = "main";

public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/main");

public static final Uri CONTENT_ID_URI_BASE
= Uri.parse("content://" + AUTHORITY + "/main/");

public static final String CONTENT_TYPE
= "vnd.android.cursor.dir/vnd.example.api-demos-throttle";

public static final String CONTENT_ITEM_TYPE
= "vnd.android.cursor.item/vnd.example.api-demos-throttle";
public static final String DEFAULT_SORT_ORDER = "data COLLATE LOCALIZED ASC";

public static final String COLUMN_NAME_DATA = "data";
}

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

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + MainTable.TABLE_NAME + " ("
+ MainTable._ID + " INTEGER PRIMARY KEY,"
+ MainTable.COLUMN_NAME_DATA + " TEXT"
+ ");");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

// Logs that the database is being upgraded
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");

// Kills the table and existing data
db.execSQL("DROP TABLE IF EXISTS notes");

// Recreates the database with a new version
onCreate(db);
}
}

public static class SimpleProvider extends ContentProvider {
// A projection map used to select columns from the database
private final HashMap<String, String> mNotesProjectionMap;
// Uri matcher to decode incoming URIs.
private final UriMatcher mUriMatcher;

// The incoming URI matches the main table URI pattern
private static final int MAIN = 1;
// The incoming URI matches the main table row ID URI pattern
private static final int MAIN_ID = 2;

// Handle to a new DatabaseHelper.
private DatabaseHelper mOpenHelper;

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

@Override
public boolean onCreate() {
mOpenHelper = new DatabaseHelper(getContext());
// Assumes that any failures will be reported by a thrown exception.
return true;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {

// Constructs a new query builder and sets its table name
SQLiteQueryBuilder 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;
}

@Override
public 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);
}
}

@Override
public 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);
}

@Override
public int delete(Uri uri, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
String finalWhere;

int count;

switch (mUriMatcher.match(uri)) {
case MAIN:
count = db.delete(MainTable.TABLE_NAME, where, whereArgs);
break;

case MAIN_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;
}

@Override
public 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;
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

FragmentManager fm = getFragmentManager();

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 ListFragment
implements LoaderManager.LoaderCallbacks<Cursor>
{

static final int POPULATE_ID = Menu.FIRST;
static final int CLEAR_ID = Menu.FIRST+1;

SimpleCursorAdapter mAdapter;

String mCurFilter;

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

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

public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
}
}

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved