国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

《移動互聯(lián)網(wǎng)技術(shù)》 第七章 數(shù)據(jù)存取: 掌握File、SharePreferences、SQLite和ContentProvider四種數(shù)據(jù)存取方式

這篇具有很好參考價值的文章主要介紹了《移動互聯(lián)網(wǎng)技術(shù)》 第七章 數(shù)據(jù)存取: 掌握File、SharePreferences、SQLite和ContentProvider四種數(shù)據(jù)存取方式。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

《移動互聯(lián)網(wǎng)技術(shù)》 第七章 數(shù)據(jù)存取: 掌握File、SharePreferences、SQLite和ContentProvider四種數(shù)據(jù)存取方式,移動互聯(lián)網(wǎng)技術(shù),sqlite,數(shù)據(jù)庫

???? 博主 libin9iOak帶您 Go to New World.???
?? 個人主頁——libin9iOak的博客??
?? 《面試題大全》 文章圖文并茂??生動形象??簡單易學(xué)!歡迎大家來踩踩~??
?? 《IDEA開發(fā)秘籍》學(xué)會IDEA常用操作,工作效率翻倍~??
???? 希望本文能夠給您帶來一定的幫助??文章粗淺,敬請批評指正!????

《移動互聯(lián)網(wǎng)技術(shù)》課程簡介

《移動互聯(lián)網(wǎng)技術(shù)》課程是軟件工程、電子信息等專業(yè)的專業(yè)課,主要介紹移動互聯(lián)網(wǎng)系統(tǒng)及應(yīng)用開發(fā)技術(shù)。課程內(nèi)容主要包括移動互聯(lián)網(wǎng)概述、無線網(wǎng)絡(luò)技術(shù)、無線定位技術(shù)、Android應(yīng)用開發(fā)和移動應(yīng)用項目實踐等五個部分。移動互聯(lián)網(wǎng)概述主要介紹移動互聯(lián)網(wǎng)的概況和發(fā)展,以及移動計算的特點。無線網(wǎng)絡(luò)技術(shù)部分主要介紹移動通信網(wǎng)絡(luò)(包括2G/3G/4G/5G技術(shù))、無線傳感器網(wǎng)絡(luò)、Ad hoc網(wǎng)絡(luò)、各種移動通信協(xié)議,以及移動IP技術(shù)。無線定位技術(shù)部分主要介紹無線定位的基本原理、定位方法、定位業(yè)務(wù)、數(shù)據(jù)采集等相關(guān)技術(shù)。Android應(yīng)用開發(fā)部分主要介紹移動應(yīng)用的開發(fā)環(huán)境、應(yīng)用開發(fā)框架和各種功能組件以及常用的開發(fā)工具。移動應(yīng)用項目實踐部分主要介紹移動應(yīng)用開發(fā)過程、移動應(yīng)用客戶端開發(fā)、以及應(yīng)用開發(fā)實例。
課程的教學(xué)培養(yǎng)目標如下:
1.培養(yǎng)學(xué)生綜合運用多門課程知識以解決工程領(lǐng)域問題的能力,能夠理解各種移動通信方法,完成移動定位算法的設(shè)計。
2.培養(yǎng)學(xué)生移動應(yīng)用編程能力,能夠編寫Andorid應(yīng)用的主要功能模塊,并掌握移動應(yīng)用的開發(fā)流程。
3. 培養(yǎng)工程實踐能力和創(chuàng)新能力。
 通過本課程的學(xué)習(xí)應(yīng)達到以下目的:
1.掌握移動互聯(lián)網(wǎng)的基本概念和原理;
2.掌握移動應(yīng)用系統(tǒng)的設(shè)計原則;
3.掌握Android應(yīng)用軟件的基本編程方法;
4.能正確使用常用的移動應(yīng)用開發(fā)工具和測試工具。

第七章 數(shù)據(jù)存取

本章小結(jié):

1**、本單元學(xué)習(xí)目的**

通過學(xué)習(xí)四種數(shù)據(jù)存取方法,重點掌握文件系統(tǒng)的內(nèi)部存儲和外部存儲**;掌握用于存取配置信息等小批量數(shù)據(jù)的SharePreferences;掌握數(shù)據(jù)庫SQLite的添加,查詢,更新和刪除操作;**掌握用于應(yīng)用程序之間交換數(shù)據(jù)的ContentProvider組件;掌握XML格式數(shù)據(jù)的Pull和SAX兩種解析方法,以及JSON格式數(shù)據(jù)的JSONObject和GSON兩種解析方法。

2**、本單元學(xué)習(xí)要求**

(1) 了解數(shù)據(jù)存取的權(quán)限管理;

(2) 掌握四種數(shù)據(jù)存取方式:File、SharePreferences、SQLite和ContentProvider;

(3) 掌握不同格式數(shù)據(jù)解析程序的編寫。

3**、本單元學(xué)習(xí)方法**

結(jié)合教材以及Android Studio開發(fā)軟件,對File、SharePreferences、SQLite和ContentProvider等模塊進行編程練習(xí),運行調(diào)試,并在模擬器中觀察運行情況。

4**、本單元重點難點分析**

重點

(1) 四種數(shù)據(jù)存取方式

1) 文件操作

在Android中,可以通過文件流對象來操作文件。要獲取文件流對象需要用openFileInput() 或openFileOutput() 函數(shù)來打開文件,并且需要將文件的名稱傳給文件輸入流。注意這里指定的文件名不可以包含路徑,因為所有的文件都是默認存儲到/data/data//files/ 目錄下。

FileInputStream in = null;

// 文件名KnowledgeUnit

in = openFileInput(“KnowledgeUnit”);

FileOutputStream out = null;

out = openFileOutput(“KnowledgeUnit”, Context.MODE_PRIVATE);

