Android ActivityResultContracts 替代 startActivityForResult
Posted 匆忙拥挤repeat
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android ActivityResultContracts 替代 startActivityForResult相关的知识,希望对你有一定的参考价值。
文章目录
Doc
implementation 'androidx.fragment:fragment-ktx:1.3.1'
implementation 'androidx.activity:activity-ktx:1.2.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
源码
启动器 ActivityResultLauncher
public abstract class ActivityResultLauncher<I> {
/**
* Executes an {@link ActivityResultContract}.
*
* <p>This method throws {@link android.content.ActivityNotFoundException}
* if there was no Activity found to run the given Intent.
* @param input the input required to execute an {@link ActivityResultContract}.
*
* @throws android.content.ActivityNotFoundException
*/
public void launch(@SuppressLint("UnknownNullness") I input) {
launch(input, null);
}
/**
* Executes an {@link ActivityResultContract}.
*
* <p>This method throws {@link android.content.ActivityNotFoundException}
* if there was no Activity found to run the given Intent.
*
* @param input the input required to execute an {@link ActivityResultContract}.
* @param options Additional options for how the Activity should be started.
*
* @throws android.content.ActivityNotFoundException
*/
public abstract void launch(@SuppressLint("UnknownNullness") I input,
@Nullable ActivityOptionsCompat options);
/**
* Unregisters this launcher, releasing the underlying result callback, and any references
* captured within it.
*
* You should call this if the registry may live longer than the callback registered for this
* launcher.
*/
@MainThread
public abstract void unregister();
/**
* Get the {@link ActivityResultContract} that was used to create this launcher.
*
* @return the contract that was used to create this launcher
*/
@NonNull
public abstract ActivityResultContract<I, ?> getContract();
}
协议 ActivityResultContract
public abstract class ActivityResultContract<I, O> {
/** Create an intent that can be used for {@link Activity#startActivityForResult} */
public abstract @NonNull Intent createIntent(@NonNull Context context,
@SuppressLint("UnknownNullness") I input);
/** Convert result obtained from {@link Activity#onActivityResult} to O */
@SuppressLint("UnknownNullness")
public abstract O parseResult(int resultCode, @Nullable Intent intent);
/**
* An optional method you can implement that can be used to potentially provide a result in
* lieu of starting an activity.
*
* @return the result wrapped in a {@link SynchronousResult} or {@code null} if the call
* should proceed to start an activity.
*/
public @Nullable SynchronousResult<O> getSynchronousResult(
@NonNull Context context,
@SuppressLint("UnknownNullness") I input) {
return null;
}
/**
* The wrapper for a result provided in {@link #getSynchronousResult}
*
* @param <T> type of the result
*/
public static final class SynchronousResult<T> {
private final @SuppressLint("UnknownNullness") T mValue;
/**
* Create a new result wrapper
*
* @param value the result value
*/
public SynchronousResult(@SuppressLint("UnknownNullness") T value) {
this.mValue = value;
}
/**
* @return the result value
*/
public @SuppressLint("UnknownNullness") T getValue() {
return mValue;
}
}
}
ActivityResultContracts
提供了一些静态内部类 声明的 协议。
GetContent
PickContact
RequestPermission
RequestMultiplePermissions
TakePicture
TakePicturePreview
TakeVideo
....
注册表 ActivityResultRegistry
粗略分析一下,内部会在每次注册register() 时,自动生成一个 requestCode ;以Map关联 requestCode 和 注册时传入的string key。此外,还有 key-requestCode 、key-callback、key-LifecycleContainer、key-reuslt 的Map。
注册方法有两个重载,一个基于 LifecycleOwner,并向其注册LifecycleEventObserver。一个不基于。
部分源码
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));
if (mParsedPendingResults.containsKey(key)) {
@SuppressWarnings("unchecked")
final O parsedPendingResult = (O) mParsedPendingResults.get(key);
mParsedPendingResults.remove(key);
callback.onActivityResult(parsedPendingResult);
}
final ActivityResult pendingResult = mPendingResults.getParcelable(key);
if (pendingResult != null) {
mPendingResults.remove(key);
callback.onActivityResult(contract.parseResult(
pendingResult.getResultCode(),
pendingResult.getData()));
}
} else if (Lifecycle.Event.ON_STOP.equals(event)) {
mKeyToCallback.remove(key);
} else if (Lifecycle.Event.ON_DESTROY.equals(event)) {
unregister(key);
}
}
};
ActivityResultContract.SynchronousResult 作用
在 ComponentActivity 中 注册器初始化代码:
private ActivityResultRegistry mActivityResultRegistry = new ActivityResultRegistry() {
@Override
public <I, O> void onLaunch(
final int requestCode,
@NonNull ActivityResultContract<I, O> contract,
I input,
@Nullable ActivityOptionsCompat options) {
ComponentActivity activity = ComponentActivity.this;
// Immediate result path
final ActivityResultContract.SynchronousResult<O> synchronousResult =
contract.getSynchronousResult(activity, input);
if (synchronousResult != null) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
dispatchResult(requestCode, synchronousResult.getValue());
}
});
return;
}
...
}
由此分析,若contract#getSynchronousResult()返回 非空的SynchronousResult 对象,则直接 dispatchResult,不再调用 contract#parseResult()
Fragment 注册协议与回调返回 launcher
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);
}
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultRegistry registry,
@NonNull final ActivityResultCallback<O> callback) {
return prepareCallInternal(contract, new Function<Void, ActivityResultRegistry>() {
@Override
public ActivityResultRegistry apply(Void input) {
return registry;
}
}, callback);
}
private <I, O> ActivityResultLauncher<I> prepareCallInternal(
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final Function<Void, ActivityResultRegistry> registryProvider,
@NonNull final ActivityResultCallback<O> callback) {
// Throw if attempting to register after the Fragment is CREATED.
if (mState > CREATED) {
throw new IllegalStateException("Fragment " + this + " is attempting to "
+ "registerForActivityResult after being created. Fragments must call "
+ "registerForActivityResult() before they are created (i.e. initialization, "
+ "onAttach(), or onCreate()).");
}
...
}
Activity 注册协议与回调返回 launcher
与Fragment一样,也有类似的方法。
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
ContextAware,
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner,
ActivityResultRegistryOwner,
ActivityResultCaller {
@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);
}
}
自定义注册
-
没有ActivityResultRegistry的函数,默认使用当前
mActivityResultRegistry
注册。 -
可以自定义传入ActivityResultRegistry 参数, 在非 Activity/Fragment中注册。
-
registerForActivityResult() ,在fragment中 需要在 created 之前,
before they are created (i.e. initialization, onAttach(), or onCreate()).
在Activity中,需要
LifecycleOwners must call register before they are STARTED.
整个launcher的启动入口
不难猜,肯定还是在以前的 onActivityResult里面。
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
ContextAware,
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner,
ActivityResultRegistryOwner,
ActivityResultCaller {
@CallSuper
@Override
@Deprecated
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (!mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
}
public abstract class FragmentManager implements FragmentResultOwner {
void launchStartActivityForResult(@NonNull Fragment f,
@SuppressLint("UnknownNullness") Intent intent,
int requestCode, @Nullable Bundle options) {
if (mStartActivityForResult != null) {
LaunchedFragmentInfo info = new LaunchedFragmentInfo(f.mWho, requestCode);
mLaunchedFragments.addLast(info);
if (intent != null && options != null) {
intent.putExtra(EXTRA_ACTIVITY_OPTIONS_BUNDLE, options);
}
mStartActivityForResult.launch(intent);
} else {
mHost.onStartActivityFromFragment(f, intent, requestCode, options);
}
}
}
Demo
替换普通的stratActivityForResult
inline fun SupportFragment.requestForResult(crossinline block: (ActivityResult) -> Unit): ActivityResultLauncher<Intent> {
return registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
block(result)
}
}
class DemoFragment: BaseExtendFragment() {
private val mLanguageLauncher: ActivityResultLauncher<Intent> = requestForResult { result ->
result.data?.getStringExtra(ConstantConfig.KEY_DATA)?.let {
...
}
}
fun startLanguageSet() {
mViewBind.tvLanguage.clickWithTrigger {
mLanguageLauncher.launch(
Intent(requireContext(), ShellPointActivity::class.java).putExtra(
ShellActivity.FRAGMENT_KEY, LanguageSetupFragment::class.java.canonicalName
)
)
}
}
}
class LanguageSetupFragment {
fun test() {
...
_mActivity.setResult(RESULT_OK, Intent.apply { ... })
}
}
系统拍照
inline fun SupportFragment.requestTakePhoto(crossinline block: (Boolean) -> Unit): ActivityResultLauncher<Uri> {
return registerForActivityResult(
ActivityResultContracts.TakePicture()
) { result ->
block(result)
}
}
class DemoFragment: BaseExtendFragment() {
val mTakeLauncher = requestTakePhoto {
if (it) {
userToast("take photo and save")
}
}
@NeedsPermission(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
fun takePhoto() {
mViewBind.tvHelp.clickWithTrigger {
val con = contentValuesOf()
con.put(MediaStore.Images.Media.DISPLAY_NAME, "stone-test.jpg")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
con.put(MediaStore.Images.Media.RELATIVE_PATH, "DCIM/Pictures")
} else {
con.put(MediaStore.Images.Media.DATA, File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),"test.jpg").absolutePath)
}
con.put(MediaStore.Images.Media.MIME_TYPE, "image/JPEG")
val uri = requireActivity().contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, con)
mTakeLauncher.launch(uri)
}
}
}
选择图片,打开文档目录
inline fun SupportFragment.requestDocument(crossinline block: (Boolean) -> Unit): ActivityResultLauncher<Uri> {
return registerForActivityResult(
ActivityResultContracts.TakePicture()
) {以上是关于Android ActivityResultContracts 替代 startActivityForResult的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )