1 Android四種數據持久化方式
????????Android有四種數據持久化方式:
? ??SharePreference
????????輕量級鍵-值方式存儲,以XML文件方式保存。
? ??文件
????????采用java.io.*庫所提供有I/O接口,讀寫文件。
? ??SQLit數據庫
????????SQLite是輕量級嵌入式內置數據庫。
? ??ContentProvider
????????ContentProvider可為數據封裝,為多個應用共享
2 SharePreference
Android--sharepreference總結
http://blog.csdn.net/wulianghuan/article/details/8501063
2.1 簡介
????????SharedPreferences類,它是一個輕量級的存儲類,特別適合用于保存軟件配置參數。SharedPreferences保存數據,其背后是用xml文件存放數據,文件存放在/data/data//shared_prefs目錄下:
????????一個簡單的存儲代碼如下:
SharedPreferences sharedPreferences = getSharedPreferences("wujay", Context.MODE_PRIVATE); //私有數據
Editor editor = sharedPreferences.edit(); ????//獲取編輯器
editor.putString("name", "wujaycode");
editor.putInt("age", 4);
editor.commit();????//提交修改
生成的wujay.xml文件內容如下:
2.2 使用方法
????????分析以下幾個方法:
2.2.1 一、getSharedPreferences(name, mode)
????????方法的第一個參數用于指定該文件的名稱,名稱不用帶后綴,后綴會由Android自動加上;方法的第二個參數指定文件的操作模式,共有四種操作模式。
????????四種操作模式分別為:
????1. MODE_APPEND: 追加方式存儲
????2. MODE_PRIVATE: 私有方式存儲,其他應用無法訪問
????3. MODE_WORLD_READABLE: 表示當前文件可以被其他應用讀取
????4. MODE_WORLD_WRITEABLE: 表示當前文件可以被其他應用寫入
2.2.2 二、edit()方法獲取editor對象
Editor editor = sharedPreferences.edit();
????????editor存儲對象采用key-value鍵值對進行存放,
editor.putString("name", "wujaycode");
????????通過commit()方法提交數據,與之對應的獲取數據的方法:
SharedPreferences share = getSharedPreferences("Acitivity", Activity.MODE_WORLD_READABLE);
int i = share.getInt("i", 0);
String str = share.getString("str", "");
boolean flag = share.getBoolean("flag", false);
????????getString()第二個參數為缺省值,如果preference中不存在該key,將返回缺省值。如果你想要刪除通過SharedPreferences產生的文件,可以通過以下方法:
File file = newFile("/data/data/"+getPackageName().toString()+"/shared_prefs", "Activity.xml");
if(file.exists()){
????file.delete();
????Toast.makeText(TestActivity.this, "刪除成功", Toast.LENGTH_LONG).show();
}
2.2.3 三、訪問其他應用中的Preference
????????如果要訪問其他應用中的Preference,必須滿足的條件是,要訪問的應用的Preference創建時指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE權限。
????????舉例,假如有個name>為com.wujay.action下面的應用使用了下面語句創建了Preference,getSharedPreferences("wujay", Context.MODE_WORLD_READABLE),
????????現在要訪問該Preferences:
????????首先,需要創建上面的Context,然后通過Context訪問Preferences,訪問preference時會在應用所在包下的shared_prefs目錄找到preference:
Context otherAppsContext = createPackageContext("com.wujay.action", Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences sharedPreferences = otherAppsContext.getSharedPreferences("wujay", Context.MODE_WORLD_READABLE);
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 0);
??? ????如果不通過創建Context訪問其他應用的preference,可以以讀取xml文件方式直接訪問其他應用preference對應的xml文件,如:
File xmlFile = new File(“/data/data/name>/shared_prefs/itcast.xml”); //應替換成應用的包名。
????????創建SharePreference
SharedPreferences settings = this.getSharedPreferences("TestXML", 0);
SharedPreferences.Editor localEditor = settings.edit();
//A_MAP03類必需要繼承了Activity的子類 才會有getSharedPreferences方法.
????????以鍵值<String Key, String Value> 方式加入數據:
localEditor.putBoolean("ShowNote", false);
IocalEditor.commit();
????????以 String Key 為索引來取出數據:
String str =settings.getString("ShowNote", "");
????????清除數據
localEditor.clear().commit();
SharedPreferences數據保存在:
????????存入XML后的內容目錄:/data/data/<包>/shared_prefs/***.xml
3 SQLite
Android中SQLite應用詳解
http://blog.csdn.net/liuhe688/article/details/6715983/
最受歡迎的5個Android ORM框架
http://www.codeceo.com/article/5-android-orm-framework.html
3.1 簡介
????????現在的主流移動設備像Android、iPhone等都使用SQLite作為復雜數據的存儲引擎,在我們為移動設備開發應用程序時,也許就要使用到SQLite來存儲我們大量的數據,所以我們就需要掌握移動設備上的SQLite開發技巧。對于Android平臺來說,系統內置了豐富的API來供開發人員操作SQLite,我們可以輕松的完成對數據的存取。
????????下面就向大家介紹一下SQLite常用的操作方法,為了方便,我將代碼寫在了Activity的onCreate中:
3.2 SQLite常用操作方法
3.2.1 Db創建
????@Override
????protected?void?onCreate(Bundle?savedInstanceState)?{
????????super.onCreate(savedInstanceState);
????????//打開或創建test.db數據庫
????????SQLiteDatabase?db?=?openOrCreateDatabase("test.db",?Context.MODE_PRIVATE,?null);
????????db.execSQL("DROP?TABLE?IF?EXISTS?person");
????????//創建person表
????????db.execSQL("CREATE?TABLE?person?(_id?INTEGER?PRIMARY?KEY?AUTOINCREMENT,?name?VARCHAR,?age?SMALLINT)");
????????Person?person?=?new?Person();
????????person.name?=?"john";
????????person.age?=?30;
????????//插入數據
????????db.execSQL("INSERT?INTO?person?VALUES?(NULL,??,??)",?new?Object[]{person.name,?person.age});
????????person.name?=?"david";
????????person.age?=?33;
????????//ContentValues以鍵值對的形式存放數據
????????ContentValues?cv?=?new?ContentValues();
????????cv.put("name",?person.name);
????????cv.put("age",?person.age);
????????//插入ContentValues中的數據
????????db.insert("person",?null,?cv);
????????cv?=?new?ContentValues();
????????cv.put("age",?35);
????????//更新數據
????????db.update("person",?cv,?"name?=??",?new?String[]{"john"});
????????Cursor?c?=?db.rawQuery("SELECT?*?FROM?person?WHERE?age?>=??",?new?String[]{"33"});
????????while(c.moveToNext())?{
????????????int?_id?=?c.getInt(c.getColumnIndex("_id"));
????????????String?name?=?c.getString(c.getColumnIndex("name"));
????????????int?age?=?c.getInt(c.getColumnIndex("age"));
????????????Log.i("db",?"_id=>"?+?_id?+?",?name=>"?+?name?+?",?age=>"+?age);
? ? }
? ? c.close();
? ? //刪除數據
? ? db.delete("person",?"age?<??",?new?String[]{"35"});
? ? //關閉當前數據庫
????db.close();
? ? //刪除test.db數據庫
????//??????deleteDatabase("test.db");
}?
????????在執行完上面的代碼后,系統就會在/data/data/[PACKAGE_NAME]/databases目錄下生成一個“test.db”的數據庫文件,如圖:
????????上面的代碼中基本上囊括了大部分的數據庫操作;對于添加、更新和刪除來說,我們都可以使用。
3.2.2 executeSQL
db.executeSQL(String?sql);
db.executeSQL(String?sql,?Object[]?bindArgs);//sql語句中使用占位符,然后第二個參數是實際的參數集
????????除了統一的形式之外,他們還有各自的操作方法:
3.2.3 增刪改
db.insert(String?table,?String?nullColumnHack,?ContentValues?values);
db.update(String?table,?Contentvalues?values,?String?whereClause,?String?whereArgs);
db.delete(String?table,?String?whereClause,?String?whereArgs);
????????以上三個方法的第一個參數都是表示要操作的表名;insert中的第二個參數表示如果插入的數據每一列都為空的話,需要指定此行中某一列的名稱,系統將此列設置為NULL,不至于出現錯誤;insert中的第三個參數是ContentValues類型的變量,是鍵值對組成的Map,key代表列名,value代表該列要插入的值;update的第二個參數也很類似,只不過它是更新該字段key為最新的value值,第三個參數whereClause表示WHERE表達式,比如“age> ? and age < ?”等,最后的whereArgs參數是占位符的實際參數值;delete方法的參數也是一樣。
3.2.4 查詢
????????下面來說說查詢操作。查詢操作相對于上面的幾種操作要復雜些,因為我們經常要面對著各種各樣的查詢條件,所以系統也考慮到這種復雜性,為我們提供了較為豐富的查詢形式:
db.rawQuery(String?sql,?String[]?selectionArgs);
db.query(String?table,?String[]?columns,?String?selection,?String[]?selectionArgs,?String?groupBy,?String?having,?String?orderBy);
db.query(String?table,?String[]?columns,?String?selection,?String[]?selectionArgs,?String?groupBy,?String?having,?String?orderBy,?String?limit);
db.query(String?distinct,?String?table,?String[]?columns,?String?selection,?String[]?selectionArgs,?String?groupBy,?String?having,?String?orderBy,?String?limit);
????????上面幾種都是常用的查詢方法,第一種最為簡單,將所有的SQL語句都組織到一個字符串中,使用占位符代替實際參數,selectionArgs就是占位符實際參數集;下面的幾種參數都很類似,columns表示要查詢的列所有名稱集,selection表示WHERE之后的條件語句,可以使用占位符,groupBy指定分組的列名,having指定分組條件,配合groupBy使用,orderBy指定排序的列名,limit指定分頁參數,distinct可以指定“true”或“false”表示要不要過濾重復值。需要注意的是,selection、groupBy、having、orderBy、limit這幾個參數中不包括“WHERE”、“GROUPBY”、“HAVING”、“ORDER BY”、“LIMIT”等SQL關鍵字。
????????最后,他們同時返回一個Cursor對象,代表數據集的游標,有點類似于JavaSE中的ResultSet。
????????下面是Cursor對象的常用方法:
c.move(int?offset);?//以當前位置為參考,移動到指定行
c.moveToFirst();????//移動到第一行
c.moveToLast();?????//移動到最后一行
c.moveToPosition(int?position);?//移動到指定行
c.moveToPrevious();?//移動到前一行
c.moveToNext();?????//移動到下一行
c.isFirst();????????//是否指向第一條
c.isLast();?????//是否指向最后一條
c.isBeforeFirst();??//是否指向第一條之前
c.isAfterLast();????//是否指向最后一條之后
c.isNull(int?columnIndex);??//指定列是否為空(列基數為0)
c.isClosed();???????//游標是否已關閉
c.getCount();???????//總數據項數
c.getPosition();????//返回當前游標所指向的行數
c.getColumnIndex(String?columnName);//返回某列名對應的列索引值
c.getString(int?columnIndex);???//返回當前行指定列的值
????????在上面的代碼示例中,已經用到了這幾個常用方法中的一些,關于更多的信息,大家可以參考官方文檔中的說明。
????????最后當我們完成了對數據庫的操作后,記得調用SQLiteDatabase的close()方法釋放數據庫連接,否則容易出現SQLiteException。
????????上面就是SQLite的基本應用,但在實際開發中,為了能夠更好的管理和維護數據庫,我們會封裝一個繼承自SQLiteOpenHelper類的數據庫操作類,然后以這個類為基礎,再封裝我們的業務邏輯方法。
3.2.5 封裝操作類DBHelper
? ? ? 下面,我們就以一個實例來講解具體的用法,我們新建一個名為db的項目,結構如下:
?????? 其中DBHelper繼承了SQLiteOpenHelper,作為維護和管理數據庫的基類,DBManager是建立在DBHelper之上,封裝了常用的業務方法,Person是我們的person表對應的JavaBean,MainActivity就是我們顯示的界面。
????????下面我們先來看一下DBHelper:
package?com.scott.db;
import?android.content.Context;
import?android.database.sqlite.SQLiteDatabase;
import?android.database.sqlite.SQLiteOpenHelper;
public?class?DBHelper?extends?SQLiteOpenHelper?{
????private?static?final?String?DATABASE_NAME?=?"test.db";
????private?static?final?int?DATABASE_VERSION?=?1;
????public?DBHelper(Context?context)?{
????????//CursorFactory設置為null,使用默認值
????????super(context,?DATABASE_NAME,?null,?DATABASE_VERSION);
}
????//數據庫第一次被創建時onCreate會被調用
????@Override
????public?void?onCreate(SQLiteDatabase?db)?{
????????db.execSQL("CREATE?TABLE?IF?NOT?EXISTS?person" + "(_id?INTEGER?PRIMARY KEY AUTOINCREMENT, name?VARCHAR,?age?INTEGER,?info?TEXT)");
}
????//如果DATABASE_VERSION值被改為2,系統發現現有數據庫版本不同,即會調用onUpgrade
????@Override
????public?void?onUpgrade(SQLiteDatabase?db,?int?oldVersion,?intnewVersion)?{
????????db.execSQL("ALTER?TABLE?person?ADD?COLUMN?other?STRING");
????}
}
??????? 正如上面所述,數據庫第一次創建時onCreate方法會被調用,我們可以執行創建表的語句,當系統發現版本變化之后,會調用onUpgrade方法,我們可以執行修改表結構等語句。
??????? 為了方便我們面向對象的使用數據,我們建一個Person類,對應person表中的字段,如下:
package?com.scott.db;
public?class?Person?{
????public?int_id;
????public?String?name;
????public?int?age;
????public?String?info;
????public?Person()?{
????}
????public?Person(String?name,?int?age,?String?info)?{
????????this.name?=?name;
????????this.age?=?age;
????????this.info?=?info;
????}
}
??????? 然后,我們需要一個DBManager,來封裝我們所有的業務方法,代碼如下:
package?com.scott.db;
import?java.util.ArrayList;
import?java.util.List;
import?android.content.ContentValues;
import?android.content.Context;
import?android.database.Cursor;
import?android.database.sqlite.SQLiteDatabase;
public?class?DBManager?{
????private?DBHelper?helper;
????private?SQLiteDatabase?db;
????public?DBManager(Context?context)?{
????????helper?=?new?DBHelper(context);
????????//因為getWritableDatabase內部調用了mContext.openOrCreateDatabase(mName,?0,?mFactory);
????????//所以要確保context已初始化,我們可以把實例化DBManager的步驟放在Activity的onCreate里
????????db?=?helper.getWritableDatabase();
????}
????/**
?????*?add?persons
?????*?@param?persons
?????*/
????public?void?add(List?persons)?{
????????db.beginTransaction();??//開始事務
????????try{
????????????for(Person?person?:?persons)?{
????????????????db.execSQL("INSERT?INTO?person?VALUES(null,??,??,??)",?new?Object[]{person.name,?person.age,?person.info});
????????????}
????????????db.setTransactionSuccessful();??//設置事務成功完成
????????}?finally{
????????????db.endTransaction();????//結束事務
????????}
????}
????/**
?????*?update?person's?age
?????*?@param?person
?????*/
????public?void?updateAge(Person?person)?{
????????ContentValues?cv?=?new?ContentValues();
????????cv.put("age",?person.age);
????????db.update("person",?cv,?"name?=??",?new?String[]{person.name});
????}
????/**
?????*?delete?old?person
?????*?@param?person
?????*/
????public?void?deleteOldPerson(Person?person)?{
????????db.delete("person",?"age?>=??",?new?String[]{String.valueOf(person.age)});
????}
????/**
?????*?query?all?persons,?return?list
?????*?@return?List
?????*/
????public?List?query()?{
????????ArrayList?persons?=?new?ArrayList();
????????Cursor?c?=?queryTheCursor();
????????while(c.moveToNext())?{
????????????Person?person?=?new?Person();
????????????person._id?=?c.getInt(c.getColumnIndex("_id"));
????????????person.name?=?c.getString(c.getColumnIndex("name"));
????????????person.age?=?c.getInt(c.getColumnIndex("age"));
????????????person.info?=?c.getString(c.getColumnIndex("info"));
????????????persons.add(person);
????????}
????????c.close();
????????return?persons;
????}
????/**
?????*?query?all?persons,?return?cursor
?????*?@return??Cursor
?????*/
????public?Cursor?queryTheCursor()?{
????????Cursor?c?=?db.rawQuery("SELECT?*?FROM?person",?null);
????????return?c;
????}
????/**
?????*?close?database
?????*/
????public?void?closeDB()?{
????????db.close();
????}
}
?????? 我們在DBManager構造方法中實例化DBHelper并獲取一個SQLiteDatabase對象,作為整個應用的數據庫實例;在添加多個Person信息時,我們采用了事務處理,確保數據完整性;最后我們提供了一個closeDB方法,釋放數據庫資源,這一個步驟在我們整個應用關閉時執行,這個環節容易被忘記,所以朋友們要注意。
??????? 我們獲取數據庫實例時使用了getWritableDatabase()方法,也許朋友們會有疑問,在getWritableDatabase()和getReadableDatabase()中,你為什么選擇前者作為整個應用的數據庫實例呢?在這里我想和大家著重分析一下這一點。
????????我們來看一下SQLiteOpenHelper中的getReadableDatabase()方法:
public?synchronized?SQLiteDatabase?getReadableDatabase()?{
????if?(mDatabase?!=?null?&&?mDatabase.isOpen())?{
????????//?如果發現mDatabase不為空并且已經打開則直接返回
????????return?mDatabase;
????}
????if(mIsInitializing)?{
????????//?如果正在初始化則拋出異常
????????throw?new?IllegalStateException("getReadableDatabase?called?recursively");
????}
????//?開始實例化數據庫mDatabase
????try{
????????//?注意這里是調用了getWritableDatabase()方法
????????return?getWritableDatabase();
????}?catch(SQLiteException?e)?{
????????if?(mName?==?null)
????????????throw?e;?//?Can't?open?a?temp?database?read-only!
????????Log.e(TAG,?"Couldn't?open?"?+?mName?+?"?for?writing?(will?try?read-only):",?e);
????}
????//?如果無法以可讀寫模式打開數據庫?則以只讀方式打開
????SQLiteDatabase?db?=?null;
????try{
????????mIsInitializing?=?true;
????????String?path?=?mContext.getDatabasePath(mName).getPath();//?獲取數據庫路徑
????????//?以只讀方式打開數據庫
????????db?=?SQLiteDatabase.openDatabase(path,?mFactory,?SQLiteDatabase.OPEN_READONLY);
????????if(db.getVersion()?!=?mNewVersion)?{
????????????throw?new?SQLiteException("Can't?upgrade?read -only database?from?version " + db.getVersion() +?"?to?" +?mNewVersion?+?":?"+?path);
????????}
????????onOpen(db);
????????Log.w(TAG,?"Opened?"?+?mName?+?"?in?read-only?mode");
????????mDatabase?=?db;//?為mDatabase指定新打開的數據庫
????????return?mDatabase;//?返回打開的數據庫
????}?finally{
????????mIsInitializing?=?false;
????????if?(db?!=?null&&?db?!=?mDatabase)
????????????db.close();
????}
}
??????? 在getReadableDatabase()方法中,首先判斷是否已存在數據庫實例并且是打開狀態,如果是,則直接返回該實例,否則試圖獲取一個可讀寫模式的數據庫實例,如果遇到磁盤空間已滿等情況獲取失敗的話,再以只讀模式打開數據庫,獲取數據庫實例并返回,然后為mDatabase賦值為最新打開的數據庫實例。既然有可能調用到getWritableDatabase()方法,我們就要看一下了:
public?synchronized?SQLiteDatabase?getWritableDatabase()?{
????if?(mDatabase?!=?null?&&?mDatabase.isOpen()?&&?!mDatabase.isReadOnly())?{
????????//?如果mDatabase不為空已打開并且不是只讀模式?則返回該實例
????????return?mDatabase;
????}
????if(mIsInitializing)?{
????????throw?new?IllegalStateException("getWritableDatabase?called?recursively");
????}
????//?If?we?have?a?read-only?database?open,?someone?could?be?using?it
????//?(though?they?shouldn't),?which?would?cause?a?lock?to?be?held?on
????//?the?file,?and?our?attempts?to?open?the?database?read-write?would
????//?fail?waiting?for?the?file?lock.?To?prevent?that,?we?acquire?the
????//?lock?on?the?read-only?database,?which?shuts?out?other?users.
????boolean?success?=?false;
????SQLiteDatabase?db?=?null;
????//?如果mDatabase不為空則加鎖?阻止其他的操作
????if?(mDatabase?!=?null)
????????mDatabase.lock();
????try{
????????mIsInitializing?=?true;
????????if?(mName?==?null)?{
????????????db?=?SQLiteDatabase.create(null);
????????}?else{
????????????//?打開或創建數據庫
????????????db?=?mContext.openOrCreateDatabase(mName,?0,?mFactory);
????????}
????????//?獲取數據庫版本(如果剛創建的數據庫,版本為0)
????????int?version?=?db.getVersion();
????????//?比較版本(我們代碼中的版本mNewVersion為1)
????????if(version?!=?mNewVersion)?{
????????????db.beginTransaction();????//?開始事務
????????????try{
????????????????if?(version?==?0)?{
????????????????????//?執行我們的onCreate方法
????????????????????onCreate(db);
????????????????}?else{
????????????????????//?如果我們應用升級了mNewVersion為2,而原版本為1則執行onUpgrade方法
????????????????????onUpgrade(db,?version,?mNewVersion);
????????????????}
????????????????db.setVersion(mNewVersion);//?設置最新版本
????????????????db.setTransactionSuccessful();//?設置事務成功
????????????}?finally{
????????????????db.endTransaction();//?結束事務
????????????}
????????}
????????onOpen(db);
????????success?=?true;
????????return?db;//?返回可讀寫模式的數據庫實例
????}?finally{
????????mIsInitializing?=?false;
????????if(success)?{
????????????//?打開成功
????????????if?(mDatabase?!=?null)?{
????????????????//?如果mDatabase有值則先關閉
????????????????try{
????????????????????mDatabase.close();
????????????????}?catch(Exception?e)?{
????????????????}
????????????????mDatabase.unlock();//?解鎖
????????????}
????????????mDatabase?=?db;//?賦值給mDatabase
????????}?else{
????????????//?打開失敗的情況:解鎖、關閉
????????????if?(mDatabase?!=?null)
????????????????mDatabase.unlock();
????????????if?(db?!=?null)
????????????????db.close();
? ? ? ? }
????}
}
??????? 大家可以看到,幾個關鍵步驟是,首先判斷mDatabase如果不為空已打開并不是只讀模式則直接返回,否則如果mDatabase不為空則加鎖,然后開始打開或創建數據庫,比較版本,根據版本號來調用相應的方法,為數據庫設置新版本號,最后釋放舊的不為空的mDatabase并解鎖,把新打開的數據庫實例賦予mDatabase,并返回最新實例。
3.3 Realm
4 ContentProvider
Android ContentProvider的介紹(很詳細)
http://xiechengfa.iteye.com/blog/1415829
android四大組件--ContentProvider詳解
http://www.2cto.com/kf/201404/296974.html
ContentProvider總結(Android)
http://blog.csdn.net/chuyuqing/article/details/39995607
4.1 ContentProvider簡介
??? ????ContentProvider:為存儲和獲取數據提供統一的接口。可以在不同的應用程序之間共享數據。Android已經為常見的一些數據提供了默認的ContentProvider。
??? 1、ContentProvider使用表的形式來組織數據
??? ????????無論數據的來源是什么,ContentProvider都會認為是一種表,然后把數據組織成表格。
??? 2、ContentProvider提供的方法
?? ?????query:查詢
?????? ?insert:插入
?? ?????update:更新
?? ?????delete:刪除
?? ?????getType:得到數據類型
?? ?????onCreate:創建數據時調用的回調函數
?? 3、每個ContentProvider都有一個公共的URI,這個URI用于表示這個ContentProvider所提供的數據。Android所提供的ContentProvider都存放在android.provider包當中。
4.1.1 使用ContentProvider共享數據
????1)ContentProvider類主要方法的作用:
public boolean onCreate():
??????? 該方法在ContentProvider創建后就會被調用,Android開機后,ContentProvider在其它應用第一次訪問它時才會被創建。
public Uri insert(Uri uri, ContentValues values):
??????? 該方法用于供外部應用往ContentProvider添加數據。
public int delete(Uri uri, String selection, String[] selectionArgs):
??????? 該方法用于供外部應用從ContentProvider刪除數據。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):
??????? 該方法用于供外部應用更新ContentProvider中的數據。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):
??????? 該方法用于供外部應用從ContentProvider中獲取數據。
public String getType(Uri uri):
??????? 該方法用于返回當前Url所代表數據的MIME類型。
2)如果操作的數據屬于集合類型,那么MIME類型字符串應該以vnd.android.cursor.dir/開頭,
????????例如:要得到所有person記錄的Uri為content://com.bing.provider.personprovider/person,那么返回的MIME類型字符串應該為:"vnd.android.cursor.dir/person"。
3)如果要操作的數據屬于非集合類型數據,那么MIME類型字符串應該以vnd.android.cursor.item/開頭,
????????例如:得到id為10的person記錄,Uri為content://com.bing.provider.personprovider/person/10,那么返回的MIME類型字符串為:"vnd.android.cursor.item/person"。
4.1.2 ContentResolver操作ContentProvider中的數據
????????1)當外部應用需要對ContentProvider中的數據進行添加、刪除、修改和查詢操作時,可以使用ContentResolver 類來完成,要獲取ContentResolver 對象,可以使用Activity提供的getContentResolver()方法。
????????2)ContentResolver類提供了與ContentProvider類相同簽名的四個方法:
public Uri insert(Uri uri, ContentValues values):該方法用于往ContentProvider添加數據。
public int delete(Uri uri, String selection, String[] selectionArgs):該方法用于從ContentProvider刪除數據。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):該方法用于更新ContentProvider中的數據。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):該方法用于從ContentProvider中獲取數據。
??? ????這些方法的第一個參數為Uri,代表要操作的ContentProvider和對其中的什么數據進行操作,其實和contentprovider里面的方法是一樣的。他們所對應的數據,最終是會被傳到我們在之前程序里面定義的那個contentprovider類的方法,假設給定的是:Uri.parse("content://com.bing.providers.personprovider/person/10"),那么將會對主機名為com.bing.providers.personprovider的ContentProvider進行操作,操作的數據為person表中id為10的記錄。
??????? 使用ContentResolver對ContentProvider中的數據進行添加、刪除、修改和查詢操作:
ContentResolver resolver =? getContentResolver();
Uri uri = Uri.parse("content://com.bing.provider.personprovider/person");
//添加一條記錄
ContentValues values = newContentValues();
values.put("name", "bingxin");
values.put("age", 25);
resolver.insert(uri, values);?
//獲取person表中所有記錄
Cursor cursor = resolver.query(uri, null, null, null, "personid desc");
while(cursor.moveToNext()){
??????Log.i("ContentTest", "personid=" + cursor.getInt(0)+", name=" + cursor.getString(1));
}
//把id為1的記錄的name字段值更改新為zhangsan
ContentValues updateValues = newContentValues();
updateValues.put("name", "zhangsan");
Uri updateIdUri = ContentUris.withAppendedId(uri, 2);
resolver.update(updateIdUri, updateValues, null, null);
//刪除id為2的記錄
Uri deleteIdUri =ContentUris.withAppendedId(uri, 2);
resolver.delete(deleteIdUri, null, null);
4.1.3 監聽ContentProvider中數據的變化
??????? 如果ContentProvider的訪問者需要知道ContentProvider中的數據發生變化,可以在ContentProvider發生數據變化時調用getContentResolver().notifyChange(uri, null)來通知注冊在此URI上的訪問者,例子如下:
public class PersonContentProviderextends ContentProvider {
? ? public Uri insert(Uri uri, ContentValues values) {
? ? ? ? db.insert("person", "personid", values);
? ? ? ? getContext().getContentResolver().notifyChange(uri, null);
? ? }
}
??????? 如果ContentProvider的訪問者需要得到數據變化通知,必須使用ContentObserver對數據(數據采用uri描述)進行監聽,當監聽到數據變化通知時,系統就會調用ContentObserver的onChange()方法:
getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"), true, new PersonObserver(new Handler()));
public class PersonObserver extends ContentObserver{
? ? public PersonObserver(Handler handler) {
? ? ? ? super(handler);
? ? }
? ? public void onChange(boolean selfChange) {
?????????//此處可以進行相應的業務處理
? ? ?}
}
4.2 Uri介紹
4.2.1 Uri簡介???
????????Uri為系統的每一個資源給其一個名字,比方說通話記錄。
????1)每一個ContentProvider都擁有一個公共的URI,這個URI用于表示這個ContentProvider所提供的數據。
????2)Android所提供的ContentProvider都存放在android.provider包中。 將其分為A,B,C,D 4個部分:
????????A:標準前綴,用來說明一個Content Provider控制這些數據,無法改變的"content://";
????????B:URI 的標識,用于唯一標識這個ContentProvider,外部調用者可以根據這個標識來找到它。它定義了是哪個Content Provider提供這些數據。對于第三方應用程序,為了保證URI標識的唯一性,它必須是一個完整的、小寫的類名。這個標識在 元素的 authorities屬性中說明:一般是定義該ContentProvider的包.類的名稱;
????????C:路徑(path),通俗的講就是你要操作的數據庫中表的名字,或者你也可以自己定義,記得在使用的時候保持一致就可以了;"content://com.bing.provider.myprovider/tablename";
????????D:如果URI中包含表示需要獲取的記錄的ID;則就返回該id對應的數據,如果沒有ID,就表示返回全部;"content://com.bing.provider.myprovider/tablename/#" #表示數據id。
? ??PS:
???????路徑(path)可以用來表示我們要操作的數據,路徑的構建應根據業務而定,如下:
????1、要操作person表中id為10的記錄,可以構建這樣的路徑:/person/10
????2、要操作person表中id為10的記錄的name字段,person/10/name
????3、要操作person表中的所有記錄,可以構建這樣的路徑:/person
????4、要操作xxx表中的記錄,可以構建這樣的路徑:/xxx
????5、當然要操作的數據不一定來自數據庫,也可以是文件、xml或網絡等其他存儲方式,如下: 要操作xml文件中person節點下的name節點,可以構建這樣的路徑:/person/name
????6、如果要把一個字符串轉換成Uri,可以使用Uri類中的parse()方法,如下:Uri uri = Uri.parse("content://com.bing.provider.personprovider/person")
4.2.2 UriMatcher類使用介紹
??????? 因為Uri代表了要操作的數據,所以我們經常需要解析Uri,并從Uri中獲取數據。Android系統提供了兩個用于操作Uri的工具類,分別為UriMatcher和ContentUris 。掌握它們的使用,會便于我們的開發工作。
????????UriMatcher類用于匹配Uri,它的用法如下:
????????首先第一步把你需要匹配Uri路徑全部給注冊上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼
UriMatcher? sMatcher = newUriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.bing.procvide.personprovider/person路徑,返回匹配碼為1
sMatcher.addURI("com.bing.procvide.personprovider", "person", 1);//添加需要匹配uri,如果匹配就會返回匹配碼
//如果match()方法匹配content://com.bing.provider.personprovider/person/230路徑,返回匹配碼為2
sMatcher.addURI("com.bing.provider.personprovider", "person/#", 2);//#號為通配符
switch(sMatcher.match(Uri.parse("content://com.ljq.provider.personprovider/person/10"))){
??????case 1
????????break;
??????case 2
????????break;
??????default://不匹配
????????break;
}
??????? 注冊完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法對輸入的Uri進行匹配,如果匹配就返回匹配碼,匹配碼是調用addURI()方法傳入的第三個參數,假設匹配content://com.ljq.provider.personprovider/person路徑,返回的匹配碼為1
4.2.3 ContentUris類使用介紹
??????? ContentUris類用于操作Uri路徑后面的ID部分,它有兩個比較實用的方法:
????withAppendedId(uri, id)用于為路徑加上ID部分:
Uri uri = Uri.parse("content://com.bing.provider.personprovider/person");
Uri resultUri = ContentUris.withAppendedId(uri, 10);
//生成后的Uri為:content://com.bing.provider.personprovider/person/10
parseId(uri)方法用于從路徑中獲取ID部分:
Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person/10");
long personid = ContentUris.parseId(uri);????//獲取的結果為:10
4.3 ContentProvider示例代碼
????自定義一個ContentProvider,來實現內部原理
?? 步驟:
?1、定義一個CONTENT_URI常量(里面的字符串必須是唯一)
?? Public static final Uri CONTENT_URI =Uri.parse("content://com.WangWeiDa.MyContentprovider");
?? 如果有子表,URI為:
?? Publicstatic final Uri CONTENT_URI = Uri.parse("content://com.WangWeiDa.MyContentProvider/users");
?2、定義一個類,繼承ContentProvider
?? Publicclass MyContentProvider extends ContentProvider
?3、實現ContentProvider的所有方法(query、insert、update、delete、getType、onCreate)
?? package com.WangWeiDa.cp;
?? import java.util.HashMap;
?? import com.WangWeiDa.cp.MyContentProviderMetaData.UserTableMetaData;
?? import com.WangWeiDa.data.DatabaseHelp;
?? import android.content.ContentProvider;
?? import android.content.ContentUris;
?? import android.content.ContentValues;
?? import android.content.UriMatcher;
?? import android.database.Cursor;
?? import android.database.sqlite.SQLiteDatabase;
?? import android.database.sqlite.SQLiteQueryBuilder;
?? import android.net.Uri;
?? import android.text.TextUtils;
?? public class MyContentProvider extends ContentProvider {
? ?????//訪問表的所有列
?? ?????public static final intINCOMING_USER_COLLECTION = 1;
?? ?????//訪問單獨的列
?? ?????public static final int INCOMING_USER_SINGLE =2;
?? ?????//操作URI的類
?? ?????public static final UriMatcher uriMatcher;
?? ?????//為UriMatcher添加自定義的URI
?? ?????static{
?? ?????????uriMatcher = newUriMatcher(UriMatcher.NO_MATCH);
?? ?????????uriMatcher.addURI(MyContentProviderMetaData.AUTHORITIES,"/user",
?? ?INCOMING_USER_COLLECTION);
?? ?????????uriMatcher.addURI(MyContentProviderMetaData.AUTHORITIES,"/user/#",
?? ?INCOMING_USER_SINGLE);
?? ?}
?? ?private DatabaseHelp dh;
?? ?//為數據庫表字段起別名
?? ?public static HashMap userProjectionMap;
?? ?static
?? ?{
?? ?????userProjectionMap = new HashMap();
?? ?????userProjectionMap.put(UserTableMetaData._ID, UserTableMetaData._ID);
?? ?????userProjectionMap.put(UserTableMetaData.USER_NAME, UserTableMetaData.USER_NAME);
?? ?}
?? ?/**
?? ?*刪除表數據
?? ?*/
?? ?@Override
?? ?public int delete(Uri uri, String selection, String[] selectionArgs) {
?? ?????System.out.println("delete");
?? ?????//得到一個可寫的數據庫
?? ?????SQLiteDatabase db = dh.getWritableDatabase();
?? ?????//執行刪除,得到刪除的行數
?? ?????int count = db.delete(UserTableMetaData.TABLE_NAME, selection, selectionArgs);
?? ?????return count;
?? ?}
?? ?/**
?? ?*數據庫訪問類型
?? ?*/
?? ?@Override
?? ?public String getType(Uri uri) {
?? ?????System.out.println("getType");
?? ?????//根據用戶請求,得到數據類型
?? ?????switch (uriMatcher.match(uri)) {
?? ?????????case INCOMING_USER_COLLECTION:
?? ?????????????return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE;
?? ?????????case INCOMING_USER_SINGLE:
?? ?????????????return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE_ITEM;
?? ?????????default:
?? ?????????????throw newIllegalArgumentException("UnKnown URI" + uri);
?? ?????}
?? ?}
?? ?/**
?? ?*插入數據
?? ?*/
?? ?@Override
?? ?public Uri insert(Uri uri, ContentValuesvalues) {
?? ?????//得到一個可寫的數據庫
?? ?????SQLiteDatabase db = dh.getWritableDatabase();
?? ?????//向指定的表插入數據,得到返回的Id
?? ?????long rowId = db.insert(UserTableMetaData.TABLE_NAME, null, values);
?? ?????if(rowId > 0){????//判斷插入是否執行成功
?? ?????????//如果添加成功,利用新添加的Id和
?? ?????????Uri insertedUserUri = ContentUris.withAppendedId(UserTableMetaData.CONTENT_URI, rowId);
?? ?????????//通知監聽器,數據已經改變
?? ?????????getContext().getContentResolver().notifyChange(insertedUserUri, null);
?? ?????????return insertedUserUri;
?? ?????}
?? ?????return uri;
?? ?}
?? ?/**
?? ?*創建ContentProvider時調用的回調函數
?? ?*/
?? ?@Override
?? ?public boolean onCreate() {
?? ?????System.out.println("onCreate");
?? ?????//得到數據庫幫助類
?? ?????dh = newDatabaseHelp(getContext(), MyContentProviderMetaData.DATABASE_NAME);
?? ?????return false;
?? ?}
?? ?/**
?? ?*查詢數據庫
?? ?*/
?? ?@Override
?? ?public Cursor query(Uri uri, String[]projection, String selection, String[] selectionArgs, String sortOrder) {
?? ?????//創建一個執行查詢的Sqlite
?? ?????SQLiteQueryBuilder qb = newSQLiteQueryBuilder();
?? ?????//判斷用戶請求,查詢所有還是單個
?? ?????switch(uriMatcher.match(uri)){
?? ?????????case INCOMING_USER_COLLECTION:
?? ?????????????//設置要查詢的表名
?? ?????????????qb.setTables(UserTableMetaData.TABLE_NAME);
?? ?????????????//設置表字段的別名
?? ?????????????qb.setProjectionMap(userProjectionMap);
?? ?????????????break;
?? ?????????case INCOMING_USER_SINGLE:
?? ?????????????qb.setTables(UserTableMetaData.TABLE_NAME);
?? ?????????????qb.setProjectionMap(userProjectionMap);
?? ?????????????//追加條件,getPathSegments()得到用戶請求的Uri地址截取的數組,get(1)得到去掉地址中/以后的第二個元素
?? ?????????????qb.appendWhere(UserTableMetaData._ID + "=" + uri.getPathSegments().get(1));
?? ?????????????break;
?? ?????????}
?? ?????????//設置排序
?? ?????????String orderBy;
?? ?????????if(TextUtils.isEmpty(sortOrder)){
?? ?????????????orderBy = UserTableMetaData.DEFAULT_SORT_ORDER;
?? ?????????}
?? ?????????else{
?? ?????????????orderBy = sortOrder;
?? ?????????}
?? ?????????//得到一個可讀的數據庫
?? ?????????SQLiteDatabase db = dh.getReadableDatabase();
?? ?????????//執行查詢,把輸入傳入
?? ?????????Cursor c = qb.query(db, projection, selection,selectionArgs, null, null, orderBy);
?? ?????????//設置監聽
?? ?????????c.setNotificationUri(getContext().getContentResolver(), uri);
?? ?????????return c;
?? ?????}
?? ?????/**
?? ?????*更新數據庫
?? ?????*/
?? ?????@Override
?? ?????public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
?? ?????????System.out.println("update");
?? ?????????//得到一個可寫的數據庫
?? ?????????SQLiteDatabase db = dh.getWritableDatabase();
?? ?????????//執行更新語句,得到更新的條數
?? ?????????int count =db.update(UserTableMetaData.TABLE_NAME, values, selection, selectionArgs);
?? ?????????return count;
?? ?????}
?? }
?? 4、在AndroidMinifest.xml中進行聲明
<android:name = ".cp.MyContentProvider"
android:authorities = "com.WangWeiDa.cp.MyContentProvider" />
? ?//為ContentProvider提供一個常量類MyContentProviderMetaData.java
?? package com.WangWeiDa.cp;
?? import android.net.Uri;
?? import android.provider.BaseColumns;
?? public class MyContentProviderMetaData {
?? ????//URI的指定,此處的字符串必須和聲明的authorities一致
?? ?????public static final String AUTHORITIES = "com.wangweida.cp.MyContentProvider";
?? ?????//數據庫名稱
?? ?????public static final String DATABASE_NAME = "myContentProvider.db";
?? ?????//數據庫的版本
?? ?????public static final int DATABASE_VERSION = 1;
?? ?????//表名
?? ?????public static final String USERS_TABLE_NAME = "user";
?? ?????public static final class UserTableMetaData implements BaseColumns{
?? ?????????//表名
?? ?????????public static final String TABLE_NAME ="user";
?? ?????????//訪問該ContentProvider的URI
?? ?????????public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIES + "/user");
?? ?????????//該ContentProvider所返回的數據類型的定義
?? ?????????public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.myprovider.user";
?? ?????????public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.myprovider.user";
?? ?????????//列名
?? ?????????public static final String USER_NAME = "name";
?? ?????????//默認的排序方法
?? ?????????public static final String DEFAULT_SORT_ORDER = "_id desc";
?? ?????}
?? }
5 參考鏈接
Android--sharepreference總結
http://blog.csdn.net/wulianghuan/article/details/8501063
Android中SharePreference的使用
http://www.cnblogs.com/wanqieddy/archive/2012/04/07/2436299.html
android--存儲之SharePreference
http://blog.csdn.net/jie1991liu/article/details/8665479
Android中SQLite應用詳解
http://blog.csdn.net/liuhe688/article/details/6715983/
最受歡迎的5個Android ORM框架
http://www.codeceo.com/article/5-android-orm-framework.html
Android ContentProvider的介紹(很詳細)
http://xiechengfa.iteye.com/blog/1415829
android四大組件--ContentProvider詳解
http://www.2cto.com/kf/201404/296974.html
ContentProvider總結(Android)