輸出流還要指定文件操作的模式。使用MODE_PRIVATE模式,文件是私有數(shù)據(jù),只能被應(yīng)用本身訪問。在該模式下,寫入的內(nèi)容會覆蓋原文件的內(nèi)容。MODE_APPEND模式會檢查文件是否存在,存在就往文件里追加內(nèi)容,否則就創(chuàng)建新文件。

先通過openFileOutput函數(shù)獲取FileOutputStream 對象。給openFileOutput()函數(shù)傳入要讀取的文件名;然后系統(tǒng)會自動到/data/data//files/目錄下去加載這個文件,并返回一個FileInputStream 對象,接下來再通過 Java 流的方式讀取數(shù)據(jù)。接著用FileOutputStream構(gòu)造BufferedWriter,寫入文本到輸出流。BufferedWriter通過緩存的方式來寫入以提高存儲效率。

public void save(String inputKPoint) {
FileOutputStream out = null;
BufferedWriter writer = null;

try {
out = openFileOutput(fileName, Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(inputKPoint);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null)
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

文件中讀取數(shù)據(jù),首先獲取文件輸入流,然后構(gòu)造讀取數(shù)據(jù)的對象reader。BufferedReader是一行一行讀取數(shù)據(jù),每讀取一行就把它連接在一起,然后存儲在content對象中。

private String fileName = "KnowledgeUnit";

FileInputStream in = null;

BufferedReader reader = null;

 

StringBuilder content = new StringBuilder();

try {

?    in = openFileInput(fileName);

?    reader = new BufferedReader(new InputStreamReader(in));

?    String line = "";

?     while ((line = reader.readLine()) != null) {

?      content.append(line);

  }

}

return content.toString();

2) SharedPreferences

在Windows系統(tǒng)中,通常會用ini文件來保存一些參數(shù)信息。在Andriod系統(tǒng)中,比如在微信里可以設(shè)置加朋友時是否需要驗證,這就是應(yīng)用的參數(shù)設(shè)置。Android系統(tǒng)提供了一個輕量級的數(shù)據(jù)存取工具:SharedPreferences類,它適合用來保存應(yīng)用的各種配置信息。

SharedPreferences非常輕量化,主要用來存儲少量的信息。它的核心思想是在xml文件中通過鍵值對(通常也稱為map)來存取數(shù)據(jù)。SharedPreferences采用文件的讀寫方式,文件存放在“/data/data//shared_prefs”目錄下。

在SharedPreferences中存儲數(shù)據(jù)一共有四個步驟:首先,獲取一個SharedPreferences對象,然后再獲取一個Editor對象;通過Editor就可以向SharedPreferences中存放數(shù)據(jù);最后,需要調(diào)用commit函數(shù)來完成數(shù)據(jù)的存儲。

要保存SharedPreferences數(shù)據(jù),先要獲取SharedPreferences.Editor對象,然后通過editor的各種put函數(shù)來寫入數(shù)據(jù),比如寫入字符串、整數(shù)、布爾變量等等;最后,完成提交。

btnSave.setOnClickListener(new View.OnClickListener() {

  @Override

  public void onClick(View v) {

?    SharedPreferences.Editor editor = 

?          getSharedPreferences(id, MODE_PRIVATE).edit();

?    editor.putString("昵稱", "libin9iOak");

?    editor.putString("簡介", "Android初學(xué)者");

?    editor.putInt("年齡", 22);

?    editor.putBoolean("是否已選課", true);

?    editor.commit();

  }

});

接下來從SharedPreferences中取出數(shù)據(jù)。也是先獲取SharedPreferences對象,然后調(diào)用針對不同數(shù)據(jù)類型的get函數(shù)來獲取剛才存儲的字符串、整數(shù)、以及布爾變量。各種get 函數(shù)都接收兩個參數(shù),第一個參數(shù)是鍵值,通過它就可以取得對應(yīng)的數(shù)據(jù);第二個參數(shù)是默認值,如果傳入的鍵值找不到對應(yīng)的值,就以該默認值作為返回值。最后,為了查看是否獲取成功,可以通過打印日志的方式來顯示獲取的結(jié)果。

private String id = “me”;

SharedPreferences pref = getSharedPreferences(id, MODE_PRIVATE);

String nickname = pref.getString(“昵稱”, “”);

String briefIntro = pref.getString(“簡介”, “”);

int age = pref.getInt(“年齡”, 0);

boolean signUp = pref.getBoolean(“是否已選課”, false);

Log.d(TAG, "昵稱: " + nickname);

Log.d(TAG, "簡介: " + briefIntro);

Log.d(TAG, “年齡:” + age);

Log.d(TAG, "是否已選課: " + signUp);

3) SQLite****數(shù)據(jù)庫

SQLite 是2000年,D. Richard Hipp 開發(fā)的一種中小型嵌入式數(shù)據(jù)庫。作為一個輕量級的關(guān)系型數(shù)據(jù)庫,SQLite運算速度非常快,占用資源少,通常只需要幾百 K 的內(nèi)存,適合在移動設(shè)備上使用。SQLite不僅支持標準的 SQL 語法,還遵循數(shù)據(jù)庫的原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)原則。它是進程內(nèi)的數(shù)據(jù)庫引擎,因此不存在數(shù)據(jù)庫的客戶端和服務(wù)器。數(shù)據(jù)庫中所有的信息(比如表、視圖等)都包含在一個文件中。這個文件可以自由復(fù)制到其它目錄或其它機器上。

首先使用 DBQuizHelper類在SQLite中創(chuàng)建數(shù)據(jù)庫。DBQuizHelper類是自定義的一個操作數(shù)據(jù)庫的類。在SQLiteActivity中,給它的構(gòu)造函數(shù)傳入數(shù)據(jù)庫的名稱:Exam.db。然后,在“創(chuàng)建數(shù)據(jù)庫”按鈕按下時,調(diào)用getWritableDatabase() 函數(shù)完成數(shù)據(jù)庫的創(chuàng)建。

