Android ActivityResultContracts 替代 startActivityForResult
Posted 匆忙拥挤repeat
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android ActivityResultContracts 替代 startActivityForResult相关的知识,希望对你有一定的参考价值。
文章目录
关联博客
Android ActivityResultContracts 请求权限(封装;含android 11权限变更)
I. Doc
implementation 'androidx.fragment:fragment-ktx:1.3.1'
implementation 'androidx.activity:activity-ktx:1.2.1'
implementation 'androidx.appcompat:appcompat:1.2.0' //主要是这个依赖,其会自动依赖前两个
II. Source Analysis
II.a. 启动器 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();
II.b. 协议 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
....
II.c. 注册表 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);
;
II.d. 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()
II.e. 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()).");
...
两个重载方法,一个有 ActivityResultRegistry,一个没有。没有的 使用 mHost 即其宿主 activity 的 ActivityResultRegistry 实例对象。
II.f. 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);
II.g. 自定义注册
-
没有ActivityResultRegistry的函数,默认使用当前宿主 Activity 的
mActivityResultRegistry
注册。 -
可以自定义传入ActivityResultRegistry 参数, 在非 Activity/Fragment中注册。
-
registerForActivityResult() ,在fragment中 需要在 created 之前
before they are created (i.e. initialization, onAttach(), or onCreate()).
在Activity中,需要在 started 之前
LifecycleOwners must call register before they are STARTED.
II.h. 整个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);
III. Extension Demo (kotlin)
III.a. 替换普通的stratActivityForResult
// Fragment 函数扩展
inline fun SupportFragment.requestForResult(crossinline block: (ActivityResult) -> Unit): ActivityResultLauncher<Intent>
return registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) result ->
block(result)
class DemoFragment: BaseExtendFragment()
// 定义 launcher 实例
private val mLanguageLauncher: ActivityResultLauncher<Intent> = requestForResult result ->
result.data?.getStringExtra(ConstantConfig.KEY_DATA)?.let
...
fun startLanguageSet()
mViewBind.tvLanguage.clickWithTrigger // 点击事件
// launch
mLanguageLauncher.launch(
Intent(requireContext(), ShellPointActivity::class.java).putExtra(
ShellActivity.FRAGMENT_KEY, LanguageSetupFragment::class.java.canonicalName
)
)
class LanguageSetupFragment
fun test()
...
// 正确结果标记 result code 为 RESULT_OK
_mActivity.setResult(RESULT_OK, Intent.apply ... )
III.b. 系统拍照
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以上是关于Android ActivityResultContracts 替代 startActivityForResult的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )