概述
早期我們會用 startActivityForResult() 配合 onActivityResult() 來取得其他 Activity 的結果,但是這種方式有很多的缺點,例如:耦合嚴重、要定義 RequestCode 常數、難以維護…等,所以 Google 推出了新的 Activity Result API 來改善這狀況。
Activity Result API 的主要元件
ActivityResultContract
這是一個抽象類別,我們必須繼承它來定義如何傳遞資料及處理回傳資料 (也就是輸入和輸出),但是 Google 已經幫我們定義好一些常用的類別,通常我們可以直接拿來使用,例如:
ActivityResultContracts.TakePicture 使用相機取得影像
從原始碼可以知道輸入型別為 Uri,輸出(回傳) 的型別為 Boolean
open class TakePicture : ActivityResultContract<Uri, Boolean>() {
@CallSuper
override fun createIntent(context: Context, input: Uri): Intent {
return Intent(MediaStore.ACTION_IMAGE_CAPTURE)
.putExtra(MediaStore.EXTRA_OUTPUT, input)
}
final override fun getSynchronousResult(
context: Context,
input: Uri
): SynchronousResult<Boolean>? = null
@Suppress("AutoBoxing")
final override fun parseResult(resultCode: Int, intent: Intent?): Boolean {
return resultCode == Activity.RESULT_OK
}
}
ActivityResultContracts.RequestPermission 運行時請求權限
從原始碼可以知道輸入型別為 String,輸出(回傳) 的型別為 Boolean
public static final class RequestPermission extends ActivityResultContract<String, Boolean> {
@NonNull
@Override
public Intent createIntent(@NonNull Context context, @NonNull String input) {
return RequestMultiplePermissions.createIntent(new String[] { input });
}
@NonNull
@Override
public Boolean parseResult(int resultCode, @Nullable Intent intent) {
if (intent == null || resultCode != Activity.RESULT_OK) return false;
int[] grantResults = intent.getIntArrayExtra(EXTRA_PERMISSION_GRANT_RESULTS);
if (grantResults == null || grantResults.length == 0) return false;
return grantResults[0] == PackageManager.PERMISSION_GRANTED;
}
@Override
public @Nullable SynchronousResult<Boolean> getSynchronousResult(
@NonNull Context context, @Nullable String input) {
if (input == null) {
return new SynchronousResult<>(false);
} else if (ContextCompat.checkSelfPermission(context, input)
== PackageManager.PERMISSION_GRANTED) {
return new SynchronousResult<>(true);
} else {
// proceed with permission request
return null;
}
}
}
ActivityResultLauncher
這是啟動器類別,其 launch() 方法可以用來啟動頁面的轉換,相當於本來的 startActivityForResult()。這裏有一點要注意,啟動器的初始化必須在 onCreate() 時處理,否則會丟出錯誤。
程式碼範例
以要求讀取聯絡人的權限為例
在 AndroidManifest.xml 中加入權限宣告
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.kirin.android.resultapidemo">
<uses-permission android:name="android.permission.READ_CONTACTS" />
...
MainActivity.kt 完整程式碼
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { result->
if(result) {
Toast.makeText(this, "授權成功", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "您必須同意授權才能繼續使用", Toast.LENGTH_SHORT).show()
}
}
permissionLauncher.launch(android.Manifest.permission.READ_CONTACTS)
}
}
執行 app 就可以看到請求權限的提示。
Comments