一般我们在请求权限或者处理Activity返回结果的时候都会使用Activity#onActivityResult()和Activity#onRequestPermissionResult()这俩个方法,如今谷歌已经提供了新的请求方法:https://developer.android.com/training/permissions/requesting?hl=zh-cn。
这篇文章是解决什么的?开始上代码,准备动手 首先解决这个registerForActivityResult()方法要在onStart()之前调用我想把每个处理结果回调的代码块直接写在ActivityResultLauncher.launch()方法中(1)中的问题:ActivityResultLauncher对象要用ActivityResultCaller#registerForActivityResult()得到(2)中的问题:注册必须在Activity#onStart()方法之前处理意外情况:Activity_A启动另一个activity_B,然后A意外被kill掉了,这时候从B返回了,A重新创建了,原先注册的地方不会执行回调
直接在Activity#onCreate()中拿到ActivityResultLauncher对象,并且将回调写好
open class baseActivityResultLauncher(
caller: ActivityResultCaller,
contract: ActivityResultContract
) {
private var launcher: ActivityResultLauncher
private lateinit var callback: ActivityResultCallback
init {
launcher = caller.registerForActivityResult(contract) { result ->
// ---- 1----
callback.onActivityResult(result)
}
}
// 在调用launch的时候把callback传入进去,然后结果回调会走 1 处代码
open fun launch(
input: I,
callback: ActivityResultCallback
) {
this.callback = callback
launcher.launch(input)
}
}
定义一些常用的ActivityResultLauncher,可以看系统的ActivityResultContracts,参考着抄一遍
class SinglePermissionLauncher(
caller: ActivityResultCaller
): baseActivityResultLauncher(
caller,
ActivityResultContracts.RequestPermission()
)
class MultiPermissionsLauncher(
caller: ActivityResultCaller
): baseActivityResultLauncher, MutableMap>(
caller,
ActivityResultContracts.RequestMultiplePermissions()
)
class StartActivityForResultLauncher(
caller: ActivityResultCaller
): baseActivityResultLauncher(
caller,
ActivityResultContracts.StartActivityForResult()
)
现在开始封装(抽出来一个baseActivity)
baseActivity {
private lateinit var singlePermissionLauncher: SinglePermissionLauncher
private lateinit var multiPermissionLauncher: MultiPermissionsLauncher
private lateinit var startForResultLauncher: StartActivityForResultLauncher
override fun onCreate(savedInstanceState: Bundle?) {
singlePermissionLauncher = SinglePermissionLauncher(this)
multiPermissionLauncher = MultiPermissionsLauncher(this)
startForResultLauncher = StartActivityForResultLauncher(this)
}
fun requestSinglePermission(
permission: String,
callback: (isGranted: Boolean, shouldShowRequestPermissionRationale: Boolean) -> Unit) {
singlePermissionLauncher.launch(permission) {
callback(it, shouldShowRequestPermissionRationale(permission))
}
}
fun requestMultiPermissions(
permissions: Array,
callback: (
isAllGranted: Boolean,
grantedList: List,
deniedList: List,
alwaysDeniedList: List
) -> Unit
) {
multiPermissionLauncher.launch(permissions) {
val grantedList = it.filterValues { it }.mapNotNull { it.key }
val leftoverList = (it - grantedList).map { it.key }
val deniedList = leftoverList.filter { shouldShowRequestPermissionRationale(it) }
callback(grantedList.size == it.size, grantedList,
deniedList, leftoverList - deniedList)
}
}
fun startActivityForResult(
intent: Intent,
callback: (requestCode: Int, data: Intent?) -> Unit
) {
startForResultLauncher.launch(intent) {
callback(it.resultCode, it.data)
}
}
}
使用
activity.requestMultiPermissions(permissions) { isAllGranted, _, _, _ ->
// 具体的回调代码逻辑
}
处理极端情况:原先发起请求的Activity被意外Kill了,我之前写的callback咋办?
核心处理方法:自定义一个保存注册回调的注册表,然后仿照系统的Activity处理流程把onStart()之前注册的逻辑屏蔽掉,让我们的注册可以在onResume()之后,并且将处理意外情况要保存的数据保存到onSaveInstanceState()中(androidx.activity.result.ActivityResultRegistry)
// ================= 屏蔽这里的代码 =============================
// ================= 屏蔽这里的代码 =============================
public abstract class LazyActivityResultRegistry {
private static final String KEY_COMPONENT_ACTIVITY_REGISTERED_RCS =
"KEY_COMPONENT_ACTIVITY_REGISTERED_RCS";
private static final String KEY_COMPONENT_ACTIVITY_REGISTERED_KEYS =
"KEY_COMPONENT_ACTIVITY_REGISTERED_KEYS";
private static final String KEY_COMPONENT_ACTIVITY_LAUNCHED_KEYS =
"KEY_COMPONENT_ACTIVITY_LAUNCHED_KEYS";
private static final String KEY_COMPONENT_ACTIVITY_PENDING_RESULTS =
"KEY_COMPONENT_ACTIVITY_PENDING_RESULT";
private static final String KEY_COMPONENT_ACTIVITY_RANDOM_OBJECT =
"KEY_COMPONENT_ACTIVITY_RANDOM_OBJECT";
private static final String LOG_TAG = "ActivityResultRegistry";
private static final int INITIAL_REQUEST_CODE_VALUE = 0x00010000;
private Random mRandom = new Random();
private final Map mRcToKey = new HashMap<>();
final Map mKeyToRc = new HashMap<>();
private final Map mKeyToLifecycleContainers = new HashMap<>();
ArrayList mLaunchedKeys = new ArrayList<>();
@SuppressWarnings("WeakerAccess")
final transient Map> mKeyToCallback = new HashMap<>();
@SuppressWarnings("WeakerAccess")
final Map mParsedPendingResults = new HashMap<>();
@SuppressWarnings("WeakerAccess")
final Bundle mPendingResults = new Bundle();
@MainThread
public abstract void onLaunch(
int requestCode,
@NonNull ActivityResultContract contract,
@SuppressLint("UnknownNullness") I input,
@Nullable ActivityOptionsCompat options);
@NonNull
public final ActivityResultLauncher register(
@NonNull final String key,
@NonNull final LifecycleOwner lifecycleOwner,
@NonNull final ActivityResultContract contract,
@NonNull final ActivityResultCallback callback) {
Lifecycle lifecycle = lifecycleOwner.getLifecycle();
// ================= 屏蔽这里的代码 =============================
// ================= 屏蔽这里的代码 =============================
final int requestCode = registerKey(key);
LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key);
if (lifecycleContainer == null) {
lifecycleContainer = new LifecycleContainer(lifecycle);
}
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);
}
}
};
lifecycleContainer.addObserver(observer);
mKeyToLifecycleContainers.put(key, lifecycleContainer);
return new ActivityResultLauncher() {
@Override
public void launch(I input, @Nullable ActivityOptionsCompat options) {
mLaunchedKeys.add(key);
Integer innerCode = mKeyToRc.get(key);
onLaunch((innerCode != null) ? innerCode : requestCode, contract, input, options);
}
@Override
public void unregister() {
LazyActivityResultRegistry.this.unregister(key);
}
@NonNull
@Override
public ActivityResultContract getContract() {
return contract;
}
};
}
@NonNull
public final ActivityResultLauncher register(
@NonNull final String key,
@NonNull final ActivityResultContract contract,
@NonNull final ActivityResultCallback callback) {
final int requestCode = registerKey(key);
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()));
}
return new ActivityResultLauncher() {
@Override
public void launch(I input, @Nullable ActivityOptionsCompat options) {
mLaunchedKeys.add(key);
Integer innerCode = mKeyToRc.get(key);
onLaunch((innerCode != null) ? innerCode : requestCode, contract, input, options);
}
@Override
public void unregister() {
LazyActivityResultRegistry.this.unregister(key);
}
@NonNull
@Override
public ActivityResultContract getContract() {
return contract;
}
};
}
@MainThread
final void unregister(@NonNull String key) {
if (!mLaunchedKeys.contains(key)) {
// only remove the key -> requestCode mapping if there isn't a launch in flight
Integer rc = mKeyToRc.remove(key);
if (rc != null) {
mRcToKey.remove(rc);
}
}
mKeyToCallback.remove(key);
if (mParsedPendingResults.containsKey(key)) {
Log.w(LOG_TAG, "Dropping pending result for request " + key + ": "
+ mParsedPendingResults.get(key));
mParsedPendingResults.remove(key);
}
if (mPendingResults.containsKey(key)) {
Log.w(LOG_TAG, "Dropping pending result for request " + key + ": "
+ mPendingResults.getParcelable(key));
mPendingResults.remove(key);
}
LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key);
if (lifecycleContainer != null) {
lifecycleContainer.clearObservers();
mKeyToLifecycleContainers.remove(key);
}
}
public final void onSaveInstanceState(@NonNull Bundle outState) {
outState.putIntegerArrayList(KEY_COMPONENT_ACTIVITY_REGISTERED_RCS,
new ArrayList<>(mKeyToRc.values()));
outState.putStringArrayList(KEY_COMPONENT_ACTIVITY_REGISTERED_KEYS,
new ArrayList<>(mKeyToRc.keySet()));
outState.putStringArrayList(KEY_COMPONENT_ACTIVITY_LAUNCHED_KEYS,
new ArrayList<>(mLaunchedKeys));
outState.putBundle(KEY_COMPONENT_ACTIVITY_PENDING_RESULTS,
(Bundle) mPendingResults.clone());
outState.putSerializable(KEY_COMPONENT_ACTIVITY_RANDOM_OBJECT, mRandom);
}
public final void onRestoreInstanceState(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) {
return;
}
ArrayList rcs =
savedInstanceState.getIntegerArrayList(KEY_COMPONENT_ACTIVITY_REGISTERED_RCS);
ArrayList keys =
savedInstanceState.getStringArrayList(KEY_COMPONENT_ACTIVITY_REGISTERED_KEYS);
if (keys == null || rcs == null) {
return;
}
mLaunchedKeys =
savedInstanceState.getStringArrayList(KEY_COMPONENT_ACTIVITY_LAUNCHED_KEYS);
mRandom = (Random) savedInstanceState.getSerializable(KEY_COMPONENT_ACTIVITY_RANDOM_OBJECT);
mPendingResults.putAll(
savedInstanceState.getBundle(KEY_COMPONENT_ACTIVITY_PENDING_RESULTS));
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
if (mKeyToRc.containsKey(key)) {
if (!mPendingResults.containsKey(key)) {
mRcToKey.remove(newRequestCode);
}
}
bindRcKey(rcs.get(i), keys.get(i));
}
}
@MainThread
public final boolean dispatchResult(int requestCode, int resultCode, @Nullable Intent data) {
String key = mRcToKey.get(requestCode);
if (key == null) {
return false;
}
mLaunchedKeys.remove(key);
doDispatch(key, resultCode, data, mKeyToCallback.get(key));
return true;
}
@MainThread
public final boolean dispatchResult(int requestCode,
@SuppressLint("UnknownNullness") O result) {
String key = mRcToKey.get(requestCode);
if (key == null) {
return false;
}
mLaunchedKeys.remove(key);
CallbackAndContract> callbackAndContract = mKeyToCallback.get(key);
if (callbackAndContract == null || callbackAndContract.mCallback == null) {
// Remove any pending result
mPendingResults.remove(key);
// And add these pre-parsed pending results in their place
mParsedPendingResults.put(key, result);
} else {
@SuppressWarnings("unchecked")
ActivityResultCallback callback =
(ActivityResultCallback) callbackAndContract.mCallback;
callback.onActivityResult(result);
}
return true;
}
private void doDispatch(String key, int resultCode, @Nullable Intent data,
@Nullable CallbackAndContract callbackAndContract) {
if (callbackAndContract != null && callbackAndContract.mCallback != null) {
ActivityResultCallback callback = callbackAndContract.mCallback;
ActivityResultContract, O> contract = callbackAndContract.mContract;
callback.onActivityResult(contract.parseResult(resultCode, data));
} else {
// Remove any parsed pending result
mParsedPendingResults.remove(key);
// And add these pending results in their place
mPendingResults.putParcelable(key, new ActivityResult(resultCode, data));
}
}
private int registerKey(String key) {
Integer existing = mKeyToRc.get(key);
if (existing != null) {
return existing;
}
int rc = generateRandomNumber();
bindRcKey(rc, key);
return rc;
}
private int generateRandomNumber() {
int number = mRandom.nextInt((Integer.MAX_VALUE - INITIAL_REQUEST_CODE_VALUE) + 1)
+ INITIAL_REQUEST_CODE_VALUE;
while (mRcToKey.containsKey(number)) {
number = mRandom.nextInt((Integer.MAX_VALUE - INITIAL_REQUEST_CODE_VALUE) + 1)
+ INITIAL_REQUEST_CODE_VALUE;
}
return number;
}
private void bindRcKey(int rc, String key) {
mRcToKey.put(rc, key);
mKeyToRc.put(key, rc);
}
private static class CallbackAndContract {
final ActivityResultCallback mCallback;
final ActivityResultContract, O> mContract;
CallbackAndContract(
ActivityResultCallback callback,
ActivityResultContract, O> contract) {
mCallback = callback;
mContract = contract;
}
}
private static class LifecycleContainer {
final Lifecycle mLifecycle;
private final ArrayList mObservers;
LifecycleContainer(@NonNull Lifecycle lifecycle) {
mLifecycle = lifecycle;
mObservers = new ArrayList<>();
}
void addObserver(@NonNull LifecycleEventObserver observer) {
mLifecycle.addObserver(observer);
mObservers.add(observer);
}
void clearObservers() {
for (LifecycleEventObserver observer: mObservers) {
mLifecycle.removeObserver(observer);
}
mObservers.clear();
}
}
}
抽象出来一个Activity
abstract class LazyRegisterActivity : FragmentActivity(), INoteActivity {
private val mActivityCallbacks = mutableListOf()
var isLoadComplete: Boolean by Delegates.observable(false) { _, old, new ->
if (new != old) {
pendingResultMap.forEach { (_, data) ->
noteActivityResultRegistry.dispatchResult(data.requestCode,
data.resultCode, data.data)
}
pendingResultMap.clear()
}
}
private val nextLocalRequestCode = AtomicInteger()
private data class ResultData(val requestCode: Int, val resultCode: Int, val data: Intent?)
private val pendingResultMap = mutableMapOf()
// copy from ComponentActivity
private val customActivityResultRegistry: LazyActivityResultRegistry = object : LazyActivityResultRegistry() {
override fun onLaunch(
requestCode: Int,
contract: ActivityResultContract,
input: I,
options: ActivityOptionsCompat?) {
val activity: ComponentActivity = this@LazyRegisterActivity
// Immediate result path
val synchronousResult = contract.getSynchronousResult(activity, input)
if (synchronousResult != null) {
Handler(Looper.getMainLooper()).post { dispatchResult(requestCode, synchronousResult.value) }
return
}
// Start activity path
val intent = contract.createIntent(activity, input)
var optionsBundle: Bundle? = null
// If there are any extras, we should defensively set the classLoader
if (intent.extras != null && intent.extras!!.classLoader == null) {
intent.setExtrasClassLoader(activity.classLoader)
}
if (intent.hasExtra(ActivityResultContracts.StartActivityForResult.EXTRA_ACTIVITY_OPTIONS_BUNDLE)) {
optionsBundle = intent.getBundleExtra(ActivityResultContracts.StartActivityForResult.EXTRA_ACTIVITY_OPTIONS_BUNDLE)
intent.removeExtra(ActivityResultContracts.StartActivityForResult.EXTRA_ACTIVITY_OPTIONS_BUNDLE)
} else if (options != null) {
optionsBundle = options.toBundle()
}
when {
ActivityResultContracts.RequestMultiplePermissions.ACTION_REQUEST_PERMISSIONS == intent.action -> {
// requestPermissions path
var permissions = intent.getStringArrayExtra(ActivityResultContracts.RequestMultiplePermissions.EXTRA_PERMISSIONS)
if (permissions == null) {
permissions = arrayOfNulls(0)
}
ActivityCompat.requestPermissions(activity, permissions, requestCode)
}
ActivityResultContracts.StartIntentSenderForResult.ACTION_INTENT_SENDER_REQUEST == intent.action -> {
val request: IntentSenderRequest = intent.getParcelableExtra(ActivityResultContracts.StartIntentSenderForResult.EXTRA_INTENT_SENDER_REQUEST)!!
try {
// startIntentSenderForResult path
ActivityCompat.startIntentSenderForResult(activity, request.intentSender,
requestCode, request.fillInIntent, request.flagsMask,
request.flagsValues, 0, optionsBundle)
} catch (e: SendIntentException) {
Handler(Looper.getMainLooper()).post {
dispatchResult(requestCode, RESULT_CANCELED,
Intent().setAction(ActivityResultContracts.StartIntentSenderForResult.ACTION_INTENT_SENDER_REQUEST)
.putExtra(ActivityResultContracts.StartIntentSenderForResult.EXTRA_SEND_INTENT_EXCEPTION, e))
}
}
}
else -> {
// startActivityForResult path
ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle)
}
}
}
}
companion object {
private const val ACTIVITY_RESULT_TAG = "note:activity-result"
}
override fun registerActivityCallbacks(callback: INoteActivity.ActivityCallbacks) {
synchronized(mActivityCallbacks) { mActivityCallbacks.add(callback) }
registerActivityLifecycleCallbacks(callback)
}
override fun unregisterActivityCallbacks(callback: INoteActivity.ActivityCallbacks) {
synchronized(mActivityCallbacks) { mActivityCallbacks.remove(callback) }
unregisterActivityLifecycleCallbacks(callback)
}
@CallSuper
override fun onMultiWindowModeChanged(isInMultiWindowMode: Boolean, newConfig: Configuration) {
super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig)
synchronized(mActivityCallbacks) {
mActivityCallbacks.forEach {
it.onMultiWindowModeChanged(this, isInMultiWindowMode, newConfig)
}
}
}
@CallSuper
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
synchronized(mActivityCallbacks) {
mActivityCallbacks.forEach {
it.onWindowFocusChanged(this, hasFocus)
}
}
}
@CallSuper
override fun onDestroy() {
super.onDestroy()
synchronized(mActivityCallbacks) { mActivityCallbacks.clear() }
}
@CallSuper
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (isLoadComplete) {
customActivityResultRegistry.dispatchResult(requestCode, resultCode, data)
} else {
pendingResultMap[requestCode] = ResultData(requestCode, resultCode, data)
}
}
override fun registerForLazyActivityResult(contract: ActivityResultContract,
callback: ActivityResultCallback): ActivityResultLauncher {
if (isLoadComplete) {
throw Throwable("can only register before load complete!")
}
return customActivityResultRegistry.register(
"activity_rq#" + nextLocalRequestCode.getAndIncrement(),
this, contract, callback)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
val data = Intent()
.putExtra(ActivityResultContracts.RequestMultiplePermissions.EXTRA_PERMISSIONS, permissions)
.putExtra(ActivityResultContracts.RequestMultiplePermissions.EXTRA_PERMISSION_GRANT_RESULTS, grantResults)
if (isLoadComplete) {
customActivityResultRegistry.dispatchResult(requestCode, RESULT_OK, data)
} else {
pendingResultMap[requestCode] = ResultData(requestCode, RESULT_OK, data)
}
}
init {
savedStateRegistry.registerSavedStateProvider(ACTIVITY_RESULT_TAG
) {
val outState = Bundle()
customActivityResultRegistry.onSaveInstanceState(outState)
outState
}
addonContextAvailableListener {
val savedInstanceState = savedStateRegistry
.consumeRestoredStateForKey(ACTIVITY_RESULT_TAG)
if (savedInstanceState != null) {
customActivityResultRegistry.onRestoreInstanceState(savedInstanceState)
}
}
}
}



