知识点补全2:startActivityForResult()弃用

1,365 阅读3分钟

平时我们在使用onActivityResult方法,遇到一个Activity中有多种返回结果时,总会出现很复杂的 if else 耦合判断,相关逻辑混杂在一起,饱受其扰。

google开发文档--获取 activity 的结果

我们知道Activity信息传递是进程间通信,是通过ams进行的,onActivityResult()方法是Activity等待结果返回的一个回调方法,所有的结果值,都要在这里进行判断,很不方便,google废弃了startActivityForResult(),提供了Result API供我们使用。

简单案例

我们直接使用 ActivityResultContracts.StartActivityForResult() google定义的一个合约来写

class MainActivity : AppCompatActivity() {

    //ActivityResultLauncher
    private val launcher : ActivityResultLauncher =
    	registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
    		activityResult ->Log.d("MainActivity", activityResult.toString())
    	 }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
      
	txt1.setOnClickListener { view ->
	    val intent = Intent(this, MainActivity::class.java)
		launcher.launch(intent)
	}
    }
}

结果

MainActivity: ActivityResult{resultCode=RESULT_OK, data=Intent { (has extras)}

google提供的合约

都定义在ActivityResultContracts

  • RequestMultiplePermissions: 用于请求一组权限
  • RequestPermission: 用于请求单个权限
  • TakePicturePreview: 调用MediaStore.ACTION_IMAGE_CAPTURE拍照,返回值为Bitmap图片
  • TakePicture: 调用MediaStore.ACTION_IMAGE_CAPTURE拍照,并将图片保存到给定的Uri地址,返回true表示保存成功。
  • TakeVideo: 调用MediaStore.ACTION_VIDEO_CAPTURE 拍摄视频,保存到给定的Uri地址,返回一张缩略图。
  • PickContact: 从通讯录APP获取联系人
  • GetContent: 提示用选择一条内容,返回一个通过ContentResolver#openInputStream(Uri)访问原生数据的Uri地址(content://形式) 。默认情况下,它增加了 Intent#CATEGORY_OPENABLE, 返回可以表示流的内容。
  • CreateDocument: 提示用户选择一个文档,返回一个(file:/http:/content:)开头的Uri。
  • OpenMultipleDocuments: 提示用户选择文档(可以选择多个),分别返回它们的Uri,以List的形式。
  • OpenDocumentTree: 提示用户选择一个目录,并返回用户选择的作为一个Uri返回,应用程序可以完全管理返回目录中的文档。 上面这些预定义的Contract中,除了StartActivityForResult和RequestMultiplePermissions之外,基本都是处理的与其他APP交互,返回数据的场景,比如,拍照,选择图片,选择联系人,打开文档等等。使用最多的就是StartActivityForResult和RequestMultiplePermissions了。

以上合约转载自 CSDN博主「乌克丽丽丶」的原创文章

理解设计思路

为了结耦,让职责单一,google抽象出了ActivityResultContract,在这个类中我们可以对合约进行扩展,来处理发起createIntentparseResult返回结果 这两个点。

而我们再使用时只要处理在哪里发起,以及协议完成后的效果,至于中间的流程则定义在了协议里,像一些返回code,返回数据的key,在之前都需要单独定义为常量,很不好管理。

当然,google也提供了一些我们常用的协议类,我们可以直接使用,如果需要特殊处理,则需要我们自己继承这个抽象类进行实现

/**
 * A contract specifying that an activity can be called with an input of type I
 * and produce an output of type O
 *
 * Makes calling an activity for result type-safe.
 *
 * @param <I> input type
 * @param <O> output type
 *
 * @see ActivityResultCaller
 */
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;
        }
    }
}

使用步骤

导入

需要引入AndroidX Activity 和 Fragment 中引入的 Activity Result API

implementation "androidx.activity:activity-ktx:1.2.4"
implementation "androidx.fragment:fragment-ktx:1.3.1"

1.制定合约

ActivityResultContract

class AActivityContract :
    ActivityResultContract<ArrayList<AItemVM>, AItemVM?>() {

    companion object {
        const val EXTRA_LIST = "List"
        const val EXTRA_RESULT = "Result"
    }

    override fun createIntent(
        context: Context,
        input: ArrayList<AItemVM>?
    ): Intent {
        return Intent(context, AActivity::class.java).apply {
            this.putParcelableArrayListExtra(EXTRA_LIST, input)
        }
    }

    override fun parseResult(resultCode: Int, intent: Intent?): AItemVM? {
        val data = intent?.getParcelableExtra<AItemVM>(EXTRA_RESULT)
        return if (resultCode == Activity.RESULT_OK) data else null
    }

}

2. 注册以及使用

  • registerForActivityResult() 必须要写在 fragment 和 activity 创建成功之前。
  • 但在 fragment 或 activity 的 Lifecycle 变为 CREATED 状态之前,您无法启动 ActivityResultLauncher。
class MainActivity : AppCompatActivity() {

    private val launcher = registerForActivityResult(AActivityContract()) {
    //TO DO 展示结果
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
      
	btn.setOnClickListener { v: View? ->
            launcher.launch(getList() as ArrayList<AItemVM>?)
        }
    }
}