private DBQuizHelper dbQuizHelper;

dbQuizHelper = new DBQuizHelper(this, “Exam.db”, null, 2);

Button btnCreateDB = (Button) findViewById(R.id.create_database);

btnCreateDB.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

? dbQuizHelper.getWritableDatabase();

}

});

DBQuizHelper繼承自SQLiteOpenHelper類。SQLiteOpenHelper是SQLite Database的一個幫助類,用來管理數(shù)據(jù)庫的創(chuàng)建、基本操作和版本更新。它是一個抽象類,需要創(chuàng)建一個自己的幫助類去繼承它。

SQLiteOpenHelper的構(gòu)造函數(shù)有四個參數(shù),第一個參數(shù)是 Context對象,有它才能對數(shù)據(jù)庫進行操作。第二個參數(shù)是數(shù)據(jù)庫名;第三個參數(shù)允許在查詢數(shù)據(jù)的時候返回一個游標(Cursor),一般傳入空 null。第四個參數(shù)表示當前數(shù)據(jù)庫的版本號,用來對數(shù)據(jù)庫進行升級操作。

接下來構(gòu)造DBQuizHelper類。

public class DBQuizHelper extends SQLiteOpenHelper {

// 創(chuàng)建的測試題目表Quiz,字符串形式的sql語句。

public static final String CREATE_QUIZ = “create table Quiz (”

? + "id integer primary key autoincrement, "

? + "statement text, "

? + "type text, "

? + "answer text, "

? + “difficulty integer)”;

private Context context;

public DBQuizHelper(Context context, String name,

? SQLiteDatabase.CursorFactory factory, int version) {

super(context, name, factory, version);

this.context = context;

}

在DBQuizHelper中,重寫SQLiteOpenHelper類的onCreate()函數(shù),調(diào)用execSQL() 函數(shù)來執(zhí)行sql語句。

@Override

public void onCreate(SQLiteDatabase db) {

db.execSQL(CREATE_QUIZ);

db.execSQL(CREATE_COURSE);

Toast.makeText(context, “Create succeeded”, Toast.LENGTH_SHORT).show();

}

在onUpgrade()函數(shù)中可以升級數(shù)據(jù)庫。如果數(shù)據(jù)庫中表的定義發(fā)生了改變,比如在Quiz表中增加了一列“題目所屬章節(jié)”,那么就需要在數(shù)據(jù)庫中重新創(chuàng)建Quiz表。首先刪除原來的Quiz表,然后再調(diào)用onCreate() 函數(shù)重新創(chuàng)建它。

@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

// 刪除原來的表

db.execSQL(“drop table if exists Quiz”);

db.execSQL(“drop table if exists Course”);

// 創(chuàng)建新的表

onCreate(db);

}

下面來實現(xiàn)數(shù)據(jù)庫的增、刪、改、查四大功能。要在數(shù)據(jù)庫中插入一行,首先獲取SQLiteDatabase的對象db,同時創(chuàng)建ContentValues對象values。values中存放要寫入數(shù)據(jù)庫的信息,然后用SQLiteDatabase的insert函數(shù)向表中添加數(shù)據(jù)。

insert函數(shù)的第一個參數(shù)是表名,第二個參數(shù)用來指定插入一條完全為NULL的記錄,一般用不到這個功能,可直接傳入 null。第三個參數(shù)是 ContentValues 對象,一個values對象代表了quiz表中的一行。注意:quiz表中還有id這一列,并沒有給它賦值,因為在前面創(chuàng)建表的時候已經(jīng)將 id 列設(shè)置為自增長,它的值會隨著行的插入自動生成,不需要手動賦值。

SQLiteDatabase db = dbQuizHelper.getWritableDatabase();

ContentValues values = new ContentValues();

// 第一個參數(shù)是表的列名。

values.put(“statement”, “簡述Android中Intent的作用?!?;

values.put(“type”, “簡答題”);

values.put(“answer”, “Intent用于同一應(yīng)用或不同應(yīng)用的組件之間通信?!?;

values.put(“difficulty”, “2”);

db.insert(“Quiz”, null, values);

values.clear();

values.put(“statement”, “Service結(jié)束運行的方法有哪兩種?有何區(qū)別?”);

? … …

db.insert(“Quiz”, null, values);

更新和刪除數(shù)據(jù)都比較簡單?,F(xiàn)在把某一道題目的難度做一下修訂,原來題目的難度是“2”,現(xiàn)在要改為“4”。需要在values對象中重新設(shè)置難度列,然后調(diào)用update函數(shù)進行修改。注意:update函數(shù)的第三個參數(shù)是where條件。第四個參數(shù)是where條件的取值,也就是要修改的題目。

SQLiteDatabase db = dbQuizHelper.getWritableDatabase();

ContentValues values = new ContentValues();

values.put(“difficulty”, “4”);

// where條件:statement=“Service結(jié)束運行的方法有哪兩種?有何區(qū)別?”

// 把這道題的難度從原來的“2”改為“4”

db.update(“Quiz”, values, “statement = ?”,

new String[]{“Service結(jié)束運行的方法有哪兩種?有何區(qū)別?”});

SQLiteDatabase db = dbQuizHelper.getWritableDatabase();

delete函數(shù)接收三個參數(shù),第一個參數(shù)是表名,第二、第三個參數(shù)用來限定要刪除哪些數(shù)據(jù)。下面的代碼刪除所有難度大于4的題目。如果不指定條件,將默認刪除所有的行。

db.delete(“Quiz”, “difficulty > ?”, new String[]{“4”});

查詢語句返回一個游標對象cursor,通過它可以獲取查詢的結(jié)果。

SQLiteDatabase db = dbQuizHelper.getWritableDatabase();

Cursor cursor = db.query(“Quiz”, null, null, null, null, null, null);

查詢獲取了游標對象,通過它能夠查看或者處理查詢結(jié)果集中的數(shù)據(jù)。游標就像一個指針它可以指向結(jié)果集中的任何一行,讓用戶能夠?qū)χ付ǖ男羞M行操作。在一個循環(huán)中,通過游標的 getColumnIndex函數(shù)取得某一列的位置索引,把這個索引傳給游標的getString函數(shù),getString函數(shù)再從結(jié)果集中讀取數(shù)據(jù)。

if (cursor.moveToFirst()) {

do {

? String statement = cursor.getString(cursor. getColumnIndex(“statement”));

? String type = cursor.getString(cursor. getColumnIndex(“type”));

? int difficult = cursor.getInt(cursor.getColumnIndex(“difficulty”));

? Log.d(TAG, "試題: " + statement);

? Log.d(TAG, "題型: " + type);

? Log.d(TAG, “難度:” + difficult);

} while (cursor.moveToNext());

}

cursor.close();

在循環(huán)末尾判斷游標是否已經(jīng)到達結(jié)果集的最后一行。最后,在游標使用完以后要調(diào)用close函數(shù)關(guān)閉它。

4) 內(nèi)容共享組件

內(nèi)容提供器(ContentProvider)是Android應(yīng)用的四大組件之一。ContentProvider 在Android中的作用是對外共享數(shù)據(jù),也就是說可以通過ContentProvider把應(yīng)用中的數(shù)據(jù)共享給其他應(yīng)用訪問。其他應(yīng)用也可以通過ContentProvider 對共享應(yīng)用中的數(shù)據(jù)進行增、刪、改、查,比如答題應(yīng)用就能夠直接訪問聯(lián)系人信息。Android系統(tǒng)內(nèi)置的短信、媒體庫等程序都實現(xiàn)了跨程序數(shù)據(jù)共享功能。

內(nèi)容提供器對底層數(shù)據(jù)存儲方式進行抽象,為存儲和獲取數(shù)據(jù)提供了統(tǒng)一的接口,可以讓數(shù)據(jù)在不同的應(yīng)用程序之間共享。內(nèi)容提供器為數(shù)據(jù)共享提供了一個安全的環(huán)境。它允許把自己的應(yīng)用數(shù)據(jù)根據(jù)需求開放給其他應(yīng)用。其他應(yīng)用也可以增加、刪除、修改和查詢開放的數(shù)據(jù),不用擔(dān)心開放數(shù)據(jù)權(quán)限而帶來的安全問題。Android系統(tǒng)還提供了音頻、視頻、圖片和通訊錄的共享接口,可以通過它們直接訪問這些資源。

一個應(yīng)用程序要使用上述多個共享數(shù)據(jù),如果需要開發(fā)者了解每個內(nèi)容提供器的不同實現(xiàn),就太繁瑣了;所以Android提供了內(nèi)容解析器ContentResolver來統(tǒng)一管理不同內(nèi)容提供器的共享功能。要訪問某一個內(nèi)容提供器,首先獲取內(nèi)容解析器,內(nèi)容解析器提供了對數(shù)據(jù)進行增、刪、改、查的操作函數(shù)。

要訪問數(shù)據(jù)需要用到ContentResolver的查詢函數(shù)。它的查詢方式類似于SQL語句,用uri表示要訪問的數(shù)據(jù)地址。通常數(shù)據(jù)以表的形式呈現(xiàn)。projection是要查詢的列名;selection是約束條件;selectionArgs是條件參數(shù)對應(yīng)的值;sortOrder是排序方式。查詢的返回結(jié)果是游標,通過它可以逐行訪問數(shù)據(jù)。

Cursor cursor = getContentResolver().query(

? uri,

? projection,

? selection,

? selectionArgs,

? sortOrder

);

通過前面的查詢,返回一個游標對象。在循環(huán)中使用游標對象把數(shù)據(jù)提取出來,直到循環(huán)結(jié)束。

if (cursor != null) {

while (cursor.moveToNext()) {

? String statement = cursor.getString(cursor.getColumnIndex(“statement”));

? int difficult = cursor.getInt(cursor.getColumnIndex(“difficult”));

}

cursor.close();

}

下面利用ContentResolver在答題APP應(yīng)用中讀取聯(lián)系人信息。首先構(gòu)造一個ContactsActivity,這個活動要訪問聯(lián)系人APP的內(nèi)容提供器。注意訪問共享數(shù)據(jù)還要聲明使用權(quán)限,如果使用Android 6.0以上的版本,記得要申請動態(tài)使用權(quán)限。

構(gòu)造一個聯(lián)系人類ContactsUtil來讀取所有聯(lián)系人信息。首先獲取內(nèi)容解析器,然后查詢聯(lián)系人。在query函數(shù)中,沒有用Uri.parse() 函數(shù)去解析一個內(nèi)容URI字符串,因為Android中的Phone類(ContactsContract.CommonDataKinds.Phone類)已經(jīng)對共享資源進行了封裝,提供了一個CONTENT_URI常量。接下來,使用游標對象遍歷聯(lián)系人信息,把聯(lián)系人姓名和手機號逐一提取出來。聯(lián)系人姓名對應(yīng)DISPLAY_NAME常量,電話號碼對應(yīng)NUMBER常量,其他參數(shù)可以查閱Andriod的相關(guān)資料。

public class ContactsUtil {

public static void getContactInfos(Context context) {

? ContentResolver resolver = context.getContentResolver();

? Cursor cursor = resolver.query(

? // 聯(lián)系人uri。

? ContactsContract.CommonDataKinds.Phone.CONTENT_URI,

? null, null, null, null);

? while (cursor.moveToNext()) {

? String contactId = cursor.getString(

? cursor.getColumnIndex(ContactsContract.Contacts._ID));

? String name = cursor.getString(cursor.getColumnIndex(

? ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));

String phone = cursor.getString(cursor.getColumnIndex(

? ContactsContract.CommonDataKinds.Phone.NUMBER));

(2) 數(shù)據(jù)的解析方法

現(xiàn)在,對網(wǎng)絡(luò)數(shù)據(jù)的組織有兩大規(guī)范,分別是XML格式和JSON格式。XML是用于標記文件使其具有結(jié)構(gòu)性的標記語言。JSON是一種輕量級的數(shù)據(jù)交換格式。

可擴展標記語言(Extensible Markup Language,XML)主要用來存儲帶有結(jié)構(gòu),帶有格式的數(shù)據(jù)。XML經(jīng)常用于網(wǎng)絡(luò)數(shù)據(jù)傳輸和作為程序配置文件。

常用的XML解析方法有:DOM解析、SAX 解析和PULL解析。SAX(Simple API for XML)是一種基于事件的解析器,它采用事件處理機制,圍繞事件源以及事件處理器來工作。當事件源產(chǎn)生事件后,調(diào)用事件處理器完成相應(yīng)的任務(wù)。調(diào)用事件處理器還要傳遞相應(yīng)事件的狀態(tài)信息,這樣事件處理器才能根據(jù)事件信息來決定自己的行為。

SAX解析包括以下四個步驟:

(1)獲取XML文件對應(yīng)的資源,可以是XML輸入流、文件、URI和字符串;

(2)獲取SAX解析工廠(SAXParserFactory);

(3)由解析工廠生成一個SAX解析器(SAXParser);

(4)將XML輸入流和處理器(Handler)傳給XMLReader,調(diào)用parse函數(shù)進行解析。

public class DataParse {

public static void parseXMLWithSAX(String xmlData) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
ParseHandler handler = new ParseHandler(xmlStr);
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(new StringReader(xmlData)));
} catch (Exception e) {
e.printStackTrace();
}
}

… …

}

SAX在解析XML時,采用逐行掃描的方式來處理數(shù)據(jù)。當掃描到文檔(document)、元素(element)的開始和結(jié)束位置時,通知事件處理函數(shù);隨后把事件發(fā)送給處理器,由處理器完成文檔的解析。解析XML的工作在類ParseHandler中完成,它繼承自DefaultHandler,解析時需要重寫DefaultHandler中的方法。

Android還附帶了一個PULL解析器來解析XML文檔。PULL解析器的工作方式和SAX類似,都是基于事件模式。不同的是在PULL解析過程中返回的事件類型是數(shù)字,并且需要從解析器中獲取事件,然后再做出相應(yīng)的處理。SAX是由處理器觸發(fā)事件,然后執(zhí)行代碼。PULL解析器的解析速度快,簡單易用。Android系統(tǒng)內(nèi)部在解析各種XML時也是用PULL解析器,Android官方也推薦使用PULL解析技術(shù)。

PULL解析器的工作原理:首先獲取一個XmlPullParserFactory對象,通過它來得到XML PULL解析器;然后調(diào)用解析器的setInput 函數(shù)把 XML 數(shù)據(jù)放入解析器,開始執(zhí)行解析。解析器用getEventType 函數(shù)得到當前的解析事件類型。

public static void parseXMLWithPull(String xmlData) {

  try {

?    Log.d(TAG, "xml 數(shù)據(jù):" + xmlData);

?    // 設(shè)置解析器

?    XmlPullParserFactory factory = XmlPullParserFactory.newInstance();

?    XmlPullParser xmlPullParser = factory.newPullParser();

?    xmlPullParser.setInput(new StringReader(xmlData));

 

?    int eventType = xmlPullParser.getEventType();

?    String id = "";

?    String statement = "";

?    String type = "";

?      // 執(zhí)行XML文檔解析

while (eventType != XmlPullParser.END_DOCUMENT) {
       String nodeName = xmlPullParser.getName();
       switch (eventType) {
         // 根據(jù)事件類型提取XML中各個標簽的信息
         case XmlPullParser.START_TAG: {
           if ("id".equals(nodeName)) {
             id = xmlPullParser.nextText();
           } else if ("statement".equals(nodeName)) {
             statement = xmlPullParser.nextText();
           } else if ("type".equals(nodeName)) {
             type = xmlPullParser.nextText();
           }
           break;
         }
 
         case XmlPullParser.END_TAG: {
           if ("quiz".equals(nodeName)) {
             Log.d(TAG, "題號: " + id);
             Log.d(TAG, "題目:" + statement);
             Log.d(TAG, "題型:" + type);
           }
           break;
         }
         default:
           break;
       }
       eventType = xmlPullParser.next();
     }
   } catch (Exception e) {
     e.printStackTrace();
   }
 }

PULL解析器能夠分辨文檔標簽的開始元素和結(jié)束元素。當某個元素開始時,調(diào)用解析器的nextText函數(shù)從XML文檔中提取所有字符數(shù)據(jù)。當解釋到一個文檔結(jié)束時,自動生成EndDocument事件。在循環(huán)中,通過 getName() 函數(shù)得到當前節(jié)點的名稱,如果發(fā)現(xiàn)節(jié)點名是id、statement 或 type,就調(diào)用 nextText 函數(shù)來取得節(jié)點內(nèi)信息,當解析完一個節(jié)點就將節(jié)點的內(nèi)容打印出來。

JSON(JavaScript Object Notation)是一種輕量級的數(shù)據(jù)交換格式,具有良好的可讀性,并且便于快速編寫。它采用完全獨立于編程語言的文本格式來存儲和表示數(shù)據(jù)。數(shù)據(jù)格式采用鍵值對的方式,可以用來表示對象、數(shù)字,還可以設(shè)置對象的屬性和值。

(1) {} 花括號用來保存對象;

(2) [] 方括號用來保存數(shù)組;

(3) “” 雙引號內(nèi)是屬性或值;

(4) : 冒號表示后者是前者的值。

相對于XML,簡潔和清晰的層次結(jié)構(gòu)使得 JSON 成為理想的數(shù)據(jù)交換語言;而且JSON格式易于人們閱讀和編寫,同時也易于機器解析和生成,能有效地提升網(wǎng)絡(luò)傳輸效率。

采用JsonArray的方式可以把JSON數(shù)據(jù)轉(zhuǎn)換成JSON數(shù)組的形式;然后再通過數(shù)組獲取一個個的JSON對象。對于每個JSON對象,利用JSONObject解析出JSON格式中的每項數(shù)據(jù)。下面把id、題干和題目類型等信息通過JSONObject提取出來。

public static void parseJSONWithJSONObject(String jsonData) {
     try {
       Log.d(TAG, "JSON 數(shù)據(jù):" + jsonData);
 
       JSONArray jsonArray = new JSONArray(jsonData);
       for (int i = 0; i < jsonArray.length(); i++) {
         JSONObject jsonObject = jsonArray.getJSONObject(i);
         String id = jsonObject.getString("id");
         String statement = jsonObject.getString("statement");
         String type = jsonObject.getString("type");
         … …
         Log.d(TAG, "題號:" + id);
         Log.d(TAG, "題目:" + statement);
         Log.d(TAG, "題型:" + type);

… …
       }
     } catch (Exception e) {
       e.printStackTrace();
     }
   }

GSON是解析JSON的一個開源框架,它用于轉(zhuǎn)換Java對象和JSON對象。在使用GSON API之前,需要在build.gradle文件中添加對gson的依賴關(guān)系。

dependencies {

… …

compile 'com.google.code.gson:gson:2.7’

… …

testCompile ‘junit:junit:4.12’

}

Gson提供了fromJson() 和toJson() 兩個直接用于解析和生成的函數(shù)。fromJson函數(shù)實現(xiàn)反序列化,toJson函數(shù)實現(xiàn)了序列化。下面用fromJson函數(shù) 把JSON數(shù)據(jù)中的測試題集合轉(zhuǎn)換為測試題列表集合;然后,把集合中的題目提取出來轉(zhuǎn)換為quiz對象。

Gson gson = new Gson();

List qList = gson.fromJson(data.toString(),

? new TypeToken<List>(){}.getType());

Quiz quiz = gson.fromJson(data, Quiz.class);

GSON作為Java對象和JSON數(shù)據(jù)之間進行映射的Java類庫,可以將一個JSON字符串轉(zhuǎn)換成一個Java對象,或者將一個Java對象轉(zhuǎn)換成JSON字符串。GSON解析的特點是:快速、高效、代碼量少、簡潔、數(shù)據(jù)傳遞和解析方便。

難點

(1) 數(shù)據(jù)共享的基本原理

開發(fā)者自己編寫的移動應(yīng)用程序也可以把數(shù)據(jù)共享出來讓其他應(yīng)用程序使用。應(yīng)用程序要共享數(shù)據(jù)需要提供公開的URI,這樣其他應(yīng)用程序才能夠訪問到共享的數(shù)據(jù)。每個ContentProvider都擁有一個公共的URI,它用于表示ContentProvider所提供的數(shù)據(jù)。

答題APP把測試題目共享給其他應(yīng)用程序。首先,創(chuàng)建一個Quiz內(nèi)容提供器,它從ContentProvider繼承;然后,通過UriMatcher 類來匹配Uri。數(shù)據(jù)是來自SQLite數(shù)據(jù)庫的quiz表。

// 自定義 ContentProvider

public class QuizProvider extends ContentProvider {

private static final int QUEYSUCESS = 0;

private static final int INSERTSUCESS = 1;

private static final int UPDATESUCESS = 2;

private static final int DELSUCESS = 3;

private static final String AUTHORITY = “pers.cnzdy.tutorial.quiz.provider”;

static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

private DBQuizHelper helper;

private String table = “quiz”;

uri用來指定數(shù)據(jù)源。當一個數(shù)據(jù)源含有多個內(nèi)容,比如包含多個表,就需要用不同的uri進行區(qū)分。在QuizProvider中,利用UriMatcher來匹配執(zhí)行不同的操作。

static{

/**

* authority 主機名 通過主機名來訪問共享的數(shù)據(jù)

* path pers.cnzdy.tutorial.quiz.provider/query

* code 匹配碼

* 添加查詢、插入、更新、刪除匹配規(guī)則。

*/

matcher.addURI(AUTHORITY, “query”, QUEYSUCESS);

matcher.addURI(AUTHORITY, “insert”, INSERTSUCESS);

matcher.addURI(AUTHORITY, “update”, UPDATESUCESS);

matcher.addURI(AUTHORITY, “delete”, DELSUCESS);

}

UriMatcher的addURI() 函數(shù)有三個參數(shù),分別是權(quán)限、路徑和自定義編碼。通過自定義編碼,通過UriMatcher的過濾,來確定要訪問哪張表,或者執(zhí)行哪個動作。

在QuizProvider的onCreate函數(shù)中,創(chuàng)建DBQuizHelper對象。

@Override

public boolean onCreate() {

helper = new DBQuizHelper(getContext(), “Exam.db”, null, 2);

return true;

}

QuizProvider的getType函數(shù)根據(jù)傳入的 URI 返回對應(yīng)的 MIME 類型。

@Override

public String getType(Uri uri) {

return null;

}

查詢函數(shù)query讓其他應(yīng)用可以從內(nèi)容提供器中查詢數(shù)據(jù)。query函數(shù)有五個參數(shù):uri, projection、selection、selectionArgs、sortOrder。uri 參數(shù)用來確定查詢哪張表,其他參數(shù)與內(nèi)容解析器的query函數(shù)的參數(shù)一樣。

在query函數(shù)中,首先匹配要執(zhí)行的動作,如果是查詢,就調(diào)用數(shù)據(jù)庫的查詢語句,獲取數(shù)據(jù),返回游標。如果匹配不成功,則產(chǎn)生異常。

@Override

public Cursor query(Uri uri, String[] projection,

String selection, String[] selectionArgs, String sortOrder) {

int match = matcher.match(uri);

if (match == QUEYSUCESS ) {

? SQLiteDatabase db = helper.getReadableDatabase();

? Cursor cursor = db.query(table, projection, selection,

? selectionArgs, null, null, sortOrder);

? return cursor;

}else{

? throw new IllegalArgumentException(“路徑匹配失敗”);

}

}

QuizProvider的插入函數(shù)insert向數(shù)據(jù)庫中添加一條數(shù)據(jù),將待添加的數(shù)據(jù)保存在 values 參數(shù)中。添加完成后,返回一個用來表示這條新記錄的 URI。如果QuizProvider的訪問者需要知道內(nèi)容提供器中的數(shù)據(jù)是否發(fā)生了變化,就調(diào)用內(nèi)容解析器的notifyChange() 函數(shù)來通知注冊在這個URI上的訪問者。

@Override

public Uri insert(Uri uri, ContentValues values) {

int match = matcher.match(uri);

if (match == INSERTSUCESS) {

? SQLiteDatabase db = helper.getReadableDatabase();

? long insert = db.insert(table, null, values);

? if (insert > 0) {

? // 通知注冊在這個uri上的訪問者

? getContext().getContentResolver().notifyChange(uri, null);

}

? Uri uriResult= Uri.parse(“content://”+ AUTHORITY + “/” + insert);

? return uriResult;

}else{ … … }

}

QuizProvider的刪除函數(shù)delete刪除數(shù)據(jù)庫中的數(shù)據(jù)。selection和selectionArgs 參數(shù)用于約束刪除哪些行,被刪除的行數(shù)將作為返回值返回。

@Override

public int delete(Uri uri, String selection, String[] selectionArgs) {

int match = matcher.match(uri);

if (match == DELSUCESS) {

? SQLiteDatabase db = helper.getReadableDatabase();

? int delete = db.delete(table, selection, selectionArgs);

? if (delete > 0) {

? getContext().getContentResolver().notifyChange(uri, null);

}

? return delete;

}else { … … }

}

更新函數(shù)更新內(nèi)容提供器中已有的數(shù)據(jù)。新數(shù)據(jù)保存在 values 參數(shù)中,同樣selection 和 selectionArgs 參數(shù)用于約束更新哪些行,受影響的行數(shù)將作為返回值返回。

@Override

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {

int match = matcher.match(uri);

if (match == UPDATESUCESS) {

? SQLiteDatabase db = helper.getReadableDatabase();

? int update = db.update(table, values, selection, selectionArgs);

? if (update > 0) {

? getContext().getContentResolver().notifyChange(uri, null);

}

? return update;

}else { … … }

}

內(nèi)容提供器還需要在全局配置文件中進行定義。android:name對應(yīng)自定義的QuizProvider類。android:authorities為QuizProvider類中定義的靜態(tài)字符串:“pers.cnzdy.tutorial.quiz.provider”。

<application

? … …

<provider

? android:name=".Data.QuizProvider"

? android:authorities=“pers.cnzdy.tutorial.quiz.provider”

? android:enabled=“true”

? android:exported=“true”>

… …

完成以上代碼后,其他的應(yīng)用程序就能夠訪問應(yīng)用共享的數(shù)據(jù)了。

(2) Android****的文件存儲方式

在邏輯上,Android系統(tǒng)把整個存儲空間劃分為內(nèi)部存儲(Internal storage)和外部存儲(External storage)。內(nèi)部存儲用于存放系統(tǒng)本身和應(yīng)用程序的數(shù)據(jù),空間有限。內(nèi)部存儲有嚴格的權(quán)限管理,用戶不能隨意訪問。如果要訪問,需要root權(quán)限。在DDMS中,用File Explorer查看Android系統(tǒng)的存儲空間,可以看到第一級的data文件夾,它就是內(nèi)部存儲,

打開data文件夾之后(需要root權(quán)限),有一個app文件夾,它存放著所有app的安裝的文件(調(diào)試app時,會上傳apk到該文件夾)。另外,還有第二級data文件夾,在這個文件夾下面都是一些包名,打開包名之后列出以下一些文件:

data/data/包名/shared_prefs

data/data/包名/databases

data/data/包名/files

data/data/包名/cache

使用sharedPreferenced存取數(shù)據(jù)時,將數(shù)據(jù)保存到該文件夾的xml文件中。如果使用數(shù)據(jù)庫,數(shù)據(jù)庫文件將存儲在databases文件夾中,一般的數(shù)據(jù)則存儲在files文件夾中,緩存文件存儲在cache文件夾中。

應(yīng)用程序?qū)⑽募4嬖趦?nèi)部存儲中。系統(tǒng)默認只有自己的應(yīng)用能訪問這些文件;并且一個應(yīng)用所創(chuàng)建的所有文件都放在一個文件夾下面,這個文件夾的名稱與應(yīng)用包名相同,即應(yīng)用創(chuàng)建的內(nèi)部存儲文件與應(yīng)用相關(guān)聯(lián)。當應(yīng)用卸載之后,內(nèi)部存儲中的這些文件也會被刪除。在默認情況下,應(yīng)用程序安裝到內(nèi)部存儲。另外,通過在AndroidManifest.xml文件中指定android:installLocation屬性,應(yīng)用程序也可以安裝在外部存儲器中。

內(nèi)部存儲通常使用Context來操作,下面是訪問內(nèi)部存儲的常用函數(shù):

Environment.getDataDirectory() // 獲取路徑: /data

context.getFilesDir() // 獲取路徑: /data/data/< package name >/files/…

context.getCacheDir() // 獲取路徑: /data/data/< package name >/cach/…

context.getDir(String name, String mode) 返回/data/data//目錄下指定名稱文件夾的File對象,如果該文件夾不存在則用指定名稱創(chuàng)建一個新的文件夾。mode用于指示文件的創(chuàng)建模式,指定MODE_PRIVATE將把文件設(shè)為應(yīng)用的私有文件。

對于外部存儲中的數(shù)據(jù),應(yīng)用程序可以自由訪問,不需要嚴格的訪問權(quán)限,比如可以在電腦上直接查看這些文件。外部存儲中的文件能夠被其他App訪問或者通過電腦進行訪問。外部存儲又分為SD卡和擴展卡兩種存儲方式。

storage的子文件夾又分為兩類,分別是公有目錄和私有目錄。公有目錄是系統(tǒng)創(chuàng)建的文件夾,比如:DCIM、DOWNLOAD等;私有目錄是“/Android”文件夾。私有目錄屬于應(yīng)用私有,當用戶卸載應(yīng)用時,該目錄及其內(nèi)容將被刪除。

通常建議應(yīng)用程序的數(shù)據(jù)(不適合其他應(yīng)用使用的文件,比如:圖像、紋理、音效等等)存放在外部存儲的私有目錄中(即該App的包名下面)。這樣當用戶卸載應(yīng)用之后,相關(guān)的數(shù)據(jù)會一起刪除;如果直接在/storage/文件夾下面創(chuàng)建應(yīng)用子文件夾,那么當應(yīng)用被刪除的時候,這個子文件夾就不會被刪除。

a) 獲取外部存儲路徑:/storage/sdcard0

Environment.getExternalStorageDirectory()

b) 通過以下函數(shù)可以獲取私有目錄:

// 獲取路徑: /storage/emulated/0/Android/data/< package name >/files/…

context.getExternalFilesDir()

// 獲取路徑: /storage/emulated/0/Android/data/< package name >/cach/…

context.getExternalCacheDir()

九個公有目錄可以通過以下函數(shù)獲?。?/p>

// 獲取路徑: /storage/sdcard0/Alarms

Environment.getExternalStoragePublicDirectory(DIRECTORY_ALARMS)

// 獲取路徑: /storage/sdcard0/DCIM

Environment.getExternalStoragePublicDirectory(DIRECTORY_DCIM)

// 獲取路徑: /storage/sdcard0/Download

Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS)

… …

在使用外部存儲執(zhí)行任何操作之前,要調(diào)用 getExternalStorageState函數(shù)檢查存儲介質(zhì)是否可用。此外,如果是Android 4.4以前版本,讀取或?qū)懭胪獠看鎯Γòü材夸浐退接心夸洠┑奈募仨毇@取 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 系統(tǒng)權(quán)限:

本章習(xí)題:

1、本單元考核點
File、SharePreferences、SQLite和ContentProvider的操作方法。
ContentProvider的基本原理。
XML格式和JSON格式數(shù)據(jù)的解析方法。
2、本單元課后習(xí)題
1、為了保存永久性的應(yīng)用數(shù)據(jù),Android 主要提供了哪幾種數(shù)據(jù)存儲方式?
答案:
(1)Shared Preferences以 key-value 對方式保存私有的基本類型數(shù)據(jù)。
(2)File Storage在設(shè)備存儲空間中保存私有數(shù)據(jù)。
(3)SQLite Databases在私有的數(shù)據(jù)庫中存儲結(jié)構(gòu)化數(shù)據(jù)。
(4)Network Connection將數(shù)據(jù)保存在網(wǎng)絡(luò)服務(wù)器上。
(5)Android 還提供將私有數(shù)據(jù)開放給其他應(yīng)用的途徑:Content Provider。
2、簡述SAX解析方式和特點。
答案:它逐行掃描文檔,一邊掃描一邊解析,在讀取文檔時激活一系列事件,這些事件被推給事件處理器,由事件處理器提供對文檔內(nèi)容的訪問;特點:不需要將數(shù)據(jù)存儲在內(nèi)存中,對于大型文檔的解析有較大優(yōu)勢。

參考資源:

1、 DebDiv移動開發(fā)社區(qū)著.移動開發(fā)平臺解決方案.北京:海洋出版社,2011.

2、 泡在網(wǎng)上的日子 - 做最好的移動開發(fā)社區(qū):www.jcodecraeer.com

原創(chuàng)聲明

=======

作者: [ libin9iOak ]


本文為原創(chuàng)文章,版權(quán)歸作者所有。未經(jīng)許可,禁止轉(zhuǎn)載、復(fù)制或引用。

作者保證信息真實可靠,但不對準確性和完整性承擔(dān)責(zé)任。

未經(jīng)許可,禁止商業(yè)用途。

如有疑問或建議,請聯(lián)系作者。

感謝您的支持與尊重。

點擊下方名片,加入IT技術(shù)核心學(xué)習(xí)團隊。一起探索科技的未來,共同成長。文章來源地址http://www.zghlxwxcb.cn/news/detail-520173.html

到了這里,關(guān)于《移動互聯(lián)網(wǎng)技術(shù)》 第七章 數(shù)據(jù)存取: 掌握File、SharePreferences、SQLite和ContentProvider四種數(shù)據(jù)存取方式的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包