平时我们在使用
onActivityResult方法,遇到一个Activity中有多种返回结果时,总会出现很复杂的 if else 耦合判断,相关逻辑混杂在一起,饱受其扰。
我们知道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,在这个类中我们可以对合约进行扩展,来处理发起createIntent和parseResult返回结果 这两个点。
而我们再使用时只要处理在哪里发起,以及协议完成后的效果,至于中间的流程则定义在了协议里,像一些返回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>?)
}
}
}