版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
Andriod應(yīng)用開發(fā)基礎(chǔ)教程第5章數(shù)據(jù)存儲(chǔ)應(yīng)用程序在Android系統(tǒng)運(yùn)行過程中產(chǎn)生的用戶數(shù)據(jù)、日志、下載的圖片、文件等都需要存儲(chǔ)在Android系統(tǒng)提供的存儲(chǔ)空間里,系統(tǒng)提供了SharePreferences、應(yīng)用專屬存儲(chǔ)、數(shù)據(jù)庫、共享存儲(chǔ)等方式來存儲(chǔ)這些產(chǎn)生的數(shù)據(jù)。(1)SharePreferences:將數(shù)據(jù)以鍵值對(duì)形式存儲(chǔ)在XML文件中;(2)應(yīng)用專屬存儲(chǔ):此種存儲(chǔ)方式僅供應(yīng)用使用,可以將應(yīng)用數(shù)據(jù)存儲(chǔ)到內(nèi)部存儲(chǔ)空間的專屬目錄或外部存儲(chǔ)空間中的私有目錄。(3)共享存儲(chǔ):存儲(chǔ)應(yīng)用間能共享的文件,包括媒體、文檔和其他文件;(4)數(shù)據(jù)庫:將結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)在專用數(shù)據(jù)庫中。表5-1數(shù)據(jù)存儲(chǔ)方式特點(diǎn)存儲(chǔ)方式存儲(chǔ)內(nèi)容類型其它應(yīng)用可否訪問卸載應(yīng)用是否移除SharePreferences鍵值對(duì)否是應(yīng)用專屬存儲(chǔ)僅供應(yīng)用使用文件否是數(shù)據(jù)庫結(jié)構(gòu)化數(shù)據(jù)否是媒體可共享的媒體文件(圖片、音頻文件、視頻)是,但其他應(yīng)用需要READ_EXTERNAL_STORAGE權(quán)限否文檔和其他文件其他類型的可共享內(nèi)容,包括已下載的文件是,可以通過系統(tǒng)文件選擇器訪問否5.1SharePreferencesSharePreferences是一種輕量級(jí)的存儲(chǔ)方式,適合單進(jìn)程、小批量的數(shù)據(jù)存儲(chǔ)與訪問,可用于保存應(yīng)用的配置信息,大數(shù)據(jù)不合適采用SharedPreferences存放,SharedPreferences用xml文件存放數(shù)據(jù),文件目錄位于/data/data/包名/shared_prefs下。SharedPreferences本身是一個(gè)接口,程序無法直接創(chuàng)建SharedPreferences的實(shí)例,只能通過Activity的getSharedPreferences()方法、Context的getPreferences()方法和PreferenceManager的getDefaultSharedPreferences()方法來獲取SharedPreferences的實(shí)例,這些方法中第一個(gè)參數(shù)用于指定SharedPreferences文件的名稱(格式為xml文件),如果指定的文件不存在則會(huì)創(chuàng)建一個(gè),第二個(gè)參數(shù)用于指定存儲(chǔ)模式。有下面的一些模式:1、MODE_PRIVATE:默認(rèn)模式,創(chuàng)建的文件只能由調(diào)用應(yīng)用程序(或共享相同用戶ID的所有應(yīng)用程序)訪問;2、MODE_WORLD_READABLE:允許所有其它應(yīng)用程序?qū)?chuàng)建的文件具有讀取權(quán)限,AndroidAPI17之后不再贊成此模式。創(chuàng)建所有應(yīng)用程序可讀的文件非常危險(xiǎn),并可能在應(yīng)用程序中造成安全漏洞,應(yīng)用程序應(yīng)該使用更正式的交互機(jī)制,如ContentProvider、BroadcastReceiver和Service。3、MODE_WORLD_WRITEABLE:允許其它所有應(yīng)用程序?qū)?chuàng)建的文件進(jìn)行讀寫,AndroidAPI17之后不再贊成此模式。同MODE_WORLD_READABLE一樣,創(chuàng)建所有應(yīng)用程序可讀寫的文件非常危險(xiǎn),并可能在應(yīng)用程序中造成安全漏洞。4、MODE_MULTI_PROCESS:允許在應(yīng)用程序有多個(gè)進(jìn)程的情況下,所有進(jìn)程都寫入同一SharedPreferences文件,AndroidAPI23之后不再贊成此模式。在一些版本的Android中工作不可靠,而且沒有提供任何機(jī)制來協(xié)調(diào)進(jìn)程之間的并發(fā)修改,應(yīng)盡量避免使用。5、MODE_APPEND:如果文件已存在,則將數(shù)據(jù)寫入現(xiàn)有文件的末尾,而不是擦除它。得到SharedPreferences實(shí)例后,需要調(diào)用SharedPreferences實(shí)例的edit()方法來獲取一個(gè)SharedPreferences.Editor實(shí)例來完成數(shù)據(jù)操作。表5-2SharedPreferences.Editor實(shí)例常用方法方法作用clear()從SharedPreferences文件中移除保存的所有值putBoolean(String
key,
booleanvalue)putFloat(String
key,
floatvalue)putInt(String
key,
intvalue)putLong(String
key,
longvalue)putString(String
key,
String
value)putStringSet(String
key,
Set<String>values)向SharedPreferences文件中添加不同類型數(shù)據(jù):布爾型、浮點(diǎn)型、整型、長整型、字符串、字符串列表remove(String
key)從SharedPreferences文件中移除鍵值相關(guān)聯(lián)數(shù)據(jù)commit()apply()向SharedPreferences文件提交修改。commit()方法是同步的,會(huì)返回一個(gè)布爾值表示是否成功提交;apply()方法是異步的,沒有返回值,適用于對(duì)性能有較高要求的情況。保存在SharedPreferences文件中的數(shù)據(jù)可直接通過SharedPreferences實(shí)例來獲取。表5-3SharedPreferences實(shí)例獲取存儲(chǔ)數(shù)據(jù)常用方法方法作用abstractboolean
contains(String
key)從SharedPreferences文件查找是否包含鍵值關(guān)聯(lián)的數(shù)據(jù)abstractMap<String,?>getAll()從SharedPreferences文件中遍歷所有數(shù)據(jù)abstractboolean
getBoolean(String
key,
booleandefValue)abstractfloat
getFloat(String
key,
floatdefValue)abstractint
getInt(String
key,
intdefValue)abstractlonggetLong(String
key,
longdefValue)abstract
String
getString(String
key,
String
defValue)abstract
Set<String>getStringSet(String
key,
Set<String>defValues)返回SharedPreferences文件中各種類型數(shù)據(jù):布爾型、浮點(diǎn)型、整型、長整型、字符串、字符串列表。"key"是存儲(chǔ)數(shù)據(jù)時(shí)使用的鍵,"defValue"是一個(gè)默認(rèn)值,如果該鍵不存在,則會(huì)返回這個(gè)默認(rèn)值。案例5.1
使用SharedPreferences存取數(shù)據(jù)(參考代碼見:SharedPreferencesActivity)運(yùn)行SharedPreferencesActivity在模擬器生成應(yīng)用,在應(yīng)用界面中文本輸入框輸入姓名和年齡信息,點(diǎn)擊“向SharedPreferences存數(shù)據(jù)”按鈕,將姓名和年齡信息保存到SharedPreferences,返回AndroidStudio在菜單欄中依次點(diǎn)開視圖>工具窗口>DeviceExplore,在打開的DeviceExplore窗口中,依次點(diǎn)開data>data
>com.example.datastorage(本項(xiàng)目的包名)>shared_prefs,在shared_prefs目錄下則可看見新生成的SharedPreferences文件MySharedPreferencesFile.xml,該文件內(nèi)容如下:1<?xmlversion='1.0'encoding='utf-8'standalone='yes'?>2<map>3<stringname="name">啄木鳥</string>4<intname="age"value="18"/>5</map>返回模擬器應(yīng)用,點(diǎn)擊應(yīng)用界面“從SharedPreferences取數(shù)據(jù)”按鈕,從SharedPreferences獲取到的姓名年齡信息5.2內(nèi)部和外部存儲(chǔ)在Android開發(fā)常涉及到以下三個(gè)幾個(gè)概念:內(nèi)存,內(nèi)部存儲(chǔ),外部存儲(chǔ)。英文中內(nèi)存為Memory,內(nèi)部存儲(chǔ)為InternalStorage,外部存儲(chǔ)為ExternalStorage,當(dāng)我們翻譯為中文之后,前兩個(gè)都簡稱為內(nèi)存,容易產(chǎn)生混淆,這三者到底有什么區(qū)別呢?1、內(nèi)存:手機(jī)內(nèi)存用于暫時(shí)存放CPU中的運(yùn)算數(shù)據(jù)以及與硬盤等外部存儲(chǔ)器交換的數(shù)據(jù)。手機(jī)內(nèi)存越大越能流暢地運(yùn)行多個(gè)應(yīng)用。2、內(nèi)部存儲(chǔ):內(nèi)部存儲(chǔ)用目錄來區(qū)分的話就是/data目錄下的data文件夾:/data/data,普通用戶是無權(quán)訪問的,用戶需要手機(jī)ROOT權(quán)限才可以查看。開發(fā)者可以通過AndroidStudio在菜單欄中依次點(diǎn)開視圖>工具窗口>DeviceExplore,在打開的DeviceExplore窗口中,依次點(diǎn)開data>data來查看該目錄。每個(gè)應(yīng)用在安裝成功后,會(huì)在/data/data目錄下面創(chuàng)建以該應(yīng)用的包名為名稱的目錄,這個(gè)目錄即為應(yīng)用專屬的內(nèi)部存儲(chǔ)目錄,當(dāng)應(yīng)用被卸載后,該目錄會(huì)被系統(tǒng)自動(dòng)刪除。將數(shù)據(jù)存儲(chǔ)于內(nèi)部存儲(chǔ)中,其實(shí)就是把數(shù)據(jù)存儲(chǔ)到自己應(yīng)用包名對(duì)應(yīng)的內(nèi)部存儲(chǔ)目錄中。每個(gè)應(yīng)用的內(nèi)部存儲(chǔ)目錄都是私有的,也就是說內(nèi)部存儲(chǔ)目錄下的文件只能被該應(yīng)用訪問到,其他應(yīng)用是沒有權(quán)限訪問的。應(yīng)用訪問自己的內(nèi)部存儲(chǔ)目錄時(shí)不需要申請(qǐng)任何權(quán)限。3、外部存儲(chǔ):外部存儲(chǔ)是我們平時(shí)操作最多的,外部存儲(chǔ)目錄就是DeviceExplore窗口中看見的/storage文件夾,也有可能是/mnt文件夾,不同廠家有可能不一樣。外部存儲(chǔ)根據(jù)存儲(chǔ)特點(diǎn)不同分為兩種類型,外部私有存儲(chǔ)和外部共有存儲(chǔ)。私有目錄屬于外部存儲(chǔ)的私有存儲(chǔ)空間,該目錄為/storage/emulated/0/Android/data下的以包名為名稱的文件夾,這些文件夾是應(yīng)用的外部存儲(chǔ)私有目錄。公共目錄屬于外部存儲(chǔ)的共享空間,在/storage/emulated/0目錄下看見的DCIM、Download、Music、Movies、Pictures等系統(tǒng)為我們創(chuàng)建的目錄里面的文件所有應(yīng)用可以共享,這些目錄為外部存儲(chǔ)公共目錄。應(yīng)用的配置信息、數(shù)據(jù)庫信息、緩存文件等應(yīng)用相關(guān)數(shù)據(jù)在應(yīng)用被卸載后,這些信息也應(yīng)該被隨之刪除,避免占用存儲(chǔ)空間產(chǎn)生不必要的浪費(fèi),這些應(yīng)用相關(guān)數(shù)據(jù)應(yīng)當(dāng)放到外部私有存儲(chǔ)目錄下。用戶在使用應(yīng)用的過程中產(chǎn)生的圖片、文件等其它應(yīng)用無關(guān)數(shù)據(jù)一般用戶希望與其他應(yīng)用共享,在應(yīng)用被刪除后仍然保存在設(shè)備中,適合存放在外部存儲(chǔ)空間的公共目錄。應(yīng)用可以無需權(quán)限訪問自己的外部存儲(chǔ)私有目錄,應(yīng)用訪問其它應(yīng)用的外部存儲(chǔ)私有目錄在Android7.0前可通過file://這種形式的Uri直接讀寫該目錄下的文件內(nèi)容,Android7.0開始需要通過FileProvider訪問。應(yīng)用在訪問外部公有目錄之前需要申請(qǐng)外部存儲(chǔ)權(quán)限,在Android6.0以后,外部存儲(chǔ)權(quán)限還要?jiǎng)討B(tài)申請(qǐng)。表5-4外部存儲(chǔ)和內(nèi)部存儲(chǔ)比較表5-5訪問內(nèi)部和外部存儲(chǔ)目錄的方法方法訪問路徑內(nèi)部存儲(chǔ)目錄context.getDataDir()/data/data/包名,應(yīng)用內(nèi)部存儲(chǔ)的根目錄context.getFilesDir()/data/data/包名/files,應(yīng)用內(nèi)部存儲(chǔ)的files目錄context.getCacheDir()/data/data/包名/cache,應(yīng)用內(nèi)部存儲(chǔ)的cache目錄context.getDir(Stringname,intmode)/data/data/包名/name,如果該目錄不存在,系統(tǒng)會(huì)自動(dòng)創(chuàng)建該目錄。mode取值可為MODE_APPEND、MODE_PRIVATE、MODE_WORLD_READABLE、MODE_WORLD_WRITABLE。外部私有存儲(chǔ)目錄context.getExternalCacheDir()/storage/emulated/0/Android/data/包名/cacheContext.getExternalFilesDir(Stringtype)1、type=””:/storage/emulated/0/Android/data/package_name/files2、type=”xxx”:(xxx表示目錄名)/storage/emulated/0/Android/data/package_name/files/xxx外部共有存儲(chǔ)目錄Environment.getExternalStorageDirectory()/storage/emulated/0,外部共有存儲(chǔ)目錄的根目錄Environment.getExternalStoragePublicDirectory(Stringtype)1、type=””:/storage/emulated/02、type=”xxx”:(xxx表示目錄名)/storage/emulated/0/xxx3、type=”Environment.DIRECTORY_PICTURES”|”Environment.DIRECTORY_MUSIC”|”Environment.DIRECTORY_DICM”|”Environment.DIRECTORY_ALARM”等:/storage/emulated/0/(系統(tǒng)自己創(chuàng)建的Pictures、Music等目錄)無論內(nèi)部還是外部存儲(chǔ)空間,只要路徑中有包名,那么就是應(yīng)用私有的存儲(chǔ)路徑,存放應(yīng)用相關(guān)數(shù)據(jù),隨著應(yīng)用的的卸載而被刪除,可通過調(diào)用Context里面的方法獲取路徑位置,而路徑中不含包名的路徑為應(yīng)用公共存儲(chǔ)空間,存放應(yīng)用無關(guān)數(shù)據(jù),可通過Environment里面的方法來獲取路徑位置。案例5.2
內(nèi)部存儲(chǔ)目錄寫入讀取數(shù)據(jù)(參考代碼見:InternalStorageActivity)運(yùn)行InternalStorageActivity在模擬器生成應(yīng)用,在應(yīng)用界面編輯框輸入數(shù)據(jù),點(diǎn)擊“保存”按鈕,在DeviceExplore窗口中,依次點(diǎn)開data>data>com.example.datastorage>files,發(fā)現(xiàn)內(nèi)部存儲(chǔ)files目錄下確實(shí)生成一個(gè)名為InternalStorageFile.text的文本文件,打開該文本文件,文本文件保存數(shù)據(jù)正為文本框中輸入內(nèi)容。返回應(yīng)用,點(diǎn)擊應(yīng)用界面“讀取”按鈕,讀取到了應(yīng)用內(nèi)部存儲(chǔ)目錄files下的InternalStorageFile.text文件內(nèi)容向外部存儲(chǔ)私有目錄讀寫數(shù)據(jù)時(shí),只需要在案例5-2基礎(chǔ)上在創(chuàng)建File實(shí)例時(shí)將getApplicationContext().getFilesDir()改為getApplicationContext().getExternalFilesDir(“”),文件存儲(chǔ)位置變?yōu)?storage/emulated/0/Android/data/com.example.datastorage/files,過程和結(jié)果與案例5-2類似案例5.3
向外部存儲(chǔ)公有目錄讀寫數(shù)據(jù)(參考代碼見:ExternalStorageActivity)運(yùn)行ExternalStorageActivity在模擬器生成應(yīng)用,應(yīng)用界面彈出動(dòng)態(tài)申請(qǐng)外部存儲(chǔ)公共目錄訪問權(quán)限的對(duì)話框,對(duì)話框點(diǎn)擊“允許”進(jìn)入應(yīng)用界面,在輯框輸入數(shù)據(jù),點(diǎn)擊“保存”按鈕,在DeviceExplore窗口中,依次點(diǎn)開/storage/emulated/0,發(fā)現(xiàn)該外部存儲(chǔ)公有目錄下確實(shí)生成一個(gè)名為ExternalStorageFile.text的文本文件,打開該文本文件,文本文件保存數(shù)據(jù)正為文本框中輸入內(nèi)容。返回應(yīng)用,點(diǎn)擊應(yīng)用界面“讀取”按鈕,讀取外部存儲(chǔ)公有目錄下ExternalStorageFile.text文件內(nèi)容5.3數(shù)據(jù)庫存儲(chǔ)SQLite是Android內(nèi)置的一個(gè)小型、關(guān)系型、屬于文本型的數(shù)據(jù)庫,是一種輕量級(jí)的嵌入式關(guān)系型數(shù)據(jù)庫管理系統(tǒng),廣泛應(yīng)用于Android應(yīng)用開發(fā)中。Android提供了SQLiteOpenHelper類和SQLiteDatabase類來管理SQLite數(shù)據(jù)庫。SQLiteOpenHelper類用于創(chuàng)建和管理數(shù)據(jù)庫以及版本控制,SQLiteDatabase類用于執(zhí)行創(chuàng)建表、數(shù)據(jù)增刪改查更新等操作。表5-6SQLiteOpenHelper類操作數(shù)據(jù)庫方法方法作用abstractvoidonCreate(SQLiteDatabasedb)數(shù)據(jù)庫第一次被創(chuàng)建時(shí)調(diào)用。在該方法中,可以執(zhí)行創(chuàng)建數(shù)據(jù)庫表的SQL語句voidclose()數(shù)據(jù)庫關(guān)閉前調(diào)用voidonOpen(SQLiteDatabasedb)數(shù)據(jù)庫打開后調(diào)用voidonConfigure(SQLiteDatabasedb)數(shù)據(jù)庫被配置時(shí)調(diào)用,以啟用如寫日志、外鍵支持等功能。abstractvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion)當(dāng)數(shù)據(jù)庫需要升級(jí)時(shí)調(diào)用此方法。在該方法中,可以執(zhí)行升級(jí)數(shù)據(jù)庫表的SQL語句voidonDowngrade(SQLiteDatabasedb,intoldVersion,intnewVersion)當(dāng)數(shù)據(jù)庫需要降級(jí)時(shí)調(diào)用此方法。在該方法中,可以執(zhí)行降級(jí)數(shù)據(jù)庫表的SQL語句href="/reference/android/database/sqlite/SQLiteDatabase"SQLiteDatabasegetWritableDatabase()獲取一個(gè)可讀/寫的數(shù)據(jù)庫實(shí)例,如果數(shù)據(jù)庫不存在,則會(huì)調(diào)用onCreate()方法創(chuàng)建href="/reference/android/database/sqlite/SQLiteDatabase"SQLiteDatabasegetReadableDatabase()獲取一個(gè)可讀的數(shù)據(jù)庫實(shí)例,如果數(shù)據(jù)庫不存在,則會(huì)調(diào)用onCreate()方法創(chuàng)建表5-7SQLiteDatabase類操作數(shù)據(jù)庫方法方法作用voidexecSQL(Stringsql)執(zhí)行SQL語句,如創(chuàng)建表、插入數(shù)據(jù)、更新數(shù)據(jù)、刪除數(shù)據(jù)等,不能進(jìn)行查詢操作longinsert(Stringtable,StringnullColumnHack,ContentValuesvalues)插入數(shù)據(jù)int
update(Stringtable,ContentValuesvalues,StringwhereClause,String[]whereArgs)更新數(shù)據(jù)int
delete(Stringtable,StringwhereClause,String[]whereArgs)刪除數(shù)據(jù)long
replace(Stringtable,StringnullColumnHack,ContentValuesinitialValues)替換數(shù)據(jù)庫中一行數(shù)據(jù)Cursor
query(Stringtable,String[]columns,Stringselection,String[]selectionArgs,StringgroupBy,Stringhaving,StringorderBy)查詢數(shù)據(jù)table:表名;columns:列名稱數(shù)組,指定從哪些列尋找;selection:查詢的條件,相當(dāng)于where后的內(nèi)容;selectionArgs:查詢條件的參數(shù);groupBy:分組列;having:分組條件;orderBy:排序列;Cursor
rawQuery(Stringsql,String[]selectionArgs)執(zhí)行一條SQL查詢語句void
beginTransaction()開啟事務(wù)void
setTransactionSuccessful()標(biāo)記事務(wù)成功void
endTransaction()結(jié)束事務(wù)Cursor
rawQueryWithFactory(SQLiteDatabase.CursorFactorycursorFactory,Stringsql,String[]selectionArgs,StringeditTable)執(zhí)行一條SQL查詢語句,并使用指定的CursorFactory創(chuàng)建Cursorclose()關(guān)閉數(shù)據(jù)庫String
getPath()獲取數(shù)據(jù)庫文件路徑表5-8Cursor類常用方法方法作用abstractintgetCount()獲得游標(biāo)總項(xiàng)abstractintgetColumnIndexOrThrow(StringcolumnName)由列名稱獲得列索引abstractString[]
getColumnNames()獲得列名稱abstractintgetPosition()獲得游標(biāo)位置abstractbooleanisFirst()判斷游標(biāo)是否在第一行abstractbooleanisLast()判斷游標(biāo)是否在最后一行abstractbooleanmoveToFirst()移動(dòng)游標(biāo)到第一行abstractbooleanmoveToLast()移動(dòng)游標(biāo)到最后一行abstractbooleanmoveToNext()移動(dòng)游標(biāo)到下一行abstractbooleanmoveToPrevious()移動(dòng)游標(biāo)到上一行abstractbooleanmoveToPosition(intposition)移動(dòng)游標(biāo)到指定位置在Android中查詢數(shù)據(jù)是通過Cursor類來實(shí)現(xiàn)的,當(dāng)我們使用query()或rawQuery()方法時(shí),會(huì)得到一個(gè)Cursor對(duì)象,Cursor指向的就是每一條數(shù)據(jù)Android中SQLite數(shù)據(jù)庫的使用一般分為以下幾步:(1)創(chuàng)建數(shù)據(jù)庫:創(chuàng)建一個(gè)繼承自SQLiteOpenHelper的類,在該類中通過onCreate()方法創(chuàng)建一個(gè)數(shù)據(jù)庫;(2)創(chuàng)建表:通過SQLiteDatabase類的execSQL()方法直接執(zhí)行sql語句創(chuàng)建表;(3)操作表:通過SQLiteDatabase類的方法執(zhí)行數(shù)據(jù)庫中的數(shù)據(jù)增刪改查等操作,建議增刪改操作通過execSQL()方法直接執(zhí)行sql語句,因?yàn)槭褂胕nsert()、delete()、query()方法具備多個(gè)參數(shù),使用較為復(fù)雜;(4)關(guān)閉數(shù)據(jù)庫:調(diào)用SQLiteDatabase類的close()方法釋放數(shù)據(jù)庫連接,否則容易出現(xiàn)SQLiteException案例5.4
通過SQLite數(shù)據(jù)庫操作應(yīng)用存儲(chǔ)數(shù)據(jù)(參考代碼見:SQLiteActivity)運(yùn)行SQLiteActivity在模擬器生成應(yīng)用,在應(yīng)用界面選擇輸入學(xué)生信息后,點(diǎn)擊“添加”按鈕即可向數(shù)據(jù)庫中添加學(xué)生信息,數(shù)據(jù)庫文件student位于/data/data/包名/databases目錄下,點(diǎn)擊“查詢”按鈕可查看數(shù)據(jù)庫中保存的所有學(xué)生信息,在提示“輸入要操作的學(xué)生ID”的編輯框輸入學(xué)生id,點(diǎn)擊“刪除”按鈕即可刪除該學(xué)生所有信息,在提示“輸入要操作的學(xué)生ID”的編輯框輸入學(xué)生id,填入學(xué)生更改之后的數(shù)據(jù)即可對(duì)該學(xué)生的信息進(jìn)行修改。Andriod應(yīng)用開發(fā)基礎(chǔ)教程第6章ContentProvider6.1ContentProvider和URI簡介ContentProvider是Android四大組件之一,用于在不同的應(yīng)用程序之間共享數(shù)據(jù)。它提供了提供數(shù)據(jù)訪問的接口,例如聯(lián)系人、媒體文件、日歷事件等,使得應(yīng)用程序可以通過ContentResolver訪問和修改其他應(yīng)用程序中的數(shù)據(jù),而不需要了底層數(shù)據(jù)存儲(chǔ)的細(xì)節(jié),ContentProvider提供了安全的數(shù)據(jù)共享機(jī)制,可以控制數(shù)據(jù)的訪問權(quán)限。ContentProvider本身并不直接存儲(chǔ)數(shù)據(jù),它只是定義了數(shù)據(jù)的訪問接口,數(shù)據(jù)的實(shí)際存儲(chǔ)方式是第五章提過的內(nèi)部和外部存儲(chǔ)、SharedPreferences存儲(chǔ)、SQLite數(shù)據(jù)庫存儲(chǔ),ContentProvider能共享的數(shù)據(jù)包括照片、音頻、視頻等文件數(shù)據(jù),數(shù)據(jù)庫中的結(jié)構(gòu)化數(shù)據(jù)和網(wǎng)絡(luò)數(shù)據(jù)等。圖6-1ContentProvider數(shù)據(jù)共享方式URI是一種用于標(biāo)識(shí)資源的字符串,它可以是一個(gè)網(wǎng)址、文件路徑或其他形式的資源定位符,通常用于訪問ContentProvider提供的數(shù)據(jù)。URI由Scheme(協(xié)議)、Authority(權(quán)限)、Path(路徑)三部分組成,如圖6-2所示。(1)Scheme:采用前綴content://,是由Android規(guī)定的ContentProvider的URI前綴;(2)Authority:通常采用應(yīng)用程序的包名,要確保其唯一性,是ContentProvider的唯一標(biāo)識(shí)符;(3)Path:指定數(shù)據(jù)或資源的路徑,可以包含具體的表名、資源名以及其他標(biāo)識(shí)符。圖6-2中data后面的1為ID,是指數(shù)據(jù)資源的某個(gè)記錄,若無ID則返回路徑data中的全部記錄。URI使用涉及到ContentUris和UriMatcher兩個(gè)類,ContentUris類作用為操作Uri,UriMatcher類作用為在ContentProvider中注冊(cè)Uri并根據(jù)Uri匹配ContentProvider中對(duì)應(yīng)的數(shù)據(jù)表。表6-1ContentUris、UriMatcher類常用方法方法作用ContentUrisstaticUri
withAppendedId(UricontentUri,longid)在URI末尾追加一個(gè)idstaticlongparseId(UricontentUri)從URI中獲取idstaticUri
removeId(UricontentUri)從URI中刪除idUriMatchervoid
addURI(Stringauthority,Stringpath,intcode)添加要匹配的URI,匹配時(shí)添加注冊(cè)碼intmatch(Uriuri)匹配URI中的路徑,匹配時(shí)返回
addURI()添加的注冊(cè)碼,不匹配返回-16.2ContentProvider共享數(shù)據(jù)ContentProvider共享數(shù)據(jù)主要涉及到ContentProvider、ContentResolver和ContentObserver三個(gè)類,ContentProvider類并不會(huì)直接與外部應(yīng)用交互,而是通過ContentResolver類與ContentProvider類進(jìn)行交互,不同的ContentProvider用來標(biāo)識(shí)的Authority也不同,因而ContentResolver可與多個(gè)ContentProvider相互操作不同應(yīng)用的數(shù)據(jù)。ContentObserver類通過ContentResolver類進(jìn)行注冊(cè)并指定需要觀察的URI,當(dāng)觀察的URI與之對(duì)應(yīng)的ContentProvider中的數(shù)據(jù)發(fā)生增刪改變化操作時(shí),就會(huì)觸發(fā)該ContentObserver。表6-2ContentProvider、ContentResolver和ContentObserver三個(gè)類常用方法方法作用ContentProviderpublicUriinsert(Uriuri,ContentValuesvalues)外部應(yīng)用向ContentProvider添加數(shù)據(jù)publicintdelete(Uriuri,Stringselection,String[]selectionArgs)外部應(yīng)用刪除ContentProvider中的數(shù)據(jù)publicintupdate(Uriuri,ContentValuesvalues,Stringselection,String[]selectionArgs)外部應(yīng)用更新ContentProvider中的數(shù)據(jù)publicCursorquery(Uriuri,String[]projection,Stringselection,String[]selectionArgs,StringsortOrder)外部應(yīng)用獲取ContentProvider中的數(shù)據(jù)publicbooleanonCreate()ContentProvider被啟動(dòng)時(shí)調(diào)用publicStringgetType(Uriuri)得到數(shù)據(jù)類型,即返回當(dāng)前Uri所代表數(shù)據(jù)的MIME類型ContentResolverpublicUriinsert(Uriuri,ContentValuesvalues)外部應(yīng)用向ContentProvider添加數(shù)據(jù)publicintdelete(Uriuri,Stringselection,String[]selectionArgs)外部應(yīng)用刪除ContentProvider中的數(shù)據(jù)publicintupdate(Uriuri,ContentValuesvalues,Stringselection,String[]selectionArgs)外部應(yīng)用更新ContentProvider中的數(shù)據(jù)publicCursorquery(Uriuri,String[]projection,Stringselection,String[]selectionArgs,StringsortOrder)外部應(yīng)用獲取ContentProvider中的數(shù)據(jù)ContentObserverContentResolver.registerContentObserver(Uriuri,booleannotifyForDescendants,ContentObserverobserver)注冊(cè)ContentObserver對(duì)象,在數(shù)據(jù)發(fā)生改變時(shí)接收通知ContentResolver.unregisterContentObserver(ContentObserverobserver)取消注冊(cè)ContentObserver對(duì)象,停止接收數(shù)據(jù)改變的通知publicvoidonChange(booleanselfChange)當(dāng)觀察的內(nèi)容發(fā)生改變的時(shí)候調(diào)用圖6-3ContentProvider、ContentResolver、ContentObserver三者關(guān)系ContentProvider的使用步驟主要包含以下幾步:1、創(chuàng)建一個(gè)繼承自ContentProvider的類,并實(shí)現(xiàn)該接口必要的方法;2、在清單文件AndroidManifest.xml中在<application>標(biāo)簽下添加一個(gè)<provider>子標(biāo)簽來注冊(cè)ContentProvider;3、定義URI用于標(biāo)識(shí)ContentProvider的數(shù)據(jù);4、在其他應(yīng)用程序中,使用ContentResolver來訪問ContentProvider提供的數(shù)據(jù);5、可以使用ContentObserver于監(jiān)聽ContentProvider的數(shù)據(jù)變化。案例6.1
使用ContentProvider、ContentResolver和ContentObserver實(shí)現(xiàn)不同應(yīng)用間的數(shù)據(jù)共享、數(shù)據(jù)操作和數(shù)據(jù)監(jiān)聽。(參考代碼見:contentproviderdemo、contentresolverdemo模塊下文件)首先運(yùn)行contentproviderdemo下的MainActivity生成數(shù)據(jù)庫文件student.db,該文件位于模擬器/data/data/包名/database目錄下,該數(shù)據(jù)庫文件通過ContentProvider實(shí)現(xiàn)共享,其它應(yīng)用也可對(duì)該文件進(jìn)行訪問修改操作。再運(yùn)行contentresolverdemo模塊下的ContentResolverActivity,應(yīng)用界面如左圖。在應(yīng)用界面可對(duì)學(xué)生信息進(jìn)行增刪改查操作,當(dāng)學(xué)生信息發(fā)生改變時(shí),控制臺(tái)輸出提示信息。Andriod應(yīng)用開發(fā)基礎(chǔ)教程第7章BroadcastReceiver7.1廣播概述BroadcastReceiver是Android四大組件之一,Android中的廣播使用了觀察者模式,即基于消息的發(fā)布-訂閱事件的模式。廣播發(fā)送者和接收者分別處于觀察者模式中的消息發(fā)布和訂閱兩端。①廣播接收者通過Binder機(jī)制在AMS(ActivityManagerService)中進(jìn)行注冊(cè)。②廣播發(fā)送者通過Binder機(jī)制向AMS發(fā)送廣播。③AMS查找符合相應(yīng)條件(IntentFilter/Permission)的廣播接收者,將廣播發(fā)送到相應(yīng)的消息循環(huán)隊(duì)列中。④執(zhí)行消息循環(huán)時(shí)獲取到發(fā)送的廣播,然后回調(diào)廣播接收者中的onReceive()方法并在該方法中進(jìn)行相關(guān)處理。這些廣播會(huì)在所關(guān)注的事件發(fā)生時(shí)發(fā)送。例如,Android系統(tǒng)會(huì)在發(fā)生各種系統(tǒng)事件時(shí)發(fā)送廣播,例如系統(tǒng)啟動(dòng)或設(shè)備開始充電時(shí)。應(yīng)用還可以發(fā)送自定義廣播,例如,通知其他應(yīng)用它們可能感興趣的內(nèi)容(例如,一些新數(shù)據(jù)已下載、電池電量不足等)。為了保持最佳系統(tǒng)運(yùn)行狀況,系統(tǒng)會(huì)優(yōu)化廣播的傳送。因此,廣播的傳送時(shí)間無法保證。需要低延遲進(jìn)程間通信的應(yīng)用應(yīng)考慮綁定服務(wù)。應(yīng)用可以注冊(cè)接收特定的廣播。發(fā)送廣播后,系統(tǒng)會(huì)自動(dòng)將廣播路由到已訂閱接收該特定類型的廣播的應(yīng)用。一般來說,廣播可用作跨應(yīng)用和正常用戶流之外的消息傳遞系統(tǒng)。但是不要濫用在后臺(tái)響應(yīng)廣播和運(yùn)行作業(yè)的機(jī)會(huì),否則可能會(huì)導(dǎo)致系統(tǒng)性能變慢。7.2廣播的注冊(cè)7.2.1靜態(tài)注冊(cè)
靜態(tài)注冊(cè)的特點(diǎn)是無論應(yīng)用程序是否處于運(yùn)行狀態(tài),廣播接收者都會(huì)對(duì)程序進(jìn)行監(jiān)聽,因此耗用資源大,Andriod8.0之后對(duì)靜態(tài)注冊(cè)進(jìn)行了限制。BroadcastReceiver在使用之前需要向系統(tǒng)注冊(cè),有兩種注冊(cè)方式:靜態(tài)注冊(cè)和動(dòng)態(tài)注冊(cè)。在AndroidManifest.xml進(jìn)行配置即靜態(tài)注冊(cè),也可在代碼中動(dòng)態(tài)注冊(cè)廣播。表7-1系統(tǒng)廣播Action舉例對(duì)應(yīng)字符串說明ent.action.PACKAGE_REMOVED設(shè)備上刪除了一個(gè)應(yīng)用程序包ent.action.PACKAGE_ADDED設(shè)備上新安裝了一個(gè)應(yīng)用程序包ent.action.BATTERY_CHANGED充電狀態(tài),或者電池的電量發(fā)生變化ent.action.BATTERY_LOW電池電量低ent.action.TIMEZONE_CHANGED時(shí)區(qū)已經(jīng)改變ent.action.DATE_CHANGED日期被改變ent.action.DEVICE_STORAGE_LOW設(shè)備存儲(chǔ)不足ent.action.SCREEN_OFF屏幕被關(guān)閉ent.action.SCREEN_ON屏幕已經(jīng)被打開ent.action.WALLPAPER_CHANGED系統(tǒng)的墻紙已經(jīng)改變靜態(tài)注冊(cè)廣播接收器的具體步驟如下:創(chuàng)建一個(gè)廣播接收器類,繼承自BroadcastReceiver類,并重寫onReceive()方法。在該方法中,可以處理接收到的廣播消息。在AndroidManifest.xml文件中,聲明廣播接收器。在標(biāo)簽內(nèi)添加標(biāo)簽,指定接收器的類名和接收的廣播類型。在接收器的onReceive()方法中,處理接收到的廣播消息。案例7.1
當(dāng)應(yīng)用卸載時(shí),控制臺(tái)打印接收到的廣播內(nèi)容。(參考代碼見:broadcastrecevierdemo模塊下MyBroadCast.java、AndroidManifest.xml)MyBroadCast代碼:1publicclassMyBroadCastextendsBroadcastReceiver{2
3privatestaticfinalStringTAG="MyBroadCast";4@Override5publicvoidonReceive(Contextcontext,Intentintent){6
7if(intent!=null){8//判斷接受到的廣播9
Stringaction=intent.getAction();10Log.d(TAG,"onReceive:"+action);11}12}13}在AndroidManifest.xml中注冊(cè)廣播:1<!--靜態(tài)注冊(cè)廣播接收器-->2<receiver3android:name=".MyBroadCast"4android:exported="true">5<!--接受的廣播-->6<intent-filter>7<!--卸載應(yīng)用廣播-->8<actionandroid:name="ent.action.PACKAGE_REMOVED"/>9<!--數(shù)據(jù)類型-->10<dataandroid:scheme="package"/>11</intent-filter>12</receiver>MainActivity代碼不變,現(xiàn)從Andriod模擬器中卸載一個(gè)應(yīng)用,控制臺(tái)輸出信息如下圖7.2.2
動(dòng)態(tài)注冊(cè)
動(dòng)態(tài)注冊(cè)的廣播接收器可以自由的控制注冊(cè)與注銷,具有很大的靈活性,但是動(dòng)態(tài)注冊(cè)的廣播接收器必須在程序啟動(dòng)之后才能接收到廣播,因?yàn)樽?cè)的邏輯代碼是寫在onCreate()當(dāng)中的。動(dòng)態(tài)注冊(cè)廣播接收器的具體步驟如下。(1)創(chuàng)建一個(gè)廣播接收器類,繼承自BroadcastReceiver類,并重寫onReceive()方法。在該方法中,可以處理接收到的廣播消息。(2)在Activity的onCreate()方法中通過調(diào)用Context.registerReceiver()方法注冊(cè)廣播接收器。(3)在不需要接收廣播時(shí),應(yīng)當(dāng)調(diào)用unregisterReceiver()方法來注銷廣播接收器,這通常是在Activity的onDestroy()方法中進(jìn)行。案例7.2
動(dòng)態(tài)注冊(cè)廣播方式說明。(參考代碼見:broadcastrecevierdemo模塊下MainActivity.java)MainActivity代碼:1publicclassMainActivityextendsAppCompatActivity{2
3privateMyBroadCastmBroadCast;4
5@Override6protectedvoidonCreate(BundlesavedInstanceState){7super.onCreate(savedInstanceState);8setContentView(R.layout.activity_main);9
10//新建廣播接收器11mBroadCast=newMyBroadCast();12//要接收的廣播13IntentFilterintentFilter=newIntentFilter();14intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);15intentFilter.addDataScheme("package");16
17//注冊(cè)廣播接收器18registerReceiver(mBroadCast,intentFilter);19}20
21@Override22protectedvoidonDestroy(){23super.onDestroy();24//取消注冊(cè)廣播接收器25if(mBroadCast!=null){26unregisterReceiver(mBroadCast);27}28}29
30}從Andriod模擬器中再卸載一個(gè)應(yīng)用后,控制臺(tái)輸出提示信息7.3自定義廣播Android中內(nèi)置了多個(gè)系統(tǒng)廣播:只要涉及到手機(jī)的基本操作(如開機(jī)、網(wǎng)絡(luò)狀態(tài)變化、拍照等等),都會(huì)發(fā)出相應(yīng)的廣播,常見系統(tǒng)廣播如表7-1所示。除系統(tǒng)廣播外用戶還可以自定義廣播。自定義廣播接收器需要繼承基類BroadcastReceiver,并實(shí)現(xiàn)抽象方法onReceive(context,intent)方法。廣播接收器接收到相應(yīng)廣播后,會(huì)自動(dòng)回到onReceive方法。默認(rèn)情況下,廣播接收器也是運(yùn)行在UI線程,因此,onReceive方法中不能執(zhí)行太耗時(shí)的操作。否則將因此ANR。一般情況下,根據(jù)實(shí)際業(yè)務(wù)需求,onReceive方法中都會(huì)涉及到與其他組件之間的交互,如發(fā)送Notification、啟動(dòng)Service等。案例7.3
自定義廣播。(參考代碼見:MyBroadcastActivity、MyBroadcastReceiver.java)7.4App應(yīng)用間廣播案例7.3
自定義廣播。修改案例7-3中的模塊代碼,將資源目錄res下values目錄中的string.xml內(nèi)容更新為:1<resources>2<stringname="app_name">Demo1</string>3</resources>復(fù)制案例7-3中的模塊到新的項(xiàng)目,將資源目錄res下values目錄中的string.xml內(nèi)容修改為:1<resources>2<stringname="app_name">Demo2</string>3</resources>分別運(yùn)行兩個(gè)項(xiàng)目中的MyBroadcastActivity,在安卓模擬器中生成Demo1和Demo2兩個(gè)應(yīng)用,運(yùn)行Demo1后向文本輸入框內(nèi)輸入fromdemo1,運(yùn)行效果如圖7-4所示,打開應(yīng)用Demo2,界面如圖7-5所示??梢妬碜杂趹?yīng)用Demo1的廣播傳到了應(yīng)用Demo2,在Demo2文本輸入框內(nèi)輸入內(nèi)容也能夠通過廣播傳到應(yīng)用Demo1,以上即為應(yīng)用內(nèi)廣播的實(shí)現(xiàn)。
圖7-4運(yùn)行Demo1效果
圖7-5打開Demo2效果Andriod應(yīng)用開發(fā)基礎(chǔ)教程第8章Service8.1Service簡介Service是一種可在后臺(tái)執(zhí)行長時(shí)間運(yùn)行的操作的應(yīng)用組件,是Android四大組件之一,
屬于計(jì)算型組件,不提供界面。啟動(dòng)后,即使用戶切換到其他應(yīng)用,服務(wù)也可能會(huì)繼續(xù)運(yùn)行一段時(shí)間。此外,組件可以綁定到服務(wù)以與其交互,甚至可以執(zhí)行進(jìn)程間通信(IPC)。例如,服務(wù)可以在后臺(tái)處理網(wǎng)絡(luò)事務(wù)、播放音樂、執(zhí)行文件I/O或與ContentProvider交互。Service按照運(yùn)行類型可分為兩種:(1)前臺(tái)服務(wù)前臺(tái)服務(wù)用于執(zhí)行可被用戶注意到的操作,會(huì)顯示狀態(tài)欄通知,讓用戶知道應(yīng)用正在前臺(tái)執(zhí)行任務(wù)并消耗系統(tǒng)資源。例如,一款音樂播放器應(yīng)用,在前臺(tái)服務(wù)中播放音樂,通知會(huì)顯示當(dāng)前播放的歌曲。前臺(tái)服務(wù)必須顯示狀態(tài)欄通知。從Android13(API級(jí)別33)開始,默認(rèn)情況下,用戶可以關(guān)閉與前臺(tái)服務(wù)關(guān)聯(lián)的通知。為此,用戶可以在通知上執(zhí)行滑動(dòng)手勢(shì),除非前臺(tái)服務(wù)停止或從前臺(tái)移除,否則通知不會(huì)關(guān)閉。(2)后臺(tái)服務(wù)后臺(tái)服務(wù)執(zhí)行時(shí)用戶不會(huì)直接注意到或進(jìn)行相關(guān)操作,例如天氣更新、日期同步等。應(yīng)用為API級(jí)別26或更高級(jí)別時(shí),那么當(dāng)應(yīng)用本身不在前臺(tái)運(yùn)行時(shí),系統(tǒng)會(huì)對(duì)運(yùn)行在后臺(tái)的服務(wù)加以限制。8.2Service生命周期服務(wù)的生命周期比Activity的生命周期簡單得多且不受Activity的生命周期影響。但是,密切關(guān)注如何創(chuàng)建和銷毀服務(wù)更重要,因?yàn)榉?wù)可以在用戶不知情的情況下在后臺(tái)運(yùn)行。Service生命周期可用圖8-1說明,左圖顯示了使用startService()創(chuàng)建服務(wù)的生命周期,右圖顯示了使用bindService()創(chuàng)建服務(wù)的生命周期。圖8-1服務(wù)生命周期服務(wù)生命周期(從創(chuàng)建到銷毀)可以遵循以下兩種路徑之一:1、啟動(dòng)服務(wù)(Unboundedservice)該服務(wù)在另一個(gè)組件調(diào)用startService()時(shí)創(chuàng)建。然后無限期運(yùn)行,且必須通過調(diào)用stopSelf()來自行停止運(yùn)行。另一個(gè)組件也可以通過調(diào)用stopService()來停止該服務(wù)。服務(wù)停止后,系統(tǒng)會(huì)將其銷毀,如8-1左圖所示。2、綁定服務(wù)(Boundedservice)該服務(wù)在另一個(gè)組件(客戶端)調(diào)用bindService()時(shí)創(chuàng)建,服務(wù)即處于綁定狀態(tài)。綁定服務(wù)提供了一個(gè)客戶端-服務(wù)器接口,該接口允許組件與服務(wù)進(jìn)行交互、發(fā)送請(qǐng)求、接收結(jié)果,甚至使用進(jìn)程間通信(IPC)跨進(jìn)程執(zhí)行這些操作??蛻舳丝赏ㄟ^調(diào)用unbindService()關(guān)閉連接。多個(gè)客戶端可以綁定到同一項(xiàng)服務(wù),當(dāng)所有綁定全部取消時(shí),系統(tǒng)將銷毀該服務(wù)。此服務(wù)不需要自行停止運(yùn)行,如8-1右圖所示這兩條路徑并非完全獨(dú)立,綁定到已使用startService()啟動(dòng)的服務(wù)是可以的,如圖8-2所示。例如,啟動(dòng)后臺(tái)音樂服務(wù)時(shí)使用用于標(biāo)識(shí)要播放的音樂的Intent調(diào)用startService()。稍后,當(dāng)用戶想要對(duì)播放器進(jìn)行某種控制或獲取有關(guān)當(dāng)前歌曲的信息時(shí),Activity可以通過調(diào)用bindService()綁定到該服務(wù)。在這種情況下,在所有客戶端取消綁定之前,stopService()或stopSelf()實(shí)際上不會(huì)停止服務(wù)。圖8-2先啟動(dòng)再綁定服務(wù)2
注意:1、無論服務(wù)是通過startService()還是bindService()創(chuàng)建,都會(huì)針對(duì)所有服務(wù)調(diào)用onCreate()和onDestroy()方法。2、盡管已啟動(dòng)的服務(wù)通過調(diào)用stopSelf()或stopService()停止,但該服務(wù)并無相應(yīng)的回調(diào)(沒有onStop()回調(diào))。除非服務(wù)綁定到客戶端,否則系統(tǒng)會(huì)在服務(wù)停止時(shí)將其銷毀(onDestroy()是收到的唯一回調(diào))。8.3Service執(zhí)行過程表8-1Service啟動(dòng)、銷毀、綁定和解綁使用方法作用手動(dòng)調(diào)用方法內(nèi)部自動(dòng)調(diào)用方法啟動(dòng)Service服務(wù)startService()onCreate()、onStartCommand()停止Service服務(wù)stopService()onDestroy()綁定Service服務(wù)bindService()onCreate()、onBind()解綁Service服務(wù)unbindService()onUnbind()、onDestroy()8.3.1Service的創(chuàng)建
要?jiǎng)?chuàng)建服務(wù),必須創(chuàng)建Service的子類或使用其現(xiàn)有的子類之一。在實(shí)現(xiàn)中,必須重寫處理Service生命周期的關(guān)鍵回調(diào)方法,并在適當(dāng)?shù)那闆r下提供一種允許組件綁定到服務(wù)的機(jī)制。以下幾個(gè)是應(yīng)該覆蓋的最重要的回調(diào)方法:1、onStartCommand()當(dāng)另一個(gè)組件(如Activity)請(qǐng)求啟動(dòng)服務(wù)時(shí),系統(tǒng)會(huì)通過調(diào)用startService()來調(diào)用此方法。執(zhí)行此方法時(shí),服務(wù)會(huì)啟動(dòng)并且可以在后臺(tái)無限期運(yùn)行。實(shí)現(xiàn)此方法時(shí),需在服務(wù)工作完成后通過調(diào)用stopSelf()或stopService()來停止服務(wù)。如果只想提供綁定,則無需實(shí)現(xiàn)此方法。2、onBind()當(dāng)另一個(gè)組件想要與服務(wù)綁定(例如執(zhí)行RPC)時(shí),系統(tǒng)會(huì)通過調(diào)用bindService()來調(diào)用此方法。在此方法的實(shí)現(xiàn)中,必須返回IBinder提供一個(gè)接口,以供客戶端用來與服務(wù)通信。如果您不想允許綁定,則應(yīng)返回null。3、onCreate()首次創(chuàng)建服務(wù)時(shí)(在調(diào)用onStartCommand()或onBind()之前),系統(tǒng)會(huì)調(diào)用此方法來執(zhí)行一次性設(shè)置過程。如果服務(wù)已在運(yùn)行,則不會(huì)調(diào)用此方法。4、onDestroy()當(dāng)服務(wù)不再使用并且即將被銷毀時(shí),系統(tǒng)會(huì)調(diào)用此方法。服務(wù)應(yīng)實(shí)現(xiàn)此機(jī)制以清理所有資源,例如線程、注冊(cè)的監(jiān)聽器或接收器。這是服務(wù)接收的最后一個(gè)調(diào)用方法。案例8.1
用戶單擊應(yīng)用界面相應(yīng)的按鈕Service將執(zhí)行相應(yīng)的操作,在控制臺(tái)輸出執(zhí)行的過程。(參考代碼見:servicedemo模塊下)8.3.2Service的啟動(dòng)
Service的啟動(dòng)方式主要有兩種,分別是startService()和bindService()。下面將通過案例8-1對(duì)這兩種啟動(dòng)方式加以說明。執(zhí)行案例8-1中的MainActivity將在模擬器生成相關(guān)應(yīng)用,首次點(diǎn)擊“啟動(dòng)服務(wù)”按鈕,控制臺(tái)輸出信息如圖8-4(a)所示,再次雙擊“啟動(dòng)服務(wù)”按鈕控制臺(tái)輸出信息如圖8-4(b)所示。
圖8-4(a)首次點(diǎn)擊“啟動(dòng)服務(wù)”按鈕控制臺(tái)輸出信息
圖8-4(b)再次雙擊“啟動(dòng)服務(wù)”按鈕控制臺(tái)輸出信息由圖8-4分析可知,首次點(diǎn)擊“啟動(dòng)服務(wù)”按鈕,控制臺(tái)輸出“服務(wù)創(chuàng)建了”和“服務(wù)啟動(dòng)了”這兩條信息,表明第一次啟動(dòng)Service時(shí)依次調(diào)用了onCreate()和onStartCommand()方法,再點(diǎn)擊兩次“啟動(dòng)服務(wù)”按鈕控制臺(tái)輸出兩次“服務(wù)啟動(dòng)了”,表明如果不是第一次啟動(dòng)Service只會(huì)調(diào)用onStartCommand()方法,這是由于startService()啟動(dòng)時(shí)是單獨(dú)開一個(gè)服務(wù),與Activity沒有任何關(guān)系,即使用的是同一個(gè)Service,因此onStart()會(huì)執(zhí)行多次,onCreate()只執(zhí)行一次,onStartCommand()也會(huì)執(zhí)行多次。bindService()方式啟動(dòng)時(shí),Service會(huì)和Activity進(jìn)行綁定,當(dāng)對(duì)應(yīng)的Activity銷毀時(shí),對(duì)應(yīng)的Service也會(huì)銷毀。銷毀Service后首次點(diǎn)擊“綁定服務(wù)”按鈕,控制臺(tái)輸出信息如圖8-6(a)所示,此時(shí)控制臺(tái)輸出“服務(wù)創(chuàng)建了”和“服務(wù)綁定了”這兩條信息,表明服務(wù)創(chuàng)建前執(zhí)行綁定服務(wù),依次調(diào)用了onCreate()和onBind()方法;銷毀Service后依次點(diǎn)擊“啟動(dòng)服務(wù)”和“綁定服務(wù)”按鈕,控制臺(tái)輸出信息如圖8-6(b)所示,此時(shí)控制臺(tái)輸出“服務(wù)創(chuàng)建了”、“服務(wù)啟動(dòng)了”和“服務(wù)綁定了”三條信息,表明服務(wù)啟動(dòng)后依然可以綁定服務(wù)。圖8-6(a)首次點(diǎn)擊“綁定服務(wù)”按鈕控制臺(tái)輸出信息圖8-6(b)依次點(diǎn)擊“啟動(dòng)服務(wù)”和“綁定服務(wù)”按鈕,控制臺(tái)輸出信息8.3.3Service的停止
啟動(dòng)服務(wù)必須管理自己的生命周期。也就是說,除非必須回收系統(tǒng)內(nèi)存,否則系統(tǒng)不會(huì)停止或銷毀服務(wù),并且服務(wù)在onStartCommand()返回后會(huì)繼續(xù)運(yùn)行。服務(wù)必須通過調(diào)用stopSelf()自行停止,或者其他組件可以通過調(diào)用stopService()來停止它。一旦請(qǐng)求使用stopSelf()或stopService()停止服務(wù),系統(tǒng)會(huì)盡快銷毀服務(wù)。銷毀Service后依次點(diǎn)擊“啟動(dòng)服務(wù)”和“停止服務(wù)”按鈕,控制臺(tái)輸出信息如圖8-8(a)所示,分析知通過startService()方法啟動(dòng)服務(wù)可直接通過stopService()銷毀該Service。然后依次點(diǎn)擊“綁定服務(wù)”和“解綁服務(wù)”按鈕,控制臺(tái)輸出信息如圖8-8(b)所示,表明bindService()方式啟動(dòng)Service后綁定了Service,解綁Service能銷毀該服務(wù)。最后依次點(diǎn)擊“綁定服務(wù)”和“停止服務(wù)”按鈕,控制臺(tái)只會(huì)輸出綁定信息,并不會(huì)彈出銷毀信息,如圖8-8(c)所示,說明服務(wù)沒有解綁之前不能銷毀該服務(wù)。圖8-8(a)依次點(diǎn)擊“啟動(dòng)服務(wù)”和“停止服務(wù)”按鈕,控制臺(tái)輸出信息圖8-8(b)依次點(diǎn)擊“綁定服務(wù)”和“解綁服務(wù)”按鈕,控制臺(tái)輸出信息圖8-8(c)依次點(diǎn)擊“綁定服務(wù)”和“停止服務(wù)”按鈕,控制臺(tái)輸出信息8.3.4Service的解綁
多個(gè)客戶端可同時(shí)綁定到服務(wù)。當(dāng)客戶端與服務(wù)完成交互后,會(huì)調(diào)用unbindService()來解除綁定。如果沒有綁定到服務(wù)的客戶端,則系統(tǒng)會(huì)銷毀該服務(wù)。服務(wù)解綁之前必然先要綁定服務(wù)。依次點(diǎn)擊“綁定服務(wù)”“啟動(dòng)服務(wù)”“解綁服務(wù)”三個(gè)按鈕,控制臺(tái)輸出信息如圖8-10(a)所示,控制臺(tái)輸出“服務(wù)創(chuàng)建了”“服務(wù)綁定了”“服務(wù)啟動(dòng)了”“服務(wù)解綁了”四條信息,分析知第一種解綁服務(wù)方式直接調(diào)用onUnbind()方法,服務(wù)綁定之后調(diào)用onStartCommand()方法啟動(dòng)服務(wù)再解綁,該服務(wù)只會(huì)解綁并不會(huì)被銷毀。點(diǎn)擊“停止服務(wù)”按鈕銷毀當(dāng)前服務(wù)后,依次點(diǎn)擊“綁定服務(wù)”和“解綁服務(wù)”按鈕,控制臺(tái)輸出信息如圖8-10(b)所示,說明第二種解綁服務(wù)方式依次調(diào)用了onUnbind()和onDestory()方法,服務(wù)綁定之后未調(diào)用onStartCommand()方法啟動(dòng)服務(wù)直接進(jìn)行解綁,該服務(wù)會(huì)先解綁再被銷毀。圖8-10(a)依次點(diǎn)擊“綁定服務(wù)”“啟動(dòng)服務(wù)”“解綁服務(wù)”三個(gè)按鈕,控制臺(tái)輸出信息圖8-10(b)依次點(diǎn)擊“綁定服務(wù)”和“解綁服務(wù)”按鈕,控制臺(tái)輸出信息案例8.2
模擬耗時(shí)操作,通過bindService()實(shí)現(xiàn)進(jìn)度監(jiān)控。(參考代碼見:servicedemo模塊下)依次點(diǎn)擊按鈕“綁定服務(wù)”“啟動(dòng)服務(wù)”按鈕,控制臺(tái)輸出“服務(wù)創(chuàng)建了”“服務(wù)綁定了”“你好”“進(jìn)度為1”“服務(wù)啟動(dòng)了”五條信息,再點(diǎn)擊“解綁服務(wù)”按鈕,控制臺(tái)輸出信息多了一條“服務(wù)解綁了”信息,隔段時(shí)間再點(diǎn)擊“綁定服務(wù)”按鈕,進(jìn)度從原先的1變?yōu)?3,再點(diǎn)擊“解綁服務(wù)”,再隔段時(shí)間點(diǎn)擊“綁定服務(wù)”,進(jìn)度增加為25。分析進(jìn)度變化知可通過綁定服務(wù)監(jiān)控進(jìn)度,在耗時(shí)任務(wù)中查看進(jìn)度變化。8.4跨進(jìn)度調(diào)用Service8.4.1AIDL介紹
AIDL全稱為AndroidInterfaceDefinitionLanguage,意為Android接口定義語言,是用于定義服務(wù)器和客戶端通信接口的一種描述語言,只有在允許來自不同應(yīng)用的客戶端跨進(jìn)程通信訪問Service,并且想要用Service處理多線程時(shí)才需要使用到AIDL。AIDL是Binder機(jī)制向外提供的接口,目的就是為了方便實(shí)現(xiàn)Binder的使用。AIDL文件以.aidl為后綴名,使用AIDL創(chuàng)建綁定服務(wù)時(shí),操作步驟如下:1、創(chuàng)建.aidl文件在module中創(chuàng)建一個(gè).aidl文件,隨著該文件的創(chuàng)建會(huì)自動(dòng)生成一個(gè)aidl目錄。2、實(shí)現(xiàn)接口重構(gòu)項(xiàng)目后AndroidSDK工具會(huì)根據(jù).aidl文件,使用Java編程語言生成一個(gè)接口。此接口具有一個(gè)名為Stub的內(nèi)部抽象類,該類擴(kuò)展Binder并實(shí)現(xiàn)AIDL接口中的方法,必須擴(kuò)展Stub類并實(shí)現(xiàn)相應(yīng)方法。3、向客戶端公開該接口實(shí)現(xiàn)Service并替換onBind(),以返回Stub類的實(shí)現(xiàn)。8.4.2遠(yuǎn)程服務(wù)開啟和通信
案例8.3
使用AIDL遠(yuǎn)程開啟案例8-2中的應(yīng)用并且能實(shí)現(xiàn)和該應(yīng)用的通信。(參考代碼見:servicedemo、aidldemo模塊下)依次點(diǎn)擊應(yīng)用界面的“遠(yuǎn)程綁定服務(wù)”“遠(yuǎn)程啟動(dòng)服務(wù)”按鈕,控制臺(tái)輸出信息如圖8-16(a)所示,點(diǎn)擊“遠(yuǎn)程解綁服務(wù)”按鈕后隔段時(shí)間再次點(diǎn)擊“遠(yuǎn)程綁定服務(wù)”按鈕,控制臺(tái)輸出信息如圖8-16(b)所示。分析知AIDL遠(yuǎn)程開啟案例8-2中的應(yīng)用并且實(shí)現(xiàn)和該應(yīng)用的通信。圖8-16(a)依次點(diǎn)擊“遠(yuǎn)程綁定服務(wù)”“遠(yuǎn)程啟動(dòng)服務(wù)”按鈕,控制臺(tái)輸出信息圖8-16(b)點(diǎn)擊“遠(yuǎn)程解綁服務(wù)”按鈕后再次點(diǎn)擊“遠(yuǎn)程綁定服務(wù)”按鈕,控制臺(tái)輸出信息Andriod應(yīng)用開發(fā)基礎(chǔ)教程第9章網(wǎng)絡(luò)編程9.1UDP、TCP、HTTP協(xié)議基礎(chǔ)UDP是一種無連接的、不可靠的協(xié)議,它不會(huì)對(duì)數(shù)據(jù)進(jìn)行確認(rèn)或重傳,也不會(huì)進(jìn)行數(shù)據(jù)排序。UDP不需要建立連接,以報(bào)文(Datagram)為單位來收發(fā)數(shù)據(jù),數(shù)據(jù)包可以直接發(fā)送,每個(gè)UDP包都是獨(dú)立的,并且可能會(huì)在傳輸過程中丟失、重復(fù)、亂序。因此,UDP適用于需要快速傳輸數(shù)據(jù),而對(duì)數(shù)據(jù)可靠性要求不高的應(yīng)用場(chǎng)景,如網(wǎng)絡(luò)直播、在線游戲、IP電話、視頻會(huì)議等。TCP是一種面向連接的、可靠的協(xié)議,它為數(shù)據(jù)包的傳輸提供了確認(rèn)、重傳、數(shù)據(jù)排序和流量控制等功能,保證了數(shù)據(jù)的可靠性。TCP通過三次握手來建立連接,四次揮手來終止連接。在傳輸數(shù)據(jù)時(shí),TCP將數(shù)據(jù)分成多個(gè)包進(jìn)行傳輸,每個(gè)包都有自己的編號(hào)和校驗(yàn),接收方會(huì)對(duì)每個(gè)包進(jìn)行確認(rèn),如果某個(gè)包沒有被確認(rèn),發(fā)送方會(huì)重新發(fā)送這個(gè)包,直到接收方確認(rèn)為止。TCP適用于需要可靠傳輸?shù)膽?yīng)用場(chǎng)景,如文件傳輸、Web瀏覽器、電子郵件等。以人與人之間的通信為例,UDP協(xié)議就相當(dāng)于是寫信給對(duì)方,寄出去信件之后不能知道對(duì)方是否收到信件,信件內(nèi)容是否完整,也不能得到及時(shí)反饋,而TCP協(xié)議就像是打電話通信,在這一系列流程都能得到及時(shí)反饋,并能確保對(duì)方及時(shí)接收到。HTTP協(xié)議全稱為HypertextTransferProtocol,即超文本傳輸協(xié)議,是互聯(lián)網(wǎng)上應(yīng)用最為廣泛的一種網(wǎng)絡(luò)傳輸協(xié)議。HTTP協(xié)議定義了客戶端(Browser)與服務(wù)器之間的通信規(guī)范,以實(shí)現(xiàn)對(duì)各種資源(如HTML頁面、圖像、音頻、視頻等)的傳輸和訪問。HTTP協(xié)議是一個(gè)無狀態(tài)的應(yīng)用層協(xié)議,基于請(qǐng)求響應(yīng)模式且請(qǐng)求和響應(yīng)都是一條文本消息,再則HTTP協(xié)議是一個(gè)無連接協(xié)議,所以每個(gè)請(qǐng)求都是獨(dú)立的,服務(wù)器處理請(qǐng)求后立即關(guān)閉連接。9.2Socket通信Socket就是兩個(gè)應(yīng)用程序通過一個(gè)雙向的通信連接實(shí)現(xiàn)數(shù)據(jù)的交換,連接的一段就是一個(gè)Socket,又稱為套接字。實(shí)現(xiàn)一個(gè)Socket連接通信至少需要兩個(gè)套接字,一個(gè)運(yùn)行在服務(wù)端(插孔),一個(gè)運(yùn)行在客戶端(插頭)。套接字用于描述IP地址和端口,是一個(gè)通信鏈的句柄。應(yīng)用程序通過套接字向網(wǎng)絡(luò)發(fā)出請(qǐng)求或應(yīng)答網(wǎng)絡(luò)請(qǐng)求。注意的是套接字既不是程序也不是協(xié)議,只是操作系統(tǒng)提供給通信層的一組抽象API接口。Socket通信是指應(yīng)用程序利用Socket接口實(shí)現(xiàn)的網(wǎng)絡(luò)通信,用于在不同的計(jì)算機(jī)之間傳輸數(shù)據(jù)。Socket是一種標(biāo)準(zhǔn)的API(應(yīng)用程序接口),可用于在不同的計(jì)算機(jī)之間傳輸數(shù)據(jù),它支持TCP、UDP等多種網(wǎng)絡(luò)協(xié)議。在Socket通信中,客戶端和服務(wù)器通過Socket接口進(jìn)行通信,通信時(shí)需指定IP地址和端口號(hào),IP地址用于唯一標(biāo)志網(wǎng)絡(luò)設(shè)備,端口主要用于區(qū)分主機(jī)上的不同應(yīng)用(沒有端口,操作系統(tǒng)沒有辦法區(qū)分?jǐn)?shù)據(jù)到底應(yīng)該發(fā)送到哪一個(gè)應(yīng)用上??蛻舳藙?chuàng)建一個(gè)Socket對(duì)象,并將其連接到服務(wù)器上的一個(gè)特定端口號(hào),通過Socket對(duì)象向服務(wù)器發(fā)送數(shù)據(jù)。服務(wù)器監(jiān)聽指定端口號(hào),等待客戶端的連接請(qǐng)求,當(dāng)連接請(qǐng)求到達(dá)時(shí),服務(wù)器會(huì)創(chuàng)建一個(gè)新的Socket對(duì)象,并與客戶端的Socket對(duì)象建立連接。然后,服務(wù)器和客戶端之間就可以進(jìn)行數(shù)據(jù)傳輸了。Socket通信的優(yōu)點(diǎn)是它是跨平臺(tái)的,因?yàn)镾ocket接口是標(biāo)準(zhǔn)的API,不受特定操作系統(tǒng)的限制。它也很靈活,可以使用不同的網(wǎng)絡(luò)協(xié)議(如TCP和UDP),以滿足不同的需求。Socket通信廣泛應(yīng)用于客戶端-服務(wù)器應(yīng)用程序、分布式計(jì)算、實(shí)時(shí)數(shù)據(jù)傳輸?shù)阮I(lǐng)域。例如,Web服務(wù)器使用Socket通信與客戶端瀏覽器進(jìn)行通信,從而提供Web服務(wù)。9.2.1UDP類型
基于UDP協(xié)議的SocketAPI中,要分清楚DatagramSocket和DatagramPacket這兩個(gè)類。Datagram意為數(shù)據(jù)包,Packet意為包裹,這兩個(gè)類的關(guān)系相當(dāng)于DatagramPacket是發(fā)送接收的數(shù)據(jù)包包裹,DatagramSocket是發(fā)送接收數(shù)據(jù)包包裹的快遞員。表9-1(a)DatagramPacket類構(gòu)造方法構(gòu)造函數(shù)名稱說明DatagramPacket(byte[]buf,intlength)接收構(gòu)造函數(shù),用來接收長度為length的數(shù)據(jù)包DatagramPacket(byte[]buf,intlength,InetAddressaddress,intport)發(fā)送構(gòu)造函數(shù),構(gòu)造數(shù)據(jù)報(bào)包,用來將長度為
length的包發(fā)送到指定主機(jī)上的指定端口號(hào)包表9-1(b)DatagramPacket類成員方法成員方法說明InetAddressgetAddress()從接收或發(fā)送的數(shù)據(jù)報(bào)中獲取對(duì)端的IP地址intgetPort()intgetPort()
從接收或發(fā)送的數(shù)據(jù)報(bào)中獲取對(duì)端的端口號(hào)byte[]getData()獲取數(shù)據(jù)報(bào)中的數(shù)據(jù),數(shù)據(jù)存在字節(jié)數(shù)組中intgetLength()獲取數(shù)據(jù)報(bào)中的數(shù)據(jù)的實(shí)際長度InetAddressgetAddress()從接收或發(fā)送的數(shù)據(jù)報(bào)中獲取對(duì)端的IP地址DatagramPacket類表示數(shù)據(jù)報(bào)包,來實(shí)現(xiàn)無連接包投遞服務(wù)。每條報(bào)文僅根據(jù)該包中包含的信息從一臺(tái)機(jī)器路由到另一臺(tái)機(jī)器。從一臺(tái)機(jī)器發(fā)送到另一臺(tái)機(jī)器的多個(gè)包可能選擇不同的路由,也可能按不同的順序到達(dá),不保證包順利投遞。表9-2(a)DatagramSocket類構(gòu)造方法表9-2(b)DatagramSocket類成員和方法DatagramSocket類表示用來發(fā)送和接收數(shù)據(jù)報(bào)包的套接字。數(shù)據(jù)報(bào)套接字是包投遞服務(wù)的發(fā)送或接收點(diǎn)。每個(gè)在數(shù)據(jù)報(bào)套接字上發(fā)送或接收的包都是單獨(dú)編址和路由的。從一臺(tái)機(jī)器發(fā)送到另一臺(tái)機(jī)器的多個(gè)包可能選擇不同的路由,也可能按不同的順序到達(dá)。在DatagramSocket總是啟用UDP廣播發(fā)送。
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 新員工入職培訓(xùn)計(jì)劃與材料
- 鋼琴教師資格認(rèn)證培訓(xùn)內(nèi)容試題及答案
- 窒息急救實(shí)踐操作培訓(xùn)手冊(cè)
- 基于行業(yè)專業(yè)性教育培訓(xùn)承諾書7篇
- 員工培訓(xùn)需求評(píng)估及方案設(shè)計(jì)模板
- 新興行業(yè)培訓(xùn)亂象分析報(bào)告
- 健康檢查衛(wèi)生培訓(xùn)制度
- 跨境電商獨(dú)立站服務(wù)器應(yīng)急協(xié)議2025
- 初審電工考試題目及答案
- 策劃入職考試題目及答案
- 2026年無錫工藝職業(yè)技術(shù)學(xué)院單招綜合素質(zhì)考試題庫帶答案解析
- 【低空經(jīng)濟(jì)】無人機(jī)AI巡檢系統(tǒng)設(shè)計(jì)方案
- 2025年湖南省公務(wù)員錄用考試錄用考試《申論》標(biāo)準(zhǔn)試卷及答案
- 漢字的傳播教學(xué)課件
- 行政崗位面試問題庫及應(yīng)對(duì)策略
- 2025衢州市市級(jí)機(jī)關(guān)事業(yè)單位編外招聘77人筆試試題附答案解析
- 2025年中信金融業(yè)務(wù)面試題庫及答案
- 零碳園區(qū)數(shù)字化建筑設(shè)計(jì)方案
- 不動(dòng)產(chǎn)數(shù)據(jù)整合技術(shù)策略規(guī)劃方案
- GB/T 46607.1-2025塑料熱固性粉末模塑料(PMCs)試樣的制備第1部分:一般原理及多用途試樣的制備
- 紫金礦業(yè)招聘面試題及答案
評(píng)論
0/150
提交評(píng)論