本文已同步发表于我的微信公众号,搜索 代码说 即可重视,欢迎与我沟通交流。

startActivityForResult()被标记为过期

一般咱们声明的Activity都会承继自 AppCompatActivity (引入androidx.appcompat:appcompat:xxx 库),并且AppCompatActivity -> FragmentActivity -> ComponentActivity (->表示承继)

当咱们需求跳转到另一个Activity并需求拿到回来成果时,能够运用startActivityForResult()来完成。可是跟着相应库版别的进步,忽然有一天,你会发现startActivityForResult()/onActivityResult()被标记成过期办法了,如下:

startActivityForResult被标记为废弃?Activity Result API闪亮登场!
点进办法内部:

//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()代替。

startActivityForResult被标记为废弃?Activity Result API闪亮登场!

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处在 ComponentActivityFragment 中调用registerForActivityResult()注册一个回调函数来处理其他页面回来的成果,registerForActivityResult() 承受 ActivityResultContractActivityResultCallback 作为参数:

  • ActivityResultContract<I,O>:界说生成成果所需的输入类型(I)输出类型(O),可为Activity传值摄影恳求权限等根本 intent 操作供给默认协议,还能够创立自界说协议。
  • ActivityResultCallback< O>:本身是interface类,内部有一个名为onActivityResult(O result)的办法,参数类型OActivityResultContract<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能够让咱们依据自己的需求创立一个新的ActivityIntent操作,并运用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()办法将CustomContractActivityResultLauncher方针绑定,并在需求发动该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,并将keyrequestCode绑定起来。
  • 3处从mKeyToLifecycleContainers调集中获取与key对应的LifecycleContainer方针。假如不存在,则会创立一个新的LifecycleContainer方针,并将其与lifecycle绑定起来。
  • 4处创立一个LifecycleEventObserver方针,并将其添加到3处的LifecycleContainer方针中。该观察者会在LifecycleOwner方针的生命周期发生变化时履行相应的操作,例如在STARTED状况时将CallbackAndContract方针添加到mKeyToCallback调集中,在STOPPED状况时将其从调集中移除,在DESTROYED状况时调用unregister()办法刊出ActivityResultLauncher方针等。
  • 5处回来一个ActivityResultLauncher方针,该方针包含了launch()unregister()getContract()三个办法。其间,launch()办法用于发动ActivityIntent操作,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处用ActivityResultContractgetSynchronousResult()办法获取同步成果。假如存在同步成果,则会将其回来值经过dispatchResult()办法分发出去。
  • 假如1处不存在同步成果,那么就会走2处逻辑,调用ActivityResultContractcreateIntent()办法创立一个新的Intent方针,并依据需求设置ActivityIntent操作的选项。
  • 查看Intent方针的Action属性是否为"androidx.activity.result.contract.action.REQUEST_PERMISSIONS""androidx.activity.result.contract.action.INTENT_SENDER_REQUEST"。假如是,则会分别调用ActivityCompat.requestPermissions()办法恳求权限或ActivityCompat.startIntentSenderForResult()办法发动相应的操作;假如都不是,则默认调用ActivityCompat.startActivityForResult()办法发动ActivityIntent操作。

能够看到终究仍是调用的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()内部终究仍是调用了ComponentActivitystartActivityForResult()/onActivityResult()、onRequestPermissionsResult()办法,只不过是对其进行了一致的封装罢了,简化了运用。
  • 运用 registerForActivityResult(),能够经过一个 lambda 表达式来处理回来成果,避免了繁琐的 requestCodeonActivityResult 的处理逻辑。此外,registerForActivityResult 还支持多个恳求码和多个回来成果,使得代码愈加明晰和易于维护。

材料

【1】android官网:Activity Result API

欢迎扫描下方二维码或搜索微信公众号 代码说, 重视我的微信公众号查看最新文章~

startActivityForResult被标记为废弃?Activity Result API闪亮登场!