在UI的主線程中異步加載資料庫,使得頁面比較流暢,以防止查詢資料庫的記錄比較多或者提取資料庫記錄使用的時間太長會操作UI主線程的阻塞,進而造成相應異常。Loader不能用于增删改,隻能用于資料庫的查詢操作,可以解決阻塞UI主線程的問題。
整體思路:建立一個繼承SQLiteOpenHelper的DBHelper類,在·這個類中聲明一個資料庫名稱和版本号碼,在onCreate方法中建立資料庫表并執行,在onUpgrade方法中暫時不預處理。建立一個繼承ContentProvider的StudentContentProvider類,在這個類中定義内容提供者的增删改查操作。在xml檔案中定義一個ListView控件,在activity中聲明一個LoaderManager對象,定義一個MyAdapter類,然後聲明一個MyAdapter對象,定義一個LoaderManager.LoaderCallbacks<Cursor>匿名内部類對象,并在onCreate方法中執行個體化這個LoaderManager對象,将匿名内部類對象與這個對象關聯,綁定listview。重寫onMenuItemSelected這個方法,在這個方法中設定點選各個item彈出的删除和添加的操作,在這兩個操作中都需要用到内容解析者來調用增删改查方法操作資料,操作完成後要使用Loader重新查詢資料,即進行資料的更新,其中增加操作需要使用一個自定義對話框,用于輸入添加的文字資訊,這個自定義對話框自然也對應一個自定義的xml檔案。另外還定義了一個繼承AndroidTestCase的MyTest測試單元,在這個測試單元中定義增删改查操作完成對資料庫操作的測試。注意:在清單檔案AndroidManifest.xml檔案中注冊provider和測試單元。
activity_main.xml檔案:
<ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
</ListView>
add.xml檔案:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="姓名:" />
<EditText
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10" />
</LinearLayout>
DBHelper.java檔案:
public class DBHelper extends SQLiteOpenHelper {
private static String name="mydb.db";
private static int version=1;
public DBHelper(Context context) {
super(context, name, null, version);
// TODO Auto-generated constructor stub
}
@Override
public void onCreate(SQLiteDatabase database) {
// TODO Auto-generated method stub
// 在資料庫中建立一張表,有主鍵stuid(自增長)、name
String sql="create table student (stuid integer primary key autoincrement,name varchar(64))";
database.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
// TODO Auto-generated method stub
}
}
StudentContentProvider.java檔案:
public class StudentContentProvider extends ContentProvider {
private final static UriMatcher URI_MATCHER = new UriMatcher(
UriMatcher.NO_MATCH);
private final static int STUDENT = 1;
private final static int STUDENTS = 2;
private DBHelper helper;
// 添加兩條比對規則
static {
URI_MATCHER.addURI(
"com.example.android_loader_manager.StudentContentProvider",
"student", STUDENTS);
URI_MATCHER.addURI(
"com.example.android_loader_manager.StudentContentProvider",
"student/#", STUDENT);
}
public StudentContentProvider() {
// TODO Auto-generated constructor stub
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
int count=0;//表示影響資料庫的行數
int flag=URI_MATCHER.match(uri);
SQLiteDatabase database =helper.getWritableDatabase();
switch (flag) {
case STUDENT:
long stuid=ContentUris.parseId(uri);
String where_value=" stuid= "+stuid;
if(selection!=null&&!selection.equals("")){
where_value+=selection;
}
count=database.delete("student", where_value, selectionArgs);
break;
case STUDENTS:
count=database.delete("student", selection, selectionArgs);
break;
}
return count;
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
int flag=URI_MATCHER.match(uri);
switch (flag) {
case STUDENT:
return "vnd.android.cursor.item/student";
case STUDENTS:
return "vnd.android.cursor.dir/students";
}
return null;
}
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
// TODO Auto-generated method stub
int flag=URI_MATCHER.match(uri);
SQLiteDatabase database=helper.getWritableDatabase();
Uri uri2=null;
switch (flag) {
case STUDENTS:
//id就是插入該行的id的值
long id=database.insert("student", null, contentValues);
uri2=ContentUris.withAppendedId(uri, id);
break;
}
System.out.println("-->>"+uri2.toString());
return uri2;
}
@Override
public boolean onCreate() {
// TODO Auto-generated method stub
helper=new DBHelper(getContext());
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// TODO Auto-generated method stub
Cursor cursor=null;
int flag=URI_MATCHER.match(uri);
SQLiteDatabase database=helper.getReadableDatabase();
switch (flag) {
case STUDENT:
long stuid=ContentUris.parseId(uri);
String where_value=" stuid= "+stuid;
if(selection!=null&&!"".equals(selection)){
where_value+=selection;
}
cursor=database.query("student", projection, where_value, selectionArgs, null, null, null);
break;
case STUDENTS:
cursor=database.query("student", projection, selection, selectionArgs, null, null, null);
break;
}
return cursor;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
int count=0;
int flag=URI_MATCHER.match(uri);
SQLiteDatabase database=helper.getWritableDatabase();
switch (flag) {
case STUDENT:
long stuid=ContentUris.parseId(uri);
String where_value=" stuid = "+stuid;
if (selection!=null&&!selection.equals("")) {
where_value+=selection;
}
count=database.update("student", values, where_value, selectionArgs);
break;
case STUDENTS:
count=database.update("student", values, selection, selectionArgs);
break;
}
return count;
}
}
MyTest.java檔案:
public class MyTest extends AndroidTestCase {
public MyTest() {
// TODO Auto-generated constructor stub
}
public void add() {
// 定義一個内容解析者
ContentResolver contentResolver = getContext().getContentResolver();
ContentValues values = new ContentValues();
values.put("name", "rose");
Uri uri = Uri
.parse("content://com.example.android_loader_manager.StudentContentProvider/student");
contentResolver.insert(uri, values);
}
public void delete() {
// 定義一個内容解析者
ContentResolver contentResolver = getContext().getContentResolver();
Uri uri = Uri
.parse("content://com.example.android_loader_manager.StudentContentProvider/student/1");
int count=contentResolver.delete(uri, null, null);//影響資料庫的行數
System.out.println("--count-->"+count);
}
public void query() {
// 定義一個内容解析者
ContentResolver contentResolver = getContext().getContentResolver();
Uri uri = Uri
.parse("content://com.example.android_loader_manager.StudentContentProvider/student");
// 沒有這句話是查詢所有記錄,有這句話是查詢id為2的一條記錄
Uri uri2=ContentUris.withAppendedId(uri, 2);
Cursor cursor=contentResolver.query(uri2, null, null, null, null);
while (cursor.moveToNext()) {
System.out.println("-->>"+cursor.getString(cursor.getColumnIndex("name")));
}
}
}
MainActivity.java檔案:
public class MainActivity extends Activity {
private LoaderManager manager;
private ListView listview;
private MyAdapter adapter=null;
@SuppressLint("NewApi")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
manager = getLoaderManager();// 加載LoaderManager完成異步加載資料的操作
manager.initLoader(1000, null, callbacks);
listview=(ListView)findViewById(R.id.listView1);
adapter=new MyAdapter(MainActivity.this);
registerForContextMenu(listview);
}
// 處理上下文菜單
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
// TODO Auto-generated method stub
getMenuInflater().inflate(R.menu.main, menu);
super.onCreateContextMenu(menu, v, menuInfo);
}
@SuppressLint("NewApi")
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
// TODO Auto-generated method stub
AdapterContextMenuInfo info=(AdapterContextMenuInfo)item.getMenuInfo();
switch (item.getItemId()) {
case R.id.del:
// Toast.makeText(MainActivity.this, "del", 1).show();
int position=info.position;
String name=adapter.getItem(position).toString();
Uri uri = Uri
.parse("content://com.example.android_loader_manager.StudentContentProvider/student");
ContentResolver contentResolver=getContentResolver();
// count表示删除後影響資料庫的行數
int count=contentResolver.delete(uri, " name=? ", new String[]{name});
if(count>0){
// 相當于重新查詢資料庫
manager.restartLoader(1000, null, callbacks);
}
break;
case R.id.add:
// 添加自定義對話框,完成使用者的資料錄入
// Toast.makeText(MainActivity.this, "add", 1).show();
final Dialog dialog=createAddDialog(MainActivity.this);
Button button=(Button)dialog.findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
@SuppressLint("NewApi")
@Override
public void onClick(View arg0) {
EditText editText=(EditText)dialog.findViewById(R.id.editText1);
//trim()表示去掉前後空格
String name=editText.getText().toString().trim();
ContentResolver contentResolver=getContentResolver();
Uri uri = Uri
.parse("content://com.example.android_loader_manager.StudentContentProvider/student");
ContentValues values=new ContentValues();
values.put("name", name);
Uri result_uri=contentResolver.insert(uri, values);
if(result_uri!=null){//不為空表示插入成功了
// 重新加載資料
manager.restartLoader(1000, null, callbacks);
dialog.dismiss();
}
}
});
dialog.show();
break;
}
return super.onMenuItemSelected(featureId, item);
}
public Dialog createAddDialog(Context context){
Dialog dialog=new Dialog(context);
dialog.setContentView(R.layout.add);
return dialog;
}
public class MyAdapter extends BaseAdapter{
private List<String> list=null;
private Context context;
public MyAdapter(Context context){
this.context=context;
}
public void setList(List<String> list){
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
TextView view=null;
if(convertView==null){
view=new TextView(context);//context是上下文對象
}else{
view=(TextView)convertView;
}
view.setText(list.get(position).toString());
return view;
}
}
// callbacks是一個匿名内部類
private LoaderManager.LoaderCallbacks<Cursor> callbacks = new LoaderCallbacks<Cursor>() {
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
// TODO Auto-generated method stub
}
// 完成對UI的資料提取,更新UI的操作
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// TODO Auto-generated method stub
// 把資料提取出來,放到擴充卡中完成對UI的更新操作
List<String> list=new ArrayList<String>();
while (cursor.moveToNext()) {
String name=cursor.getString(cursor.getColumnIndex("name"));
list.add(name);
}
adapter.setList(list);
listview.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
@SuppressLint("NewApi")
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
// TODO Auto-generated method stub
// 第一種方式
// CursorLoader loader=new CursorLoader(MainActivity.this);
// Uri uri = Uri
// .parse("content://com.example.android_loader_manager.StudentContentProvider/student");
// loader.setUri(uri);
// // API提供的設定loader的方法
// // loader.setProjection(projection);
// // loader.setSelection(selection);
// // loader.setSelectionArgs(selectionArgs);
// // loader.setSortOrder(sortOrder);
Uri uri = Uri
.parse("content://com.example.android_loader_manager.StudentContentProvider/student");
CursorLoader loader = new CursorLoader(MainActivity.this, uri,
null, null, null, null);
return loader;
}
};
@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;
}
}