Android Result API详解
旧版本startActivityForResult使用方式(传统方式)
在两个Activity之间交换数据
class FirstActivity : AppCompatActivity() {
//传统方式
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val firstButton = findViewById<Button>(R.id.button2)
firstButton.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startActivityForResult(intent, 1)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(resultCode){
1 -> {
if (resultCode == RESULT_OK){
val data = data?.getStringExtra("data")
}
}
}
}
}
调用statActivityForResult()方法去向SecondActivity请求数据,然后在onActivityResult()方法去解析SecondActivity返回的结果。
另一个SecondActivity
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val secondButton = findViewById<Button>(R.id.button2)
secondButton.setOnClickListener {
val intent = Intent()
intent.putExtra("data","data from SecondActivity")
setResult(RESULT_OK,intent)
finish()
}
}
}
使用Activity Result API来实现同样的功能
class FirstActivity : AppCompatActivity() {
//传统方式
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val firstButton = findViewById<Button>(R.id.button2)
firstButton.setOnClickListener {
//传统方式
val intent = Intent(this, SecondActivity::class.java)
requestDataLauncher.launch(intent)
}
}
//使用 Activity Result API来实现同样的功能
private val requestDataLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if(result.resultCode == RESULT_OK){
val data = result.data?.getStringExtra("data")
}
}
}
首先移除来对onActivityResult()
方法的重写,通过调用registerForActivityResult()
方法注册一个对Activity结果的监听。
registerForActivityResult()
:接收两个参数,第一个参数是一种Contract
类型;第二个是一个Lambda
表达式,当有结果返回时会回调到这里,然后我们在这里获取并处理数据。
registerForActivityResult()
方法的返回值是一个ActivityResultLauncher
对象,这个对象当中有一个launch()
方法可以用于去启用Intent
。这样我们就不需要再调用startActivityForResult()
方法了,而是直接调用launch()
方法,并把Intent
传入即可。
请求运行时权限
class FirstActivity : AppCompatActivity() {
private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
if (granted) {
// User allow the permission.
} else {
// User deny the permission.
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_first)
val firstButton = findViewById<Button>(R.id.first_button)
firstButton.setOnClickListener {
requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
}
}
}
请求运行时的权限,需要使用RequestPermission
这种Contract
。
指定了不同的Contract
类型,Lambda
表达式的参数也会发生变化,现在Lambda
表达式会传入一个布尔型的参数,用于告诉我们用户是否允许了我们的请求的权限。
在launch()
方法的参数也发生了变化,需要传入要请求的权限名即可。
内置Contract
StartActivityForResult()
StartIntentSenderForResult()
RequestMultiplePermissions()
RequestPermission()
TakePicturePreview()
TakePicture()
TakeVideo()
PickContact()
GetContent()
GetMultipleContents()
OpenDocument()
OpenMultipleDocuments()
OpenDocumentTree()
CreateDocument()
自定义Contract
除了以上内置Contract之外,还可以自定义自己的Conteact类型。
首先继承ActivityResultContract
,它的内部定义了两个抽象方法,如下所示:
public abstract class ActivityResultContract<I, O> {
public abstract @NonNull Intent createIntent(@NonNull Context context, I input);
public abstract O parseResult(int resultCode, @Nullable Intent intent);
...
}
也就是说,任何一个继承自ActivityResultContract
的Contract
,都需要重写createIntent()
和parseResult()
这两个方法。
而这两个方法的作用也非常明显。createIntent()
就是用于创建一个Intent,后续会使用这个Intent来发起动作,比如启动另外一个Activity去获取数据,或者打开相机去拍照等等。而parseResult()
则是用于解析响应的结果,并把解析出来的结果作为输出参数返回到Lambda
表达式当中。
每一个内置的Contract
都是使用的这种规则来封装的自己的逻辑。
那么我们要自定义一个什么样的Contract
来进行演示呢?
我想了一下,刚才在编写两个Activity
之间交换数据的时候,我们需要显示地启动SecondActivity
,并手动将SecondActivity
返回的数据从Intent
中解析出来,这就稍微有些麻烦。而借助自定义Contract
就可以对此处进行优化。
新建一个叫做GetDataFromSecondActivity
的Contract
,代码如下所示:
class GetDataFromSecondActivity : ActivityResultContract<Void, String?>() {
override fun createIntent(context: Context, input: Void?): Intent {
return Intent(context, SecondActivity::class.java)
}
override fun parseResult(resultCode: Int, intent: Intent?): String? {
if (resultCode == Activity.RESULT_OK) {
if (intent != null) {
return intent.getStringExtra("data")
}
}
return null
}
}
我们通过泛型指定,这个Contract
的输入参数是Void
,输出参数是一个字符串。
然后在createIntent()
方法中,我们手动创建了一个Intent
,并将它的用途设置为打开SecondActivity
。
最后在parseResult()
方法中,我们对SecondActivity
返回的结果进行解析,并将解析出来的字符串作为输出参数返回。
这样一个自定义的Contract
就完成了,而我们使用这个Contract
再去实现最开始的在两个Activity
之间交换数据的功能,就会变得更加简单:
class FirstActivity : AppCompatActivity() {
private val getDataLauncher = registerForActivityResult(GetDataFromSecondActivity()) { data ->
// Handle data from SecondActivity
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_first)
val firstButton = findViewById<Button>(R.id.first_button)
firstButton.setOnClickListener {
getDataLauncher.launch(null)
}
}
}
可以看到,借助GetDataFromSecondActivity
这个Contract
,我们不需要再显式地声明去启动SecondActivity
,launch()
方法直接传入null
即可。另外,我们也不需要再去手动解析SecondActivity
返回的数据,lambda
表达式上的参数就是解析出来的结果了。
小提示:
在多任务的场景中,我们调用startActivityForResult()方式时,除了传入Intent之外,还需要传入一个requestCode,用于进行区分。例如:
class FirstActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_first)
val firstButton = findViewById<Button>(R.id.first_button)
val secondButton = findViewById<Button>(R.id.second_button)
firstButton.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW)
startActivityForResult(intent, 1)
}
secondButton.setOnClickListener {
val intent = Intent(Intent.ACTION_DIAL)
startActivityForResult(intent, 2)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
1 -> {
// Handle result for ACTION_VIEW
}
2 -> {
// Handle result for ACTION_DIAL
}
}
}
}
这里我们分别在两处调用了startActivityForResult()方法,它们各自用于处理不同的任务,因此需要给它们设置不同的requestCode。
在onActivityResult()方法当中,我们为了区分这个结果是来自之前的哪个任务的,所以要在这里再对requestCode进行判断。
这是以前使用startActivityForResult()方法时的传统写法。
而Activity Result API是没有地方让你传入requestCode的。
我在刚接触Activity Result API的时候受思维惯性的影响被这个问题困扰了一下,没有地方传入requestCode该怎么办呢?
后来思维转过来弯之后发现,原来Activity Result API根本就不需要requestCode这种东西,我们可以使用如下写法来实现和刚才完全一样的功能:
class FirstActivity : AppCompatActivity() {
private val actionViewLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
// Handle result for ACTION_VIEW
}
private val actionDialLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
// Handle result for ACTION_DIAL
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_first)
val firstButton = findViewById<Button>(R.id.first_button)
val secondButton = findViewById<Button>(R.id.second_button)
firstButton.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW)
actionViewLauncher.launch(intent)
}
secondButton.setOnClickListener {
val intent = Intent(Intent.ACTION_DIAL)
actionDialLauncher.launch(intent)
}
}
}
由此也可以看出,Activity Result API的设计更加合理,不需要借助requestCode这种魔术数字也能对多个任务进行区分。
一切都更加简单和清晰。
版权属于:Monster_4y
本文链接:https://blog.zmonster.top/87.html
转载时须注明出处及本声明