ActivityResultLauncher使用 - SheTieJun/BaseKit GitHub Wiki

ActivityResultLauncher使用

当前基于androidx.activity1.4.0版本

一、这篇文章讲的是什么的?

1. 替代startActivityForResult
2. 注册必须在Activity#onStart()? 不,我们完全可以在用的时候才创建并使用

1. 了解ActivityResultContract

首先我们需要了解有哪些自带的ActivityResultContract

  • StartActivityForResult 使用Intent在activity(fragment)间通信,返回ActivityResult
  • StartIntentSenderForResult 使用IntentSenderRequest. builder构造,可以返回带有activity的ActivityResult
  • RequestMultiplePermissions 获取多个动态权限,返回Map<String,boolean>数组
  • RequestPermission 获取单个动态权限,成功后返回true
  • TakePicturePreview 调用相机,拍照后返回bitmap图片信息
  • TakePicture 调用相机,拍照后将图片保存到开发者指定的Uri,返回true
  • TakeVideo 拍摄视频,将拍摄内容保存到开发者指定的Uri,返回缩略图
  • CaptureVideo 拍摄视频,将内容保存到开发者指定的Uri后,返回true
  • PickContact 弹出手机联系人列表,用户选中其中一个后,返回Uri
  • GetContent 访问原始数据,返回Uri,例如相册中的单个图片
  • GetMultipleContents 功能如上,返回Uri数组,例如相册中的多个图片
  • OpenDocument 访问文件数据,打开文件夹,返回单个文件的Uri
  • OpenMultipleDocuments 功能如上,返回多个文件的Uri
  • OpenDocumentTree
  • CreateDocument 创建文件

当然也可以自己定义,不过上面已经基本满足我们的需求了。

eg:

class UserActivityResultContract : ActivityResultContract<String, UserInfo?>() {
    override fun createIntent(context: Context, input: String): Intent {
        val intent = Intent(context, UserInfoActivity::class.java)
        intent.putExtra("url", input)
        return intent
    }

    override fun parseResult(resultCode: Int, intent: Intent?): UserInfo? {
        if (resultCode == Activity.RESULT_OK) {
            if (intent?.hasExtra("userInfo") == true) {
                if (intent.getSerializableExtra("userInfo") != null) {
                    return intent.getSerializableExtra("userInfo") as UserInfo
                }
            }
        }
        return null
    }
}
 

2. 如何使用

大家一般是在Fragment或者Activity中使用,需要在onCreate中使用registerForActivityResult注册一下得到ActivityResultLauncher, 需要注意的是,通过源码androidx.activity.ComponentActivity#registerForActivityResult

     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.");
        }

Activity必须在onStart及之前执行。

androidx.fragment.app.Fragment#registerForActivityResult

  if (mState > CREATED) {
            throw new IllegalStateException("Fragment " + this + " is attempting to "
                    + "registerForActivityResult after being created. Fragments must call "
                    + "registerForActivityResult() before they are created (i.e. initialization, "
                    + "onAttach(), or onCreate()).");
        }

fragment必须在onCreate及以前执行。

案例:

直接在在Fragment或者Activity中创建ActivityResultLauncher

private val permissionLauncher =
        registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
           //结果
        }
 

然后在要调用的地方

permissionLauncher.launch(arrayOf(
                        Manifest.permission.WRITE_CALENDAR,
                        Manifest.permission.READ_CALENDAR
                    ))

总结:

使用registerForActivityResult注册得到ActivityResultLauncher,在Activity必须在onStart及之前执行,在fragment必须在onCreate及以前执行。

二注册ActivityResultLauncher必须在Activity#onStart()之前?

答案:当然不是

通过源码我可以知道,实际上注册获取ActivityResultLauncher是通过ActivityResultRegistry注册,通过查看源码然后我发现还有一个注册方法是不需要LifecycleOwner

这样我们就可以在任意一个地方只要有Activity的上下文(context)就可以注册获取ActivityResultLauncher

首先通过context(需要注意这里的context,要判断是否ComponentActivity) androidx.activity.ComponentActivity#getActivityResultRegistry获取ActivityResultRegistry 然后通过下面的方法进行注册:

于是添加了下面的扩展方法,进行注册获取ActivityResultLauncher: Activity:

fun <I, O> ComponentActivity.register(
    @NonNull key: String,
    @NonNull contract: ActivityResultContract<I, O>,
    @NonNull callback: ActivityResultCallback<O>
): ActivityResultLauncher<I> {
    return activityResultRegistry.register(key, contract, callback).also {
        lifecycle.addObserver(object : LifecycleEventObserver {
            override fun onStateChanged(source: LifecycleOwner, event: Event) {
                if (event == Event.ON_DESTROY) {
                    it.unregister()
                }
            }
        })
    }
}

fragment:

fun <I, O> Fragment.register(
    @NonNull key: String,
    @NonNull contract: ActivityResultContract<I, O>,
    @NonNull callback: ActivityResultCallback<O>
): ActivityResultLauncher<I> {
    return requireActivity().activityResultRegistry.register(key, contract, callback).also {
        lifecycle.addObserver(object : LifecycleEventObserver {
            override fun onStateChanged(source: LifecycleOwner, event: Event) {
                if (event == Event.ON_DESTROY){
                    it.unregister()
                }
            }
        })
    }
}

使用案例:

fun ComponentActivity.startActivityResult2(
    @NonNull key: String = "startActivityResult" + mNextLocalRequestCode.getAndIncrement(),
    @NonNull intent: Intent,
    @NonNull callback: ActivityResultCallback<ActivityResult>
) {
    return register(key,ActivityResultContracts.StartActivityForResult(), callback)
        .also {
            lifecycle.addObserver(object : LifecycleEventObserver {
                override fun onStateChanged(source: LifecycleOwner, event: Event) {
                    if (event == Event.ON_DESTROY) {
                        it.unregister()
                    }
                }
            })
        }.launch(intent)
}

ComponentActivity需要的地方进行直接调用就可以了

    startActivityResult2(intent = Intent(this,TestActivity::class.java)){
                it.resultCode.toString().logI()
            }

三、总结:

  1. 使用registerForActivityResult注册得到ActivityResultLauncher,在Activity必须在onStart及之前执行,在fragment必须在onCreate及以前执行。

  2. ActivityResultLauncher的注册实际上是通过ActivityResultRegistry注册获取的

  3. ActivityResultRegistry有2个注册方法,其中一个并不需要LifecycleOwner,所以通过这个新的注册方法我们可以不遵守:在Activity必须在onStart及之前执行,在fragment必须在onCreate及以前执行。

  4. Fragment在使用register的是时候需要注意Activity是不是为空,否则会异常

⚠️ **GitHub.com Fallback** ⚠️