4.17 一个SQLite的小例子
一个使用Sqlite数据库示例 本例中,使用SQLite数据库作为数据存储仓库。 示例中,使用DBHelper继承SQLiteOpenHelper进行数据库中数据表的新建删除及数据库的获得,另外利用DBHelper进行数据在数据表中的CRUD。 示例中,界面上提供ListView+SimpleCursorAdapter来呈现数据表中的数据 示例中,提供对数据库文件的备份和恢复。当备份/恢复结束或过程中出现异常,会以通知的形式提示用户。 示例中,数据库文件的备份恢复会采用异步方式进行,具体的IO操作采用nio的channel进行 示例中,也可以使用ContentProvider来对数据库的数据表进行CRUD操作 示例中,也使用了CursorLoader这种方式来加载数据库。对于一些大型数据库的加载,如果在主线程上使用getReadableDatabase/getWriteableDatabase方式打开数据库时,可能造成ANR,所以利用CursorLoader的方式来加载数据库,加载完毕后将内容填充到ListView中进行显示 代码展示: MainActivity的界面: <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" android:orientation="vertical">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="暱稱:" android:typeface="monospace" android:layout_marginTop="10dp" android:layout_marginLeft="5dp" /> <EditText android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="100" android:hint="新的暱稱" android:typeface="monospace" android:layout_marginTop="10dp" android:layout_marginLeft="5dp" android:layout_marginRight="5dp" android:paddingLeft="5dp" /> </LinearLayout> <Button android:id="@+id/btn_add" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="增加新的暱稱" android:padding="5dp" /> <Button android:id="@+id/btn_save" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="備份數據庫" android:padding="5dp" /> <Button android:id="@+id/btn_provider" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="試試provider" android:padding="5dp" /> <ListView android:id="@+id/lv_person" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" ></ListView>
</LinearLayout> 界面如图所示: 在EditText中输入内容后,点击“增加新的昵称”,会将EditText中的内容添加到Sqlite数据库,并且新增数据会刷新到ListView中。 点击“备份数据库”后,将数据库文件保存到SD卡上的指定文件夹下 点击“试试Provider”按钮后,会显示ProviderActivity。在ProviderActivity中,会使用CursorLoader加载数据库文件,并使用ContentProvider对数据表进行CRUD 在进行MainActivity进行具体编码之前,因为MainActivity中需要用到大量的其它相关自定义类内容,所以先从其它自定义类开始。 常量接口com.example.constant.Constant: /** * * 該接口中會定義若干關於數據庫的常量值,方便調用使用 * *@author piglite * */ public interface Constant { StringDB_NAME="mydb";//数据库名称 intDB_VERSION=1;//数据库版本 StringTABLE_NAME="t_person";//数据表名称 StringCOLUMN_ID="_id";//列名 StringCOLUMN_NAME="name";//列名 StringCOLUMN_DATE="date";//列名 StringCREATE_TABLE="CREATE TABLE IF NOT EXISTS "+TABLE_NAME+ " ("+ COLUMN_ID+ " INTEGER PRIMARY KEY AUTOINCREMENT,"+ COLUMN_NAME+ " TEXT NOT NULL,"+ COLUMN_DATE+ " DATE NOT NULL" + ") "; //建表语句 StringDROP_TABLE="DROP TABLE IF EXISTS "+TABLE_NAME;//删表语句 } 创建SqliteOpenHelper的继承类DBHelper,作为数据库操作的辅助类: /** * *SQLite數據庫的輔助類。負責數據庫中數據表的新建、刪除以及數據庫示例的獲得等操作 *@author piglite * */ public class DBHelper extendsSQLiteOpenHelper implements Constant{
publicDBHelper(Context context){ super(context,DB_NAME,null,DB_VERSION); } @Override publicvoid onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE); //創建數據表后插入兩條數據作為測試數據 ContentValuescv1=new ContentValues(); cv1.put(COLUMN_NAME,"大寶貝貓"); cv1.put(COLUMN_DATE,getNow()); db.insert(TABLE_NAME,cv1); ContentValuescv2=new ContentValues(); cv2.put(COLUMN_NAME,"金絲貓"); cv2.put(COLUMN_DATE,cv2); } @Override publicvoid onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion) { db.execSQL(DROP_TABLE); onCreate(db); } /** * 將ContentValues插入數據表時,計算插入時間的工具方法 * @return ContentValues插入數據表時的日期,該日期用字符串形式表示 */ publicString getNow(){ returnnew SimpleDateFormat("yyyy-MM-dd").format(new Date()); } } 创建一个进行数据库文件IO操作的工具类IOTask。因为IO操作是典型的耗时操作应该异步执行,所以让IOTask继承AsyncTask类: /** * 負責數據庫文件的備份和恢復 * 因為IO操作是耗時操作,因此要異步進行 * 當執行完IO操作后,根據執行結構回調OnCompleteListener中的對應方法 *@author piglite * */ public class IOTask extendsAsyncTask<String,Void,Integer> implements Constant{ //在後台進行IO操作后的可能結果 privatestatic final int BACKUP_OK=0;//備份成功 privatestatic final int RESOTRE_OK=1;//恢復成功 private static final int ERROR=2;//有異常 //指定IOTask執行備份(BACKUP)還是恢復(RESTORE)操作 public static final String BACKUP="backupdbfile"; public static final String RESTORE="restoredbfile";
private Context mContext; private OnCompleteListener mListener; /** * 當IO操作完成后,根據執行結果,進行回調處理 * @author piglite * */ public interface OnCompleteListener{ void onBackSuccess(); void onRestoreSuccess(); void onError(int errorcode); } //為IOTask提供回調實例 public void setOnCompleteListener(OnCompleteListener listener){ mListener=listener; } publicIOTask(Context ctx) { super(); this.mContext=ctx; }
@Override protectedInteger doInBackground(String... params) { //判斷是要執行備份還是恢復操作 Stringcommand=params[0]; //IO執行結果的默認值 intresult=ERROR; //獲得指定數據庫的路徑 FiledbPath=mContext.getDatabasePath(DB_NAME); //獲得SD上存放目標文件的文件夾 FiletargetPath=newFile(Environment.getExternalStorageDirectory(),"dbbak"); if(!targetPath.exists())targetPath.mkdirs(); //獲得目標文件 FiletargetFile=new File(targetPath,dbPath.getName());
if(BACKUP.equals(command)){ try{ //注意,createNewFile可能返回true也可能返回false //返回true意味著目標文件夾中沒有該文件,此時新建并準備被寫入 //返回false意味著目標文件夾中有該文件,此時準備將原文件覆蓋 targetFile.createNewFile(); fileCopy(dbPath,targetFile); result=0; }catch (Exception e) { e.printStackTrace(); result=2; }
}elseif(RESTORE.equals(command)){ try{ if(!targetFile.exists()){ thrownew RuntimeException("沒有存儲的文件"); } dbPath.createNewFile(); fileCopy(targetFile,dbPath); result=1; }catch (Exception e) { e.printStackTrace(); result=2; } }else{ result=2; }
returnresult; } /** * 利用nio的Channel來進行實際IO操作 * @param dbPath 源文件(如果是備份數據庫的操作,源文件就是數據庫文件) * @param targetFile 目標文件(如果是備份數據庫的操作,目標文件就是SD卡上的文件) * @throws Exception IO過程錯誤 */ privatevoid fileCopy(File dbPath,File targetFile) throws Exception { FileChannelout=new FileOutputStream(dbPath).getChannel(); FileChannelin=new FileInputStream(targetFile).getChannel(); in.transferTo(0,in.size(),out); out.close(); in.close(); } /** * 當異步的IO操作完成后,會返回代表操作結果的result值 * 根據result值來調用回調接口的相關方法 */ @Override protectedvoid onPostExecute(Integer result) { super.onPostExecute(result); if(mListener==null){ thrownew RuntimeException("未指定有效的回調接口"); } switch(result) { caseBACKUP_OK: mListener.onBackSuccess(); break; caseRESOTRE_OK: mListener.onRestoreSuccess(); break; default: mListener.onError(result); break; } } } 在使用IOTask的时候,要传入OnCompleteListener实例,否则会引发异常。 写一个ContentProvider的继承者,用来查询我们的数据库: /** * * 用來提供對數據庫訪問的“接口” *@author piglite * */ public class MyDBProvider extendsContentProvider implements Constant { //數據庫的uri(注意,必須要有content://前綴) publicstatic final Uri CONTENT_URI = Uri.parse("content://com.example.mydbprovider/person"); privatestatic final int PERSON = 1; privatestatic final int PERSON_ID = 2; //添加一個UriMatcher,用來判斷用戶查詢的uri是什麼 //如果是content://com.example.mydbprovider/person,就認為用戶是想對多條數據發起訪問 //如果是content://com.example.mydbprovider/person/xxxid,就認為用戶是想對指定的數據發起訪問 privatestatic final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); static{ //設定matcher可匹配的uri后,利用Matcher的match方法 //就根據用戶提供的uri,判斷用戶欲訪問的數據內容是多條數據還是特定某條數據 matcher.addURI(CONTENT_URI.getAuthority(),"person",PERSON); matcher.addURI(CONTENT_URI.getAuthority(),"person/#",PERSON_ID); } privateSQLiteDatabase mDb; @Override publicboolean onCreate() { DBHelper_helper = new DBHelper(getContext()); mDb= _helper.getWritableDatabase(); returntrue; } @Override publicCursor query(Uri uri,String[] projection,String selection, String[]selectionArgs,String sortOrder) { //根據用戶query時提供的uri來判斷用戶是想訪問多條數據還是特定的某條數據 intresult = matcher.match(uri); switch(result) { //如果是訪問多條數據時 casePERSON: returnmDb.query(TABLE_NAME,projection,selection,selectionArgs, null,sortOrder); //如果是訪問指定的id的數據時 casePERSON_ID: returnmDb .query(TABLE_NAME,"_id=?", newString[] { uri.getLastPathSegment() },null); //如果查詢提供的uri不符合matcher裡面添加的模式,則直接返回null default: returnnull; } } @Override publicString getType(Uri uri) { returnnull; } @Override publicUri insert(Uri uri,ContentValues values) { long_id = mDb.insert(TABLE_NAME,values); //如果數據庫數據表插入數據成功,將該條數據的_id值拼接為一個uri作為方法的返回值返回 if(_id > 0) returnuri.withAppendedPath(uri,String.valueOf(_id)); else returnnull; } @Override publicint delete(Uri uri,String[] selectionArgs) { intresult = matcher.match(uri); switch(result) { //刪除整張數據表的數據 casePERSON: returnmDb.delete(TABLE_NAME,selectionArgs); //刪除數據表中指定的某條數據 casePERSON_ID: returnmDb.delete(TABLE_NAME, newString[] { uri.getLastPathSegment() }); //提供的uri不符合matcher中添加的uri模式 default: return0; } } @Override publicint update(Uri uri,ContentValues values, String[]selectionArgs) { intresult = matcher.match(uri); switch(result) { //更新數據表中的多條數據 casePERSON: returnmDb.update(TABLE_NAME,values,selectionArgs); //更新數據表中的某條數據 casePERSON_ID: returnmDb.update(TABLE_NAME, newString[] { uri.getLastPathSegment() }); //提供的uri不符合matcher中添加的uri模式 default: return0; } } } Provider要在AndroidManifest文件中进行注册,注册的时候必须要提供name和authorities两个属性值 <provider android:name="com.example.util.MyDBProvider" android:authorities="com.example.mydbprovider" > </provider> 这个Provider的authorities与类中定义的CONTENT_URI常量的内容要对应上。 最后是在两个Activity中加载呈现数据。 MainActivity MainActivity提供3个按钮,第一个按钮为向数据表中添加新的数据;第二个按钮为备份整个数据库;第三个按钮为跳转到另一个Activity。它还有一个ListView,利用SimpleCursorAdapter来加载数据库得到的Cursor对象到ListView中显示。 public class MainActivity extends Activityimplements Constant,OnItemClickListener{ @ViewInject(R.id.et_name) privateEditText mEtName;//輸入新的暱稱 @ViewInject(R.id.btn_add) privateButton mBtnAdd;//添加新暱稱到數據庫中 @ViewInject(R.id.btn_save) privateButton mBtnSave;//保存數據庫文件到SD卡 @ViewInject(R.id.btn_provider) privateButton mBtnProvider;//跳轉到另一個Activity @ViewInject(R.id.lv_person) privateListView mListView;//將Cursor中的數據加載到ListView中顯示
privateSQLiteDatabase mDb; privateDBHelper mHelper; privateSimpleCursorAdapter mAdapter; privateCursor mCursor; @Override protectedvoid onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewUtils.inject(this); mHelper=newDBHelper(this); }
@Override protectedvoid onResume() { super.onResume(); mDb=mHelper.getWritableDatabase(); String[]columns=new String[]{COLUMN_ID,COLUMN_NAME,COLUMN_DATE}; //直接利用SqliteDatebase提供的方法來查詢獲得結果集 mCursor=mDb.query(TABLE_NAME,null); //將結果集中每一條數據中的指定列信息加載到item的指定佈局中 mAdapter=newSimpleCursorAdapter(this,android.R.layout.simple_list_item_1, mCursor, newString[]{COLUMN_NAME,COLUMN_DATE}, newint[]{android.R.id.text1,android.R.id.text2}); //為ListView綁定適配器 mListView.setAdapter(mAdapter); //為ListView的條目添加點擊事件監聽器 mListView.setOnItemClickListener(this); }
@OnClick({R.id.btn_add,R.id.btn_provider,R.id.btn_save}) publicvoid doClick(View v){ switch(v.getId()) { //將EditText中輸入的內容添加到數據庫中 caseR.id.btn_add: ContentValuescv=new ContentValues(); cv.put(COLUMN_NAME,mEtName.getText().toString()); cv.put(COLUMN_DATE,mHelper.getNow()); mDb.insert(TABLE_NAME,cv); //插入完畢后,重新獲得最新的結果集 mCursor.requery(); //通知綁定該結果集的適配器,去刷新與該適配器綁定的ListView mAdapter.notifyDataSetChanged(); mEtName.setText(""); break; caseR.id.btn_save: IOTasktask=new IOTask(this); //在使用IOTask的時候要指定OnCompleteListener實例 task.setOnCompleteListener(newOnCompleteListener() {
@Override publicvoid onRestoreSuccess() {
}
@Override publicvoid onError(int errorcode) { makeNotification("數據庫操作錯誤"); }
@Override publicvoid onBackSuccess() { makeNotification("數據庫備份完畢"); } }); task.execute(IOTask.BACKUP); break; //點擊跳轉到OtherActivity界面。OtherListView將採用LoaderManager異步加載數據庫文件,并使用ContentResolver //訪問數據庫綁定的ContentProvider來獲得數據 caseR.id.btn_provider: startActivity(newIntent(this,OtherActivity.class)); break; } } //利用通知來提示用戶 protectedvoid makeNotification(String string) { /** * 使用通知的步驟: * 1 獲得NotificationManager * 2 構建Notification對象 * 3 如果有必要,設置Notification的相關屬性(flag) * 4 設置Notification用的PendingIntent * 5 用NotificationManager呈現Notification */ //1 NotificationManagermanager=(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); //2 Notificationn = new Notification(R.drawable.ic_launcher,"數據庫操作信息",System.currentTimeMillis()); //4 n.setLatestEventInfo(this,"提示消息",string,PendingIntent.getActivity( this,new Intent(this,MainActivity.class),0)); //5 manager.notify(0,n); } @Override publicvoid onItemClick(AdapterView<?> parent,View view,int position, longid) { //因為數據集中的數據是一條條按順序顯示在ListView中的,因此點擊的item在ListView中的位置也就是數據在數據集中的位置 mCursor.moveToPosition(position); //獲得該位置數據的_id值 int_id=mCursor.getInt(mCursor.getColumnIndex(COLUMN_ID)); //刪除該條數據 mDb.delete(TABLE_NAME,new String[]{String.valueOf(_id)}); //刪除完畢后,重新查詢獲得結果集 mCursor.requery(); //通知Adaptercursor發生了變化,去刷新ListView的內容 mAdapter.notifyDataSetChanged(); } } 最后是利用Loader来加载数据和利用ContentResolver来访问数据的OtherActivity public class OtherActivity extendsFragmentActivity implementsConstant,OnItemClickListener,LoaderCallbacks<Cursor>{ @ViewInject(R.id.lv_other) privateListView mListView;//用來呈現數據表中的數據 privateSimpleCursorAdapter mAdapter;//用來管理數據表中的數據 privateContentResolver mCr;//用來通過數據庫的ContentProvider來實現數據的CRUD @Override protectedvoid onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_other); ViewUtils.inject(this); //初始化Loader時需要提供一個該Loader的id值,以及實現一個LoaderManager的LoaderCalbacks<Cursor>的實例 getSupportLoaderManager().initLoader(1001,this); //獲得ContentResolver,用來通過ContentProvider來訪問數據表中的數據 mCr=getContentResolver(); //獲得一個SimpleCursorAdapter對象。 //注意該SimpleCursorAdapter的Cursor參數為null。 //初次與ListView進行綁定時,ListView中呈現的數據並不來自SimpleCursorAdapter適配器 //但是以後依然可以通過適配器獲得與ListView綁定的數據集,儘管這個數據集是來自CursorLoader mAdapter=newSimpleCursorAdapter(this,newString[]{COLUMN_NAME},new int[]{android.R.id.text1}); mListView.setAdapter(mAdapter); mListView.setOnItemClickListener(this); } @Override publicLoader<Cursor> onCreateLoader(int arg0,Bundle arg1) { CursorLoaderloader = new CursorLoader(this, MyDBProvider.CONTENT_URI, newString[]{COLUMN_ID, null); returnloader; } @Override publicvoid onLoadFinished(Loader<Cursor> arg0,Cursor arg1) { mAdapter.swapCursor(arg1); } @Override publicvoid onLoaderReset(Loader<Cursor> arg0) { mAdapter.swapCursor(null); } @Override publicvoid onItemClick(AdapterView<?> parent, long id) { Cursorcursor = mAdapter.getCursor(); cursor.moveToPosition(position); int_id=cursor.getInt(cursor.getColumnIndex(COLUMN_ID)); Cursorresult=mCr.query(Uri.withAppendedPath(MyDBProvider.CONTENT_URI,String.valueOf(_id)), "_id=?", newString[]{String.valueOf(_id)}, null); result.moveToFirst(); Stringstr=String.format("您點擊的內容是,%s %s %s", result.getInt(0),result.getString(1),result.getString(2)); Toast.makeText(this,str,0).show(); } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- c# – 实体框架代码第一个错误“定位服务器/实例指定的错误
- Using the Flex Compilers Flex编译器的使用 第三部分
- cocos2d-x3.2中的getVisibleSize,getContentSize,bounding
- (1) near "limit": syntax error (sqlite报错)
- c – O(N)锦标赛冠军和O(NLogN)球员排名
- 【ajax】 $.ajax()的基本用法
- [Swift]ASCII值的获取和转换:扩展Character类和Int类
- 是否可以从C#读取内部CPU滴答计数器?
- swfUpload自定义button图片
- React + Redux 入门(一):抛开 React 学 Redux