Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android應用開發入門(三十九)Loaders的使用

Android應用開發入門(三十九)Loaders的使用

編輯:Android開發實例

前言

  Loaders,裝載機,適用於Android3.0以及更高的版本,它提供了一套在UI的主線程中異步加載數據的框架。使用Loaders可以非常簡單的在Activity或者Fragment中異步加載數據,一般適用於大量的數據查詢,或者需要經常修改並及時展示的數據顯示到UI上,這樣可以避免查詢數據的時候,造成UI主線程的卡頓。

  Loaders有以下特點:

  • 可以適用於Activity和Fragment。
  • 可以提供異步的方式加載數據。
  • 監聽數據源,當數據改變的時候,將新的數據發布到UI上。
  • Loaders使用Cursor加載數據,在更改Cursor的時候,會自動重新連接到最後配置的Cursor中讀取數據,因此不需要重新查詢數據。

  在Android中使用Loaders機制,需要多個類和接口的配合,以下是它們大致的關系圖,之後的內容會對這幾個類或接口進行詳細講解:

 

LoaderManager

  LoaderManager,裝載機管理器。用於在Activity或者Fragment中管理一個或多個Loader實例。在Activity或者Fragment中,可以通過getLoaderManager()方法獲取LoaderManager對象,它是一個單例模式。

  介紹幾個LoaderManager提供的方法,用於管理Loader:

  • Loader<D> initLoader(int id,Bundle bundle,LoaderCallbacks<D> callback):初始化一個Loader,並注冊回調事件。
  • Loader<D> restartLoader(int id,Bundle bundle,LoaderCallbacks<D> callback):重新啟動或創建一個Loader,並注冊回調事件。
  • Loader<D> getLoader(int id):返回給定Id的Loader,如果沒有找到則返回Null。
  • void destroyLoader(int id):根據指定Id,停止和刪除Loader。

   通過上面幾個方法的參數可以看到,都有一個id參數,這個Id是Loader的標識,因為LoaderManager可以管理一個或多個Loader,所以必須通過這個Id參數來唯一確定一個Loader。而InitLoader()、restartLoader()中的bundle參數,傳遞一個Bundle對象給LoaderCallbacks中的onCreateLoader()去獲取,下面介紹LoaderCallbacks。

 

LoaderManager.LoaderCallbacks

  LoaderCallbacks是LoaderManager和Loader之間的回調接口。它是一個回調接口,所以我們需要實現其定義的三個方法:

  • Loader<D> onCreateLoader(int id,Bundle bundle):根據指定Id,初始化一個新的Loader。
  • void onLoadFinished(Loader<D> loader,D data):當Loader被加載完畢後被調用,在其中處理Loader獲取的Cursor數據。
  • void onLoaderReset(Loader<D> loader):當Loader被銷毀的時候被調用,在其中可以使Loader的數據不可用。

  從LoaderCallbacks的聲明的幾個方法中可以看到,它是一個泛型的接口,需要指定Loader數據的類型。如果是數據源是從一個ContentProvider中獲取的,一般直接使用它的子類CursorLoader,下面介紹CursorLoader。

 

Loader

  Loader,一個抽象的類,用於執行異步加載數據,這個Loader對象可以監視數據源的改變和在內容改變後,以新數據的內容改變UI的展示。它是一個Loader的抽象接口,所有需要實現的Loader功能的類都需要實現這個接口,但是如果需要自己開發一個裝載機的話,一般並不推薦繼承Loader接口,而是繼承它的子類AsyncTaskLoader,這是一個以AsyncTask框架執行的異步加載。

  Android中還提供了一個CursorLoader類,它是AsyncTaskLoader的子類,一個異步的加載數據的類,通過ContentResolver的標准查詢並返回一個Cursor。這個類實現了Loader的協議,以一種標准的方式查詢Cursor。

  CursorLoader類有兩個構造函數,推薦使用第二個,因為使用第一個構造函數,需要還需要通過CursorLoader提供的一些了getXxx()方法設置對應的屬性:

  • CursorLoader(Context context)
  • CursorLoader(Context context,Uri uri,String[] projection,String selection ,String[] selectionArgs,String sortOrder)

 

SimpleCursorAdapter

  在Android中,數據的展示都需要使用一個Adapter適配器,而Loader一般返回的就是一個Cursor的數據,可以使用BaseAdapter的一個子類SimpleCursorAdapter,它可以使用XML資源文件自定義一個布局在展示數據。它有兩個構造函數,但是有一個構造函數在API Level11之後就不推薦使用。下面是構造函數的簽名:

  SimpleCursorAdapter(Context context,int layout,Cursor c,String[] from,int[] to,int flags).

  最後一個參數flags是一個標識,標識當數據改變調用onContentChanged()的時候,是否通知ContentProvider數據的改變,如果無需監聽ContentProvider的改變,則可以傳0。對於SimpleCursorAdapter適配器的Cursor的改變,可以使用SimpleCursorAdapter.swapCursor(Cursor)方法,它會與舊的Cursor互換,並且返回舊的Cursor。

 

Demo

  下面通過一個Demo來講解一下Loaders的使用。在這個Demo中,數據使用SQLite數據庫保存,而使用ContentProvider進行數據的請求與訪問。在SQLite數據庫中,存在一個Student表,它近有兩個字段:_id,name。在Demo中,使用一個ListView展示數據,使用LoaderManager管理一個Loader,並通過這個Loader的回調接口進行刷新ListView的數據顯示。進行對SQLite數據庫中的數據進行增加與刪除。下面不提供SQLiteOpenHelper和ContentProvider相關實現類的代碼,如有需要可以下載源碼查看,對於SQLite和ContentProvider的內容,不清楚的朋友可以參見:http://www.fengfly.com/plus/view-213507-1.html和http://www.fengfly.com/plus/view-213512-1.html。

  實現代碼:

  1. package com.example.loadermanagerdemo;  
  2.  
  3. import android.net.Uri;  
  4. import android.os.Bundle;  
  5. import android.app.Activity;  
  6. import android.app.AlertDialog;  
  7. import android.app.LoaderManager;  
  8. import android.app.LoaderManager.LoaderCallbacks;  
  9. import android.content.ContentResolver;  
  10. import android.content.ContentValues;  
  11. import android.content.CursorLoader;  
  12. import android.content.Loader;  
  13. import android.database.Cursor;  
  14. import android.util.Log;  
  15. import android.view.ContextMenu;  
  16. import android.view.LayoutInflater;  
  17. import android.view.Menu;  
  18. import android.view.MenuInflater;  
  19. import android.view.MenuItem;  
  20. import android.view.View;  
  21. import android.view.ContextMenu.ContextMenuInfo;  
  22. import android.widget.AdapterView.AdapterContextMenuInfo;  
  23. import android.widget.Button;  
  24. import android.widget.EditText;  
  25. import android.widget.ListView;  
  26. import android.widget.SimpleCursorAdapter;  
  27. import android.widget.TextView;  
  28.  
  29. public class MainActivity extends Activity {  
  30.     private LoaderManager manager;  
  31.     private ListView listview;  
  32.     private AlertDialog alertDialog;  
  33.     private SimpleCursorAdapter mAdapter;  
  34.     private final String TAG="main";  
  35.  
  36.     @Override 
  37.     protected void onCreate(Bundle savedInstanceState) {  
  38.         super.onCreate(savedInstanceState);  
  39.         setContentView(R.layout.activity_main);  
  40.         listview = (ListView) findViewById(R.id.listView1);  
  41.         //使用一個SimpleCursorAdapter,布局使用android自帶的布局資源simple_list_item_1, android.R.id.text1 為simple_list_item_1中TextView的Id  
  42.         mAdapter = new SimpleCursorAdapter(MainActivity.this,  
  43.                 android.R.layout.simple_list_item_1, null,  
  44.                 new String[] { "name" }, new int[] { android.R.id.text1 },0);  
  45.           
  46.         // 獲取Loader管理器。  
  47.         manager = getLoaderManager();  
  48.         // 初始化並啟動一個Loader,設定標識為1000,並制定一個回調函數。  
  49.         manager.initLoader(1000, null, callbacks);  
  50.  
  51.         // 為ListView注冊一個上下文菜單  
  52.         registerForContextMenu(listview);  
  53.     }  
  54.  
  55.     @Override 
  56.     public void onCreateContextMenu(ContextMenu menu, View v,  
  57.             ContextMenuInfo menuInfo) {  
  58.         super.onCreateContextMenu(menu, v, menuInfo);  
  59.         // 聲明一個上下文菜單,contentmenu中聲明了兩個菜單,添加和刪除  
  60.         MenuInflater inflater = getMenuInflater();  
  61.         inflater.inflate(R.menu.contentmenu, menu);  
  62.     }  
  63.  
  64.     @Override 
  65.     public boolean onContextItemSelected(MenuItem item) {  
  66.           
  67.         switch (item.getItemId()) {  
  68.         case R.id.menu_add:  
  69.             // 聲明一個對話框  
  70.             AlertDialog.Builder builder = new AlertDialog.Builder(  
  71.                     MainActivity.this);  
  72.             // 加載一個自定義布局,add_name中有一個EditText和Button控件。  
  73.             final View view = LayoutInflater.from(MainActivity.this).inflate(  
  74.                     R.layout.add_name, null);  
  75.             Button btnAdd = (Button) view.findViewById(R.id.btnAdd);  
  76.             btnAdd.setOnClickListener(new View.OnClickListener() {  
  77.  
  78.                 @Override 
  79.                 public void onClick(View v) {  
  80.                     EditText etAdd = (EditText) view  
  81.                             .findViewById(R.id.username);  
  82.                     String name = etAdd.getText().toString();  
  83.                     // 使用ContentResolver進行刪除操作,根據name字段。  
  84.                     ContentResolver contentResolver = getContentResolver();  
  85.                     ContentValues contentValues = new ContentValues();  
  86.                     contentValues.put("name", name);  
  87.                     Uri uri = Uri  
  88.                             .parse("content://com.example.loadermanagerdemo.StudentContentProvider/student");  
  89.                     Uri result = contentResolver.insert(uri, contentValues);  
  90.                     if (result != null) {  
  91.                         //result不為空證明刪除成功,重新啟動Loader,注意標識需要和之前init的標識一致。  
  92.                         manager.restartLoader(1000, null, callbacks);  
  93.                     }  
  94.                     // 關閉對話框  
  95.                     alertDialog.dismiss();  
  96.                       
  97.                     Log.i(TAG, "添加數據成功,name="+name);  
  98.                 }  
  99.             });  
  100.             builder.setView(view);  
  101.             alertDialog = builder.show();  
  102.             return true;  
  103.         case R.id.menu_delete:  
  104.             // 獲取菜單選項的信息  
  105.             AdapterContextMenuInfo info = (AdapterContextMenuInfo) item  
  106.                     .getMenuInfo();  
  107.             // 獲取到選項的TextView控件,並得到選中項的那麼  
  108.             TextView tv = (TextView) info.targetView;  
  109.             String name = tv.getText().toString();  
  110.             // 使用ContentResolver進行刪除操作  
  111.             Uri url = Uri  
  112.                     .parse("content://com.example.loadermanagerdemo.StudentContentProvider/student");  
  113.             ContentResolver contentResolver = getContentResolver();  
  114.             String where = "name=?";  
  115.             String[] selectionArgs = { name };  
  116.             int count = contentResolver.delete(url, where, selectionArgs);  
  117.             if (count == 1) {  
  118.                 //這個操作僅刪除單挑記錄,如果刪除行為1 ,則重新啟動Loader  
  119.                 manager.restartLoader(1000, null, callbacks);  
  120.             }  
  121.             Log.i(TAG, "刪除數據成功,name="+name);  
  122.             return true;  
  123.         default:  
  124.             return super.onContextItemSelected(item);  
  125.         }  
  126.  
  127.     }  
  128.  
  129.     // Loader的回調接口  
  130.     private LoaderManager.LoaderCallbacks<Cursor> callbacks = new LoaderCallbacks<Cursor>() {  
  131.  
  132.         @Override 
  133.         public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {  
  134.             // 在Loader創建的時候被調用,這裡使用一個ContentProvider獲取數據,所以使用CursorLoader返回數據  
  135.             Uri uri = Uri  
  136.                     .parse("content://com.example.loadermanagerdemo.StudentContentProvider/student");  
  137.             CursorLoader loader = new CursorLoader(MainActivity.this, uri,  
  138.                     null, null, null, null);  
  139.             Log.i(TAG, "onCreateLoader被執行。");  
  140.             return loader;  
  141.         }  
  142.  
  143.         @Override 
  144.         public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {  
  145.             //刷新SimpleCursorAdapter的數據  
  146.             mAdapter.swapCursor(cursor);  
  147.             // 重新設定適配器  
  148.             listview.setAdapter(mAdapter);  
  149.             Log.i(TAG, "onLoadFinished被執行。");  
  150.         }  
  151.  
  152.         @Override 
  153.         public void onLoaderReset(Loader<Cursor> loader) {  
  154.             // 當Loader被從LoaderManager中移除的時候,被執行,清空SimpleCursorAdapter適配器的Cursor  
  155.             mAdapter.swapCursor(null);  
  156.             Log.i(TAG, "onLoaderReset被執行。");  
  157.         }  
  158.     };  
  159.  
  160.     @Override 
  161.     public boolean onCreateOptionsMenu(Menu menu) {  
  162.         getMenuInflater().inflate(R.menu.main, menu);  
  163.         return true;  
  164.     }  
  165.  

 效果展示:

  源碼下載

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