本文已同步发表于我的微信公众号,搜索
代码说
即可重视,欢迎与我沟通交流。
startActivityForResult()被标记为过期
一般咱们声明的Activity
都会承继自 AppCompatActivity
(引入androidx.appcompat:appcompat:xxx
库),并且AppCompatActivity -> FragmentActivity -> ComponentActivity (->表示承继)
。
当咱们需求跳转到另一个Activity
并需求拿到回来成果时,能够运用startActivityForResult()
来完成。可是跟着相应库版别的进步,忽然有一天,你会发现startActivityForResult()/onActivityResult()
被标记成过期办法了,如下:
点进办法内部:
//ComponentActivity.java
/**
* {@inheritDoc}
*
* @deprecated use
* {@link #registerForActivityResult(ActivityResultContract, ActivityResultCallback)}
* passing in a {@link StartActivityForResult} object for the {@link ActivityResultContract}.
*/
@Override
@Deprecated //被标记为过期
public void startActivityForResult(@SuppressLint("UnknownNullness") Intent intent,
int requestCode) {
super.startActivityForResult(intent, requestCode);
}
/**
* {@inheritDoc}
*
* @deprecated use
* {@link #registerForActivityResult(ActivityResultContract, ActivityResultCallback)}
* with the appropriate {@link ActivityResultContract} and handling the result in the
* {@link ActivityResultCallback#onActivityResult(Object) callback}.
*/
@CallSuper
@Override
@Deprecated //被标记为过期
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (!mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
ComponentActivity
是经过androidx.activity:activity
这个库引入的,经过查看该库的版别更新记录发现是在 androidx.activity:activity:1.2.0-alpha04 中将其抛弃的,所以在这个版别之后,当运用startActivityForResult()
时,都会看到过期提示并引荐运用registerForActivityResult()
代替。
registerForActivityResult代替计划
运用示例
//第一个Activity
class ResultApiActivity : AppCompatActivity() {
//1、注册回调函数
private var resultLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
//处理回来的成果
val code = result.resultCode //回来码 如:Activity.RESULT_OK、Activity.RESULT_CANCELED
val data = result.data
log("resultCode:$code,data:${intent?.getStringExtra(ResultApi2Activity.KEY_TRANSFER)}")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_result_api)
mTvResultApi.setOnClickListener {
//2、发动Intent跳转
resultLauncher.launch(Intent(this, ResultApi2Activity::class.java))
}
}
}
//第二个Activity
class ResultApi2Activity : AppCompatActivity() {
companion object{
const val KEY_TRANSFER = "key_transfer"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setResult(RESULT_OK, intent.putExtra(KEY_TRANSFER, "i'm value from ResultApi2Activity"))
}
}
//履行成果: resultCode:-1, data:i'm value from ResultApi2Activity
首要1处在 ComponentActivity
或 Fragment
中调用registerForActivityResult()
注册一个回调函数来处理其他页面回来的成果,registerForActivityResult()
承受 ActivityResultContract
和 ActivityResultCallback
作为参数:
-
ActivityResultContract<I,O>:界说生成成果所需的
输入类型(I)
、输出类型(O)
,可为Activity传值
、摄影
、恳求权限
等根本intent
操作供给默认协议,还能够创立自界说协议。 -
ActivityResultCallback< O>:本身是
interface
类,内部有一个名为onActivityResult(O result)
的办法,参数类型O
是ActivityResultContract<I,O>
中界说的输出类型O
方针。
registerForActivityResult()
办法履行后,回来ActivityResultLauncher
类型,用来发动另一个activity
,可是此刻还没有发动,需求调用ActivityResultLauncher#launch()
进行发动。
registerForActivityResult()
不只能够用在startActivityForResult()
的场景下,还能够用在其他场景下,如权限恳求:
private var permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
for ((string, isGrant) in permissions) {
log("$string 权限恳求状况:$isGrant")
/**
* 如履行成果:
* 1、android.permission.CAMERA 权限恳求状况:true
* 2、android.permission.WRITE_EXTERNAL_STORAGE 权限恳求状况:true
*/
}
}
//权限恳求
mTvPermission.setOnClickListener {
permissionLauncher.launch(
arrayOf( Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
)}
能够看到ActivityResultContract
参数传入的是ActivityResultContracts.RequestMultiplePermissions()
,所以传入不同的ActivityResultContract
就能够用于不同的场景中,那么都能够用于什么场景呢?
ActivityResultContracts 场景
上一节示例中,registerForActivityResult()
的第一个参数咱们传入的是ActivityResultContracts.StartActivityForResult()、RequestMultiplePermissions()
,其是ActivityResultContract
接口的详细完成类,咱们点进ActivityResultContracts
看看其他完成类:
ActivityResultContracts.xxx | launch()入参 | 成果回调 | 说明 |
---|---|---|---|
StartActivityForResult | Intent | ActivityResult(resultCode, intent) | 发动另一个Activity并接纳其回来成果 |
RequestPermission | String,如:Manifest.permission.CAMERA | Boolean | 恳求一个权限并接纳其授权成果 |
RequestMultiplePermissions | String[],如arrayOf(Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE) |
Map<String, Boolean> ,如:for ((permission, isGranted) in permissions) {if (isGranted) {// 权限已授权} else {// 权限未授权}}
|
恳求多个权限并接纳其授权成果 |
CaptureVideo | Uri |
Boolean | 用于获取视频,并将其保存到供给的Uri中 |
@RequiresApi(19) CreateDocument |
String ,如:launcher.launch("document.txt")
|
Uri |
创立一个新的文档并回来其URI
|
GetContent |
String ,如:launcher.launch("image/*")
|
Uri |
翻开文件选择器并获取所选文件的URI |
@RequiresApi(18) GetMultipleContents | String | List< Uri> | 翻开文件选择器并获取所选多个文件的URI |
@RequiresApi(19) OpenDocument | String[] | Uri | 翻开现有文档并回来其URI |
@RequiresApi(21) OpenDocumentTree | Uri | Uri | 翻开文档树并回来所选目录的URI |
@RequiresApi(19) OpenMultipleDocuments | String[] | List< Uri> | 翻开多个现有文档并回来它们的URI |
PickContact | Void | Uri | 翻开联系人应用程序并回来所选联系人的URI |
StartIntentSenderForResult | IntentSenderRequest | ActivityResult | 发动一个IntentSender并接纳其回来成果 |
TakePicture | Uri | Boolean | 发动相机应用程序并拍照相片 |
TakePicturePreview | Void | Bitmap | 发动相机应用程序并拍照预览相片 |
上述都是体系现已给咱们界说好的行为(API 31
),通常来说现已够咱们平时开发运用了,不过体系仍是供给了自界说ActivityResultContract
的才能。
自界说ActivityResultContract
自界说ActivityResultContract
能够让咱们依据自己的需求创立一个新的Activity
或Intent
操作,并运用registerForActivityResult()
办法将其与ActivityResultLauncher
方针绑定。以下是一个自界说ActivityResultContract
的示例:
/**
* 1、自界说ActivityResultContract
*/
class CustomContract : ActivityResultContract<Void, String>() {
companion object {
const val DEFAULT_VALUE = "default_value"
}
/**
* 创立Intent
* @param context Context
* @param input 当时类的第一个泛型参数
* @return
*/
override fun createIntent(context: Context, input: Void?): Intent {
return Intent(context, ResultApi2Activity::class.java)
}
/**
* 解析成果,类似于Activity#onActivityResult
* @param resultCode 回来码 [Activity.setResult] 的 resultCode
* @param intent [Activity.setResult] 的 intent
* @return
*/
override fun parseResult(resultCode: Int, intent: Intent?): String {
if (resultCode != Activity.RESULT_OK || intent == null) return DEFAULT_VALUE
return intent.getStringExtra(ResultApi2Activity.KEY_TRANSFER) ?: DEFAULT_VALUE
}
/**
* 直接获取同步成果,能够用于中间恳求阻拦判别运用,
* 如ActivityResultContracts.RequestPermission中的该办法重写
*/
override fun getSynchronousResult(context: Context, input: Void?): SynchronousResult<String>? {
return super.getSynchronousResult(context, input)
}
}
//2、Activity中运用自界说Contract
private val mLauncher =
registerForActivityResult(CustomContract()) { result -> log("return result:$result") }
//点击事件中触发launch
mTvCustomContract.setOnClickListener { mLauncher.launch(null)}
//3、方针Activity
class ResultApi2Activity : AppCompatActivity() {
companion object{
const val KEY_TRANSFER = "key_transfer"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setResult(RESULT_OK, intent.putExtra(KEY_TRANSFER, "i'm value from ResultApi2Activity"))
}
}
首要自界说ActivityResultContract
,重写了其间的createIntent()
和parseResult()
办法。
-
createIntent()
办法用于创立一个新的Intent
方针,因为输入参数为Void
,所以不需求传数据给方针Activity
。 -
parseResult()
办法用于解析方针Activity
回来的成果,并将其转换为输出类型String
。
接着,运用registerForActivityResult()
办法将CustomContract
与ActivityResultLauncher
方针绑定,并在需求发动该Activity
时运用该方针的launch()
发动它。
源码浅析
上述示例中首要涉及两个办法,分别为ComponentActivity#registerForActivityResult()
及ActivityResultLauncher#launch()
办法,下面要点看下这两个办法。
registerForActivityResult()
//ComponentActivity.java
private final ActivityResultRegistry mActivityResultRegistry = new ActivityResultRegistry(){...}
@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 <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);
}
能够看到终究调用了ActivityResultRegistry#register()
办法:
public final <I, O> ActivityResultLauncher<I> register(
@NonNull final String key,
@NonNull final LifecycleOwner lifecycleOwner,
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultCallback<O> callback) {
Lifecycle lifecycle = lifecycleOwner.getLifecycle();
//1、判别Lifecycle的状况有必要小于Lifecycle.State.STARTED,否则直接抛反常
if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
throw new IllegalStateException("LifecycleOwner " + lifecycleOwner + " is "
+ "attempting to register while current state is "
+ lifecycle.getCurrentState() + ". LifecycleOwners must call register before "
+ "they are STARTED.");
}
//2、生成唯一的 requestCode
final int requestCode = registerKey(key);
//3、获取或初始化生命周期容器LifecycleContainer
LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key);
if (lifecycleContainer == null) {
lifecycleContainer = new LifecycleContainer(lifecycle);
}
//4、注册观察者并将其添加到LifecycleContainer中
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)) {
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);
//5、初始化ActivityResultLauncher方针并回来
return new ActivityResultLauncher<I>() {
@Override
public void launch(I input, @Nullable ActivityOptionsCompat options) {
mLaunchedKeys.add(key);
onLaunch(requestCode, contract, input, options);
}
@Override
public void unregister() {
ActivityResultRegistry.this.unregister(key);
}
@NonNull
@Override
public ActivityResultContract<I, ?> getContract() {
return contract;
}
};
}
-
1处判别当时
Lifecycle
的状况有必要小于Lifecycle.State.STARTED
,否则直接抛反常。也就是说registerForActivityResult()
有必要在onStart()
之前调用。 - 2处注册键值,该办法会调用
registerKey()
办法生成一个唯一的requestCode
,并将key
和requestCode
绑定起来。 - 3处从
mKeyToLifecycleContainers
调集中获取与key
对应的LifecycleContainer
方针。假如不存在,则会创立一个新的LifecycleContainer
方针,并将其与lifecycle
绑定起来。 - 4处创立一个
LifecycleEventObserver
方针,并将其添加到3处的LifecycleContainer
方针中。该观察者会在LifecycleOwner
方针的生命周期发生变化时履行相应的操作,例如在STARTED
状况时将CallbackAndContract
方针添加到mKeyToCallback
调集中,在STOPPED
状况时将其从调集中移除,在DESTROYED
状况时调用unregister()
办法刊出ActivityResultLauncher
方针等。 - 5处回来一个
ActivityResultLauncher
方针,该方针包含了launch()
、unregister()
和getContract()
三个办法。其间,launch()
办法用于发动Activity
或Intent
操作,unregister()
办法用于刊出ActivityResultLauncher
方针,getContract()
办法用于获取与之绑定的ActivityResultContract
方针。
ActivityResultLauncher#launch()
上一节中registerForActivityResult()
回来了ActivityResultLauncher
方针,当发动跳转时,只需求调用launch()
办法即可:
public void launch(I input) {
launch(input, null);
}
public abstract void launch( I input, ActivityOptionsCompat options);
能够看到终究调用的 launch( I input, ActivityOptionsCompat options)
办法是一个abstract
笼统办法,其详细完成自然是上一节5处初始化ActivityResultLauncher
的当地,launch()
内部又调用了ActivityResultRegistry#onLaunch(requestCode, contract, input, options)
办法,继续找ActivityResultRegistry
初始化的当地,能够找到在ComponentActivity
中初始化了ActivityResultRegistry
(这儿看的是androidx.activity:activity:1.3.1
版别):
this.mActivityResultRegistry = new ActivityResultRegistry() {
public <I, O> void onLaunch(final int requestCode, @NonNull ActivityResultContract<I, O> contract, I input, @Nullable ActivityOptionsCompat options) {
ComponentActivity activity = ComponentActivity.this;
final ActivityResultContract.SynchronousResult<O> synchronousResult = contract.getSynchronousResult(activity, input);
//1、同步查看成果,假如不为空,直接回来contract.getSynchronousResult中的数据
if (synchronousResult != null) {
(new Handler(Looper.getMainLooper())).post(new Runnable() {
public void run() {
dispatchResult(requestCode, synchronousResult.getValue());
}
});
} else {
//2、创立Intent方针
Intent intent = contract.createIntent(activity, input);
Bundle optionsBundle = null;
if (intent.getExtras() != null && intent.getExtras().getClassLoader() == null) {
intent.setExtrasClassLoader(activity.getClassLoader());
}
if (intent.hasExtra("androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE")) {
optionsBundle = intent.getBundleExtra("androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE");
intent.removeExtra("androidx.activity.result.contract.extra.ACTIVITY_OPTIONS_BUNDLE");
} else if (options != null) {
optionsBundle = options.toBundle();
}
if ("androidx.activity.result.contract.action.REQUEST_PERMISSIONS".equals(intent.getAction())) {
String[] permissions = intent.getStringArrayExtra("androidx.activity.result.contract.extra.PERMISSIONS");
if (permissions == null) {
permissions = new String[0];
}
ActivityCompat.requestPermissions(activity, permissions, requestCode);
} else if ("androidx.activity.result.contract.action.INTENT_SENDER_REQUEST".equals(intent.getAction())) {
IntentSenderRequest request = (IntentSenderRequest)intent.getParcelableExtra("androidx.activity.result.contract.extra.INTENT_SENDER_REQUEST");
try {
ActivityCompat.startIntentSenderForResult(activity, request.getIntentSender(), requestCode, request.getFillInIntent(), request.getFlagsMask(), request.getFlagsValues(), 0, optionsBundle);
} catch (final IntentSender.SendIntentException var11) {
(new Handler(Looper.getMainLooper())).post(new Runnable() {
public void run() {
dispatchResult(requestCode, 0, (new Intent()).setAction("androidx.activity.result.contract.action.INTENT_SENDER_REQUEST").putExtra("androidx.activity.result.contract.extra.SEND_INTENT_EXCEPTION", var11));
}
});
}
} else {
ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle);
}
}
}
};
- 1处用
ActivityResultContract
的getSynchronousResult()
办法获取同步成果。假如存在同步成果,则会将其回来值经过dispatchResult()
办法分发出去。 - 假如1处不存在同步成果,那么就会走2处逻辑,调用
ActivityResultContract
的createIntent()
办法创立一个新的Intent
方针,并依据需求设置Activity
或Intent
操作的选项。 - 查看
Intent
方针的Action
属性是否为"androidx.activity.result.contract.action.REQUEST_PERMISSIONS"
或"androidx.activity.result.contract.action.INTENT_SENDER_REQUEST"
。假如是,则会分别调用ActivityCompat.requestPermissions()
办法恳求权限或ActivityCompat.startIntentSenderForResult()
办法发动相应的操作;假如都不是,则默认调用ActivityCompat.startActivityForResult()
办法发动Activity
或Intent
操作。
能够看到终究仍是调用的startActivityForResult()/requestPermissions()
这些办法发起了恳求,那么恳求完的成果也会回来到onActivityResult()/onRequestPermissionsResult()
中:
//ComponentActivity.java
//1、startActivityForResult()回来成果
@Deprecated
@CallSuper
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (!this.mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
//2、权限恳求的成果
@Deprecated
@CallSuper
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (!this.mActivityResultRegistry.dispatchResult(requestCode, -1, (new Intent()).putExtra("androidx.activity.result.contract.extra.PERMISSIONS", permissions).putExtra("androidx.activity.result.contract.extra.PERMISSION_GRANT_RESULTS", grantResults)) && VERSION.SDK_INT >= 23) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
//分发ActivityResultLauncher方针的回来成果
@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;
}
//3、处理回来成果
private <O> void doDispatch(String key, int resultCode, @Nullable Intent data,
@Nullable CallbackAndContract<O> callbackAndContract) {
if (callbackAndContract != null && callbackAndContract.mCallback != null) {
ActivityResultCallback<O> 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));
}
}
首要来看3处的doDispatch()
办法:
-
查看
callbackAndContract
方针是否为空,假如不为空,则会调用ActivityResultCallback
方针的onActivityResult()
办法处理回来成果。到这儿履行成果就会在registerForActivityResult()
中的第2个参数中收到成果。 - 假如上述不成立,会将回来成果存储到
mPendingResults
调集中,以便稍后运用。
总结
- 个人认为
startActivityForResult()/onActivityResult()、onRequestPermissionsResult()
被标记成过期并不是因为有什么性能问题或许bug
,而是经过registerForActivityResult()
一致进行收口,为什么这么说呢,因为registerForActivityResult()
内部终究仍是调用了ComponentActivity
的startActivityForResult()/onActivityResult()、onRequestPermissionsResult()
办法,只不过是对其进行了一致的封装罢了,简化了运用。 - 运用
registerForActivityResult()
,能够经过一个lambda
表达式来处理回来成果,避免了繁琐的requestCode
和onActivityResult
的处理逻辑。此外,registerForActivityResult
还支持多个恳求码和多个回来成果,使得代码愈加明晰和易于维护。
材料
【1】android官网:Activity Result API
欢迎扫描下方二维码
或搜索微信公众号 代码说
, 重视我的微信公众号查看最新文章~