天天看點

深入了解ActivityResultContracts--替代startActivityForResult的新玩法如何使用ActivityResultContracts相關實作ComponentActivity相關實作還需要requestCode嗎Fragment相關實作

本文使用的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形式如下:

深入了解ActivityResultContracts--替代startActivityForResult的新玩法如何使用ActivityResultContracts相關實作ComponentActivity相關實作還需要requestCode嗎Fragment相關實作

還需要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
                }
            }
        });