在Android應(yīng)用程序開發(fā)中趁耗,啟動(dòng)一個(gè)Activity不一定是單項(xiàng)操作疆虚,從啟動(dòng)的Activity獲取數(shù)據(jù)是常見的場(chǎng)景苛败,最傳統(tǒng)的方式是通過Intent攜帶數(shù)據(jù),然后使用startActivityForResult方法來啟動(dòng)下一個(gè)Activity径簿,然后通過onActivityResult來接收返回的數(shù)據(jù)罢屈,代碼如下:
- 調(diào)用startActivityForResult方法啟動(dòng)
startActivityForResult(intent,1)
- 實(shí)現(xiàn)onActivityResult方法
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(requestCode == 1 && resultCode == Activity.RESULT_OK){
// 處理第二個(gè)頁面帶回的數(shù)據(jù)
}
}
隨著應(yīng)用的擴(kuò)展,onActivityResult回調(diào)方法各種嵌套篇亭、耦合嚴(yán)重缠捌、難以維護(hù)。
Google 可能也意識(shí)到onActivityResult的這些問題译蒂,在androidx.activity:activity:1.2.0-alpha02 和androidx.fragment:fragment:1.3.0-alpha02 中曼月,已經(jīng)廢棄了startActivityForResult和onActivityResult方法。Google推薦使用什么方式從Activity中更好的獲取數(shù)據(jù)呢柔昼?答案就是 Activity Results API
Activity Results API
Activity Results API 是 Google官方推薦的Activity哑芹、Fragment獲取數(shù)據(jù)的方式。Activity Results API 中兩個(gè)重要的組件:ActivityResultContract和ActivityResultLauncher捕透。
- ActivityResultContract: 協(xié)議聪姿,它定義了如何傳遞數(shù)據(jù)和如何處理返回的數(shù)據(jù)。ActivityResultContract是一個(gè)抽象類乙嘀,你需要繼承它來創(chuàng)建自己的協(xié)議末购,每個(gè) ActivityResultContract 都需要定義輸入和輸出類,如果您不需要任何輸入虎谢,可使用 Void(在 Kotlin 中招盲,使用 Void? 或 Unit)作為輸入類型。
- ActivityResultLauncher: 啟動(dòng)器嘉冒,調(diào)用ActivityResultLauncher的launch方法來啟動(dòng)頁面跳轉(zhuǎn)曹货,作用相當(dāng)于原來的startActivity()
使用
- 首先,在app下的build.gradle中加入依賴:
implementation 'androidx.activity:activity:1.2.0-beta01'
implementation 'androidx.fragment:fragment:1.3.0-beta01'
- 定義協(xié)議
新建一個(gè)Contract類讳推,繼承自ActivityResultContract<I,O>顶籽,其中,I是輸入的類型银觅,O是輸出的類型礼饱。需要實(shí)現(xiàn)2個(gè)方法,createIntent和parseResult,輸入類型I作為createIntent的參數(shù),輸出類型O作為parseResult方法的返回值镊绪,在下面的例子中匀伏,輸入輸出類型都是String:
class MyActivityResultContract: ActivityResultContract<String,String>(){
override fun createIntent(context: Context, input: String?): Intent {
return Intent(context,SecondActivity::class.java).apply {
putExtra("name",input)
}
}
override fun parseResult(resultCode: Int, intent: Intent?): String? {
val data = intent?.getStringExtra("result")
return if (resultCode == Activity.RESULT_OK && data != null) data
else null
}
}
如上代碼,我們?cè)赾reateIntent方法中創(chuàng)建了Intent蝴韭,并且攜帶了參數(shù)name,在parseResult方法中够颠,獲取了返回的數(shù)據(jù)result。
- 注冊(cè)協(xié)議榄鉴,獲取啟動(dòng)器-ActivityResultLauncher
注冊(cè)協(xié)議履磨,使用registerForActivityResult方法,該方法由ComponentActivity或者Fragment提供,接受2個(gè)參數(shù)庆尘,第一個(gè)參數(shù)就是我們定義的Contract協(xié)議,第二個(gè)參數(shù)是一個(gè)回調(diào)ActivityResultCallback<O>,其中O就是前面Contract的輸出類型驶忌。代碼如下:
private val myActivityLauncher = registerForActivityResult(MyActivityResultContract()){result ->
Toast.makeText(applicationContext,result,Toast.LENGTH_SHORT).show()
textView.text = "回傳數(shù)據(jù):$result"
}
如上代碼,注冊(cè)了MyActivityResultContract,registerForActivityResult方法的返回值是ActivityResultLauncher, 因此我們定義了一個(gè)myActivityLauncher,回調(diào)方法中如筛,result就是從上一個(gè)界面?zhèn)骰氐闹凳闾А_@里我們簡(jiǎn)單的用Toast顯示擦剑。
- 最后,調(diào)用啟動(dòng)器的launch方法開啟界面跳轉(zhuǎn)
MainActivity中添加一個(gè)Button,點(diǎn)擊Button時(shí)惠勒,調(diào)用launch方法跳轉(zhuǎn):
button.setOnClickListener {
// 開啟頁面跳轉(zhuǎn)
myActivityLauncher.launch("豌豆公主")
}
以上幾步,就實(shí)現(xiàn)了使用新的Activity Results API 來完成Activity之間的數(shù)據(jù)傳遞纠屋,并獲取Activity返回的數(shù)據(jù)
但是盾计,我們發(fā)現(xiàn)雖然確實(shí)減少了代碼耦合,但是使用并不簡(jiǎn)單啊族铆。
確實(shí)哭尝,不過還有......
預(yù)定義的Contract
新的Activity Results API使用起來好像有點(diǎn)麻煩,每次都得定義Contract。Google肯定考慮到了這個(gè)問題的逝淹,于是,Google 預(yù)定義了很多Contract,把你們能想到的使用場(chǎng)景基本上都想到了栅葡,它們都定義在類ActivityResultContracts中,有以下這些Contract:
StartActivityForResult()
RequestMultiplePermissions()
RequestPermission()
TakePicturePreview()
TakePicture()
TakeVideo()
PickContact()
CreateDocument()
OpenDocumentTree()
OpenMultipleDocuments()
OpenDocument()
GetMultipleContents()
GetContent()
StartActivityForResult: 通用的Contract,不做任何轉(zhuǎn)換妥畏,Intent作為輸入,ActivityResult作為輸出醉蚁,這也是最常用的一個(gè)協(xié)定。
RequestMultiplePermissions: 用于請(qǐng)求一組權(quán)限网棍。
RequestPermission: 用于請(qǐng)求單個(gè)權(quán)限。
TakePicturePreview: 調(diào)用MediaStore.ACTION_IMAGE_CAPTURE拍照滥玷,返回值為Bitmap圖片。
TakePicture: 調(diào)用MediaStore.ACTION_IMAGE_CAPTURE拍照蛋欣,并將圖片保存到給定的Uri地址如贷,返回true表示保存成功。
TakeVideo: 調(diào)用MediaStore.ACTION_VIDEO_CAPTURE 拍攝視頻杠袱,保存到給定的Uri地址,返回一張縮略圖楣富。
PickContact: 從通訊錄APP獲取聯(lián)系人。
GetContent: 提示用選擇一條內(nèi)容庄萎,返回一個(gè)通過塘安。ContentResolver#openInputStream(Uri)訪問原生數(shù)據(jù)的Uri地址(content://形式) 。默認(rèn)情況下耙旦,它增加了Intent#CATEGORY_OPENABLE, 返回可以表示流的內(nèi)容萝究。
CreateDocument: 提示用戶選擇一個(gè)文檔锉罐,返回一個(gè)(file:/http:/content:)開頭的Uri。
OpenMultipleDocuments: 提示用戶選擇文檔(可以選擇多個(gè))栽连,分別返回它們的Uri,以List的形式侨舆。
OpenDocumentTree: 提示用戶選擇一個(gè)目錄,并返回用戶選擇的作為一個(gè)Uri返回熔恢,應(yīng)用程序可以完全管理返回目錄中的文檔臭笆。
上面這些預(yù)定義的Contract中叙淌,除了StartActivityForResult和RequestMultiplePermissions之外鹰霍,基本都是處理的與其他APP交互茵乱,返回?cái)?shù)據(jù)的場(chǎng)景,比如督勺,拍照在验,選擇圖片堵未,選擇聯(lián)系人,打開文檔等等渗蟹。使用最多的就是StartActivityForResult和RequestMultiplePermissions了。