本文使用的alpha版本API已經過期,想了解最新API的使用,請移步
深入了解Result API:ActivityResultContract的實作原理
ActivityResultContract
是
Activity 1.2.0-alpha02
和
Fragment 1.3.0-alpha02
中新追加的API,可以更加友善且typeSafe地處理
startActivityForResult
。
如何使用
AppCompatActivity和Fragment中可以通過
prepareCall()
建立
launcher
,然後調用
launch(intent)
進行startActivityForResult
//MainActivity.kt
val intent = Intent(this, SecondActivity::class.java)
val launcher: ActivityResultLauncher<Intent> = prepareCall(
ActivityResultContracts.StartActivityForResult()
) { activityResult: ActivityResult ->
Log.d("MainActivity", activityResult.toString())
// D/MainActivity: ActivityResult{resultCode=RESULT_OK, data=Intent { (has extras) }}
}
fab.setOnClickListener { view ->
launcher.launch(intent)
}
//SecondActivity.kt
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setResult(Activity.RESULT_OK, Intent().putExtra("my-data", "data"))
finish()
}
}
ActivityResultContracts相關實作
StartActivityForResult
prepareCall()
中傳入
ActivityResultContracts.StartActivityForResult
的執行個體,它繼承自
ActivityResultContract
// ActivityResultContracts.java
public class ActivityResultContracts {
private ActivityResultContracts() {}
...
public static class StartActivityForResult
extends ActivityResultContract<Intent, ActivityResult> {
@NonNull
@Override
public Intent createIntent(@NonNull Intent input) {
return input;
}
@NonNull
@Override
public ActivityResult parseResult(int resultCode, @Nullable Intent intent) {
return new ActivityResult(resultCode, intent);
}
}
當然替換為匿名類的寫法也OK,如下
val launcher: ActivityResultLauncher<Intent> = prepareCall(
// ** ↓ **
object : ActivityResultContract<Intent, ActivityResult>() {
override fun createIntent(input: Intent): Intent {
return input
}
override fun parseResult(resultCode: Int, intent: Intent?): ActivityResult {
return ActivityResult(resultCode, intent)
}
}
// ** ↑ **
) { activityResult: ActivityResult ->
Log.d("MainActivity", activityResult.toString())
// D/MainActivity: ActivityResult{resultCode=RESULT_OK, data=Intent { (has extras) }}
}
通過ActivityResultContract的兩個泛型參數限制startActivity的參數類型以及onActivityResult傳回的結果類型
ActivityResultRegistry
prepareCall
内會調用
ActivityResultRegistry.registerActivityResultCallback()
方法
@NonNull
@Override
public <I, O> ActivityResultLauncher<I> prepareCall(
@NonNull ActivityResultContract<I, O> contract,
@NonNull ActivityResultCallback<O> callback) {
return prepareCall(contract, mActivityResultRegistry, callback);
}
@NonNull
@Override
public <I, O> ActivityResultLauncher<I> prepareCall(
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultRegistry registry,
@NonNull final ActivityResultCallback<O> callback) {
return registry.registerActivityResultCallback(
"activity_rq#" + mNextLocalRequestCode.getAndIncrement(), this, contract, callback);
}
@NonNull
public ActivityResultRegistry getActivityResultRegistry() {
return mActivityResultRegistry;
}
當然Activity也可以脫離prepareCall直接調用activityResultRegistry,如下
// ** ↓ **
val launcher: ActivityResultLauncher<Intent> = activityResultRegistry
.registerActivityResultCallback(
"activity_rq#0", // 此數字在調用時保持Autoincrement
// ** ↑ **
object : ActivityResultContract<Intent, ActivityResult>() {
override fun createIntent(input: Intent): Intent {
return input
}
override fun parseResult(resultCode: Int, intent: Intent?): ActivityResult {
return ActivityResult(resultCode, intent)
}
}
) { activityResult: ActivityResult ->
Log.d("MainActivity", activityResult.toString())
// D/MainActivity: ActivityResult{resultCode=RESULT_OK, data=Intent { (has extras) }}
}
registerActivityResultCallback()
會向持有
ActivityResultRegistory
的HashMap執行put操作,記錄
ActivityResultContract
ComponentActivity相關實作
Activity中持有ActivityResultRegistry,ActivityResultRegistry通過HashMap管理ActivityResultContract和ActivityResultCallback。HashMap的Key形式如下:
還需要requestCode嗎
以往,onActivityResult需要通過
requestCode
來識别是哪個startActivityForResult的傳回,現在可以通過AutoIncrement來管理。而且當程序被殺時
onSaveInstanceState
會自動儲存requestCode和ActivityResultRegistry的key的pair對,當onActivityResult傳回rc時,可以通過對應關系找到key,然後找到ActivityResultCallback
//ActivityResultRegistry.java
private int registerKey(String key) {
Integer existing = mKeyToRc.get(key);
if (existing != null) {
return existing;
}
int rc = mNextRc.getAndIncrement();
bindRcKey(rc, key);
return rc;
}
Fragment相關實作
Fragment.prepareCall()的實作中,在ON_CREATE的時候,會調用getActivity().getActivityResultRegistry().registerActivityResultCallback
//Fragment.java
public <I, O> ActivityResultLauncher<I> prepareCall(
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultCallback<O> callback) {
...
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner lifecycleOwner,
@NonNull Lifecycle.Event event) {
if (Lifecycle.Event.ON_CREATE.equals(event)) {
ref.set(getActivity()
.getActivityResultRegistry()
// 這裡registerActivityResultCallback
.registerActivityResultCallback(
key, Fragment.this, contract, callback));
}
}
});
return new ActivityResultLauncher<I>() {
@Override
public void launch(I input) {
...
}
};
}
registerActivityResultCallback雖然将framgent執行個體注入到上級持有的HashMap,但是在ON_DESTROY的時候會進行對應的後處理,是以不必擔心造成記憶體洩漏
//ActivityResultRegistry.java
lifecycle.addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner lifecycleOwner,
@NonNull Lifecycle.Event event) {
if (Lifecycle.Event.ON_DESTROY.equals(event)) {
unregisterActivityResultCallback(key);//後處理避免leak
}
}
});