使用CursorLoader異步加載SQLite數據
完整源碼下載:http://download.csdn.net/detail/q296264785/9686676
設及到的知識點:
1、對SQLite數據的操作。
關於SQLite的使用:http://blog.csdn.net/q296264785/article/details/53155739
2、CursorLoader加載器異步加載數據
同步加載數據的方法:http://blog.csdn.net/q296264785/article/details/53167961
3、內容提供者ContentProvider
內容提供者詳解:http://blog.csdn.net/q296264785/article/details/53158873
使用loaders的應用應該包含以下內容:
1、一個Activity或者Fragment。
2、一個LoaderManager的實例。
3、一個CursorLoader來載入被ContentProvider返回的數據。 或者,你可以實現自己的Loader或者AsyncTaskLoader的子類 從而從其他的資源加載數據
LoaderManager.LoaderCallbacks的一個實現 這是你創建新的loader和管理你的已經存在的loader的引用的地方
4、一個展示loader的數據的方法,比如一個SimpleCursorAdapter。
5、一個數據源,比如一個ContentProvider,當你使用一個 CursorLoader的時候。
使用CursorLoader加載器異步加載數據的好處是避免同步查詢數據庫時阻塞UI線程,通過使用觀察者設計模式來告訴使用者(監聽者)加載數據的改變來實現實時更新。
演示效果:Button會將兩個輸入框的數據通過內容提供者存到數據庫文件並且動態更新在ListView中
1、創建過程class_cursorloader_contentprovider_test,選擇3.0以上版本,因爲加載器的功能是API11更新的。
新建3個類:
a、DBHelper 繼承 SQLiteOpenHelper,創建數據庫用。
b、MProvider 繼承 ContentProvider,操作內容提供者用。
c、test 繼承 AndroidTestCase,單元測試–調試並且給數據庫添加數據使用,記得在清單文件中添加單元測試功能。
數據庫的創建和ContentProvider前面有博文已經介紹過,本博文只貼源碼,重點介紹異步加載器的使用。
首先需要創建一個數據庫,來爲Listview提供數據。我創建了一個包含 _pid,name,number 三類的class表格。並且使用單元測試的方法給表class加載了幾條數據。
數據庫創建類源碼:
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DBHelper extends SQLiteOpenHelper {
public DBHelper(Context context) {
super(context, "mydb.db", null, 1);
// TODO Auto-generated constructor stub
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
String sql = "create table class(_pid integer primary key autoincrement,name varchar(64),number varchar(64))";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
}
單元測試源碼:
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.test.AndroidTestCase;
public class test extends AndroidTestCase {
public void create() {
DBHelper dbHelper = new DBHelper(getContext());
SQLiteDatabase database = dbHelper.getReadableDatabase();
ContentValues values = new ContentValues();
values.put("name", "張利利");
values.put("number", "12345");
database.insert("class", null, values);
values.put("name", "李提莫");
values.put("number", "12345");
database.insert("class", null, values);
values.put("name", "王石說");
values.put("number", "12345");
database.insert("class", null, values);
values.put("name", "趙六殼");
values.put("number", "12345");
database.insert("class", null, values);
values.put("name", "郭撒大");
values.put("number", "12345");
database.insert("class", null, values);
values.put("name", "劉䮻明");
values.put("number", "12345");
database.insert("class", null, values);
}
}
解決了數據來源的問題,接下來我們需要創建一個ContentProvider的子類,用來實現對數據庫數據的修改操作,本列只需要用到數據庫的查找和增加操作,所以只重編寫查找以及新增數據的方法。在該類中我們註冊了一個監聽者,監聽者的作用就是當數據有變化時監聽者會將信息反饋給調用監聽者的對象。
cursor.setNotificationUri(contentResolver, uri);//註冊監聽
import android.content.ContentProvider;
import android.content.ContentResolver;
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 MProvider extends ContentProvider {
private DBHelper dbHelper;
private ContentResolver contentResolver;
private static String str_Uri = "com.example.class_cursorloader_contentprovider_test.MProvider";
private static final UriMatcher MATCHER = new UriMatcher(
UriMatcher.NO_MATCH);
static {
MATCHER.addURI(str_Uri, "class", 1);
MATCHER.addURI(str_Uri, "class/#", 2);
}
@Override
public boolean onCreate() {
// TODO Auto-generated method stub
dbHelper = new DBHelper(getContext());
contentResolver = getContext().getContentResolver();
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
Cursor cursor = null;
SQLiteDatabase sqLiteDatabase = dbHelper.getReadableDatabase();
switch (MATCHER.match(uri)) {
case 1:// 爲1時代表查找全部
cursor = sqLiteDatabase.query("class", projection, selection,
selectionArgs, null, null, sortOrder);
break;
case 2:
long id = ContentUris.parseId(uri);// 截取獲取uri的“id”
String str = "_pid = " + id;
if (selection != null && !selection.equals("")) {
str += selection;
}
cursor = sqLiteDatabase.query("class", projection, str,
selectionArgs, null, null, sortOrder);
break;
}
cursor.setNotificationUri(contentResolver, uri);//註冊監聽
return cursor;
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
switch (MATCHER.match(uri)) {
case 1:
return "vnd.android.cursor.dir/class";// 多行操作
case 2:
return "vnd.android.cursor.item/class";// 單行操作
}
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
Uri uri2 = null;
switch (MATCHER.match(uri)) {
case 1:
SQLiteDatabase sqLiteDatabase = dbHelper.getReadableDatabase();
long id = sqLiteDatabase.insert("class", null, values);
uri2 = ContentUris.withAppendedId(uri, id); // 重組uri
break;
}
contentResolver.notifyChange(uri, null);// 通知監聽者數據改變
return uri2;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}
}
在MainActivity類中,我要MainActivity 類實現LoaderCallbacks並且返回一個遊標參數。爲了在listView顯示數據還需要一個適配器,我們自定義一個適配器myAdapter繼承BaseAdapter,並且爲該適配器編寫一個XML佈局文件,用來實現對Class表格中三列的顯示。
MainActivity類
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends Activity implements LoaderCallbacks<Cursor> {
private myAdapter adapter;
private ListView listView;
private LoaderManager loaderManager;
private EditText editText1;
private EditText editText2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loaderManager = getLoaderManager();
listView = (ListView) findViewById(R.id.listview1);
editText1 = (EditText) findViewById(R.id.editText1);// 工號
editText2 = (EditText) findViewById(R.id.editText2);// 名字
findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Uri uri = Uri
.parse("content://com.example.class_cursorloader_contentprovider_test.MProvider/class");
ContentResolver contentResolver = getContentResolver();
ContentValues values = new ContentValues();
values.put("name", editText2.getText().toString());
values.put("number", editText1.getText().toString());
contentResolver.insert(uri, values);//按鍵操作,將兩個EditText中的數據傳遞給內容提供者保存到數據庫
loaderManager.restartLoader(1111, null, MainActivity.this);
}
});
loaderManager.initLoader(1111, null, this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// TODO Auto-generated method stub
CursorLoader loader = new CursorLoader(this);
Uri uri = Uri
.parse("content://com.example.class_cursorloader_contentprovider_test.MProvider/class");
loader.setUri(uri);
return loader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// TODO Auto-generated method stub
List<List<String>> list = new ArrayList<List<String>>();
while (data.moveToNext()) {
List<String> data_list = new ArrayList<String>();
data_list.add(data.getString(data.getColumnIndex("_pid")));
data_list.add(data.getString(data.getColumnIndex("name")));
data_list.add(data.getString(data.getColumnIndex("number")));
list.add(data_list);
}
adapter = new myAdapter(list);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();// 通知數據有更新
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
// TODO Auto-generated method stub
}
//自定義適配器
class myAdapter extends BaseAdapter {
private List<List<String>> list;
public myAdapter(List<List<String>> list) {
// TODO Auto-generated constructor stub
this.list = list;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return list.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View view = null;
if (convertView == null) {
view = LayoutInflater.from(MainActivity.this).inflate(
R.layout.list, null);
} else {
view = convertView;
}
TextView textView1 = (TextView) view
.findViewById(R.id.list_textView1);
TextView textView2 = (TextView) view
.findViewById(R.id.list_textView2);
TextView textView3 = (TextView) view
.findViewById(R.id.list_textView3);
List<String> list1 = new ArrayList<String>();
list1 = list.get(position);
textView1.setText(list1.get(0));
textView2.setText(list1.get(1));
textView3.setText(list1.get(2));
return view;
}
}
}