AndroidX从Activity:1.2.0-alpha02
和 Fragment:1.3.0-alpha02
起追加了Result API,使用ActivityResultContract
替代startActivityForResult,更加高效且typesafe的处理跨Activity通信。目前Result API已升级到rc版,相对于alpha版API上有所变化,本文内容基于1.2.0-rc-01
如何使用
在AppCompatActivity或者Fragment中通过registerForActivityResult()
创建ActivityResultLauncher
,然后调用launch(...)
替代startActivityForResult启动目标Activity。
//MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fab.setOnClickListener { view ->
val intent = Intent(this, MainActivity::class.java)
launcher.launch(intent)
}
}
// Create ActivityResultLauncher
private val launcher : ActivityResultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
activityResult ->
// activityResult will return this as ActivityResult
Log.d("MainActivity", activityResult.toString())
// D/MainActivity: ActivityResult{resultCode=RESULT_OK, data=Intent { (has extras) }}
}
}
//SecondActivity.kt
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setResult(Activity.RESULT_OK, Intent().putExtra("my-data", "data"))
finish()
}
}
源码分析
Result API能以近似回调的形式替代startActivityResult,核心是ActivityResultContract
这个协议类。ActivityResultContracts
中预置了多个ActivityResultContract的实现,StartActivityForResul
便是其中之一。
StartActivityForResult
//ActivityResultContracts.java
public static final class StartActivityForResult
extends ActivityResultContract<Intent, ActivityResult> {
@NonNull
@Override
public Intent createIntent(@NonNull Context context, @NonNull Intent input) {
return input;
}
@NonNull
@Override
public ActivityResult parseResult(
int resultCode, @Nullable Intent intent) {
return new ActivityResult(resultCode, intent);
}
}
ActivityResultContract接受两个泛型,分别表示ActivityResultLauncher的启动参数类型以及onActivityResult返回的结果类型。对于StartActivityForResult来说,通过Intent
启动,等待目标Activity返回ActivityResult
。
自定义ActivityResultContract
当然,你可以自定义ActivityResultContract,接受任意类型的启动参数。然后在createIntent
中构造Intent,例如:
//PostActivityContract.kt
class PostActivityContract : ActivityResultContract<Int, String?>() {
override fun createIntent(context: Context, input: Int): Intent {
return Intent(context, PostActivity::class.java).apply {
putExtra("PostActivity.ID", input)
}
}
override fun parseResult(resultCode: Int, intent: Intent?): String? {
val data = intent?.getStringExtra(PostActivity.TITLE)
return if (resultCode == Activity.RESULT_OK && data != null) data
else null
}
}
// MainActivity.kt
launcher.launch(100089) //launch with Int
private val launcher =
registerForActivityResult(PostActivityContract()) { result ->
// Result will return this as string?
if (result != null) toast("Result : $result")
else toast("No Result")
}
ActivityResultRegistry
registerForActivityResult内调用ActivityResultRegistry
的register()
方法
//ComponentActivity.java
@NonNull
@Override
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultRegistry registry,
@NonNull final ActivityResultCallback<O> callback) {
return registry.register(
"activity_rq#" + mNextLocalRequestCode.getAndIncrement(), this, contract, callback);
}
@NonNull
@Override
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull ActivityResultContract<I, O> contract,
@NonNull ActivityResultCallback<O> callback) {
return registerForActivityResult(contract, mActivityResultRegistry, callback);
}
@NonNull
@Override
public final ActivityResultRegistry getActivityResultRegistry() {
return mActivityResultRegistry;
}
register()内部会将ActivityResultContract存入一个HashMap中。你也可以不使用mActivityResultRegistry,使用自定义的ActivityResultRegistry,例如
val launcher: ActivityResultLauncher<Intent> = activityResultRegistry
.register(ActivityResultContracts.StartActivityForResult(),
"activity_rq#0",
this, //LifecyleOwner: ComponentActivity
) { activityResult: ActivityResult ->
Log.d("MainActivity", activityResult.toString())
// D/MainActivity: ActivityResult{resultCode=RESULT_OK, data=Intent { (has extras) }}
}
register接受一个LifecycleOwner(ComponentActivity自身),在合适的生命周期将回调存入/移除Map,保证回调响应的时机正确。
LifecycleEventObserver observer = new LifecycleEventObserver() {
@Override
public void onStateChanged(
@NonNull LifecycleOwner lifecycleOwner,
@NonNull Lifecycle.Event event) {
if (Lifecycle.Event.ON_START.equals(event)) {
mKeyToCallback.put(key, new CallbackAndContract<>(callback, contract));
...
} else if (Lifecycle.Event.ON_STOP.equals(event)) {
mKeyToCallback.remove(key);
} else if (Lifecycle.Event.ON_DESTROY.equals(event)) {
unregister(key);
}
}
};
ComponentActivity相关实现
ComponentActivity比较简单,持有ActivityResultRegistry,并提供registerForActivityResult方法。ActivityResultRegistry中通过HashMap存储回调,HashMap的key是自增的。
"activity_rq#" + mNextLocalRequestCode.getAndIncrement()
还需要requestCode吗
以往onActivityResult需要通过requestCode
来识别是哪个startActivityForResult的返回,现在可以通过AutoIncrement
来管理。而且当进程被杀时onSaveInstanceState
会自动保存requestCode和ActivityResultRegistry的key的pair对,当onActivityResult返回requestCode时,可以通过对应关系找到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.registerForActivityResult()内部,在onAttach
的时候,会调用getActivity().getActivityResultRegistry()进行register
//Fragment.java
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultCallback<O> callback) {
return prepareCallInternal(contract, new Function<Void, ActivityResultRegistry>() {
@Override
public ActivityResultRegistry apply(Void input) {
if (mHost instanceof ActivityResultRegistryOwner) {
return ((ActivityResultRegistryOwner) mHost).getActivityResultRegistry();
}
return requireActivity().getActivityResultRegistry();
}
}, callback);
}
@NonNull
private <I, O> ActivityResultLauncher<I> prepareCallInternal(
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final Function<Void, ActivityResultRegistry> registryProvider,
@NonNull final ActivityResultCallback<O> callback) {
...
registerOnPreAttachListener(new OnPreAttachedListener() {
@Override
void onPreAttached() {
final String key = generateActivityResultKey();
ActivityResultRegistry registry = registryProvider.apply(null);
ref.set(registry.register(key, Fragment.this, contract, callback));
}
});
return new ActivityResultLauncher<I>() {
@Override
public void launch(I input) {
...
}
};
}
private void registerOnPreAttachListener(@NonNull final OnPreAttachedListener callback) {
//If we are already attached, we can register immediately
if (mState >= ATTACHED) {
callback.onPreAttached();
} else {
// else we need to wait until we are attached
mOnPreAttachedListeners.add(callback);
}
}
register虽然将framgent实例注入到上级持有的HashMap,但是此时使用的LifecycleOwner是Fragment自身,前文分析可知,在其ON_DESTROY
的时候会进行对应的后处理,因此不必担心造成内存泄漏。
更多预置ActivityResultContract
除了StartActivityFroResult以外,还有很多预知的ActivityResultContract:
例如,通过GetContent打开文件管理器选择图片并返回URI:
class MainActivity : AppCompatActivity() {
private val launcher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
Log.d("MainActivity", "uri: $uri")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button_get_content.setOnClickListener {
launcher.launch("image/*")
}
}
}
通过RequestPermission
和RequestMultiplePermission
进行权限申请也将变得更加简单:
request_permission.setOnClickListener {
requestPermission.launch(permission.BLUETOOTH)
}
request_multiple_permission.setOnClickListener {
requestMultiplePermissions.launch(
arrayOf(
permission.BLUETOOTH,
permission.NFC,
permission.ACCESS_FINE_LOCATION
)
)
}
// Request permission contract
private val requestPermission =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
// Do something if permission granted
if (isGranted) toast("Permission is granted")
else toast("Permission is denied")
}
// Request multiple permissions contract
private val requestMultiplePermissions =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions : Map<String, Boolean> ->
// Do something if some permissions granted or denied
permissions.entries.forEach {
// Do checking here
}
}
最后
通过对比可以清楚看到Result API好处:通过ActivityResultContract避免了对requestCode、onActivityResult的感知,减少了模板代码。更重要的是多种预置ActivityResultContract,极大降低了系统API的通信成本,为日常开发提供了便利,是时候跟startActivityForResult说再见了!