前言
google 在 androidX 上对 activity 及 fragment 部分功能调整(startActivityForResult ,requestPermission,Save/Restore InstanceState,onBackPress)简化为 callback 的可回调方式
使用(暂时还会修改,建议 beta 版再更)
// build.gradle
implementation 'androidx.activity:activity-ktx:1.2.0-alpha03'
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha03'
demo
1 构造方法直接传 layoutId
class MyActivity : AppCompatActivity(R.layout.my_activity)
class MyFragmentActivity: FragmentActivity(R.layout.my_fragment_activity)
class MyFragment : Fragment(R.layout.my_fragment)
2 BackPress
class MyFragment : Fragment() {
override fun onAttach(context: Context) {
super.onAttach(context)
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// Do something
}
}
requireActivity().onBackPressedDispatcher.addCallback(this, callback)
}
}
3 SaveInstance
class MyActivity : AppCompatActivity() {
companion object {
private const val MY_SAVED_STATE_KEY = "my_saved_state"
private const val SOME_VALUE_KEY = "some_value"
}
private lateinit var someValue: String
private val savedStateProvider = SavedStateRegistry.SavedStateProvider {
Bundle().apply {
putString(SOME_VALUE_KEY, someValue)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
savedStateRegistry
.registerSavedStateProvider(MY_SAVED_STATE_KEY, savedStateProvider)
}
fun someMethod() {
someValue = savedStateRegistry
.consumeRestoredStateForKey(MY_SAVED_STATE_KEY)
?.getString(SOME_VALUE_KEY)
?: ""
}
}
4 Permission 请求
val requestPermission = prepareCall(RequestPermission()) { isGranted ->
toast("Location granted: $isGranted")
}
requestPermission(ACCESS_FINE_LOCATION)
5 FragmentFactory
class MyFragmentFactory : FragmentFactory() {
override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
// 调用loadFragmentClass()获得Class对象
val fragmentClass = loadFragmentClass(classLoader, className)
// 现在,您可以使用className / fragmentClass来确定您的首选方式
// 实例化Fragment对象,然后在此处进行操作。
// 或者调用常规FragmentFactory来实例化Fragment
// 没有参数的构造函数
return super.instantiate(classLoader, className)
}
}
6 kotlin 对 FragmentManager 提供操作符
// Before
supportFragmentManager
.beginTransaction()
.add(R.id.container, MyFragment::class.java, null)
.commit()
// After
supportFragmentManager.commit {
replace<MyFragment>(R.id.container)
}
7 FragmentContainerView
如果您正在使用 FrameLayout 作为 Fragment 的父级,您应该切换到 FragmentContainerView代替。
它修复了一些动画z索引顺序问题和窗口插入调度。
FragmentContainerView 从 AndroidX Fragment 1.2.0 开始可用。
最后我们看下 startActivityForResult 使用、原理及参考源码做的修改
使用:MainActivity -> SecondActivity
class MainActivity : BaseActivity(R.layout.activity_main) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
tvJump.setOnClickListener {
prepareCall(StartSecondActivityForResultContract()){
console("output",it)
}.launch("input:哈哈")
}
}
}
class StartSecondActivityForResultContract : ActivityResultContract<String, String>() {
companion object {
const val INPUT = "INPUT"
const val OUTPUT = "OUTPUT"
}
override fun createIntent(context: Context, input: String?): Intent =
Intent(context, SecondActivity::class.java).apply {
putExtra(INPUT, input)
}
override fun parseResult(resultCode: Int, intent: Intent?): String? {
return when (resultCode) {
Activity.RESULT_OK -> intent?.getStringExtra(OUTPUT)
else -> null
}
}
}
class SecondActivity : AppCompatActivity(R.layout.activity_second) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
tvInput.text = intent?.getStringExtra(INPUT)?:"Null"
btn.setOnClickListener {
setResultAndFinish()
}
}
}
fun Activity.setResultAndFinish(){
setResult(Activity.RESULT_OK, Intent().putExtra(OUTPUT," nice "))
finish()
}
原理:观察者模式
1: prepareCall 会调用 ActivityResultRegistry 的 register 方法拿到 ActivityResultLauncher 对象
2: register 方法调用时 将 ActivityResultCallback 对象 登记在注册中心(ActivityResultRegistry) 的 Map 里
3: prepareCall 返回 ActivityResultLauncher对象后会调用 launch 方法
4: launch 方法会调用 ActivityResultRegistry 的抽象方法 invoke
5: 而 invoke 会 调用 startActivityForResult
6: 最终等 ComponentActivity 回调到 onActivityResult
7: 再通过 dispatchResult(requestCode, resultCode, data) 分发 result
8: 通过 requestCode 找到 key ,再从 Map 里 拿到 ActivityResultCallback 对象并调用 他的 onActivityResult( O result) 返回结果
以下是相关类图:
上面 demo 代码并不简单,还需要创建 ActivityResultContract 实现类。虽然多了也可以像 google 的 ActivityResultContracts 那样管理,但对于我们还是只想知道
callback,不必关心 contracts。
于是我们可以根据这个思路做个精简版的 ActivityResultCallback
// MainActivity
prepareCall(ActivityResultCallback<String> { result ->
console(
"output",
result ?: "Null"
)
}).launch(Intent(this, SecondActivity::class.java).apply {
putExtra("INPUT", "hhhhh")
})
// SecondActivity
class SecondActivity : BaseActivity(R.layout.activity_second) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
tvInput.text = intent?.getStringExtra("INPUT") ?: "Null"
btn.setOnClickListener {
setResult(" nice ")
finish()
}
}
}
hh, 没有 contracts 并且不用关心 extras 的 key 名字了
code 地址
end...
1:BaseActivity 相当于ComponentActivity 重写了 onActivityResult / setResult 等方法
2: ActivityResultRegistry 提供注册 分发 result 等gong n
2:另外需注意使用场景,result 需基本数据类型和序列化对象,数据量不能超过1m
注册中心代码:
abstract class ActivityResultRegistry {
private val requestCodeToCallback = SparseArray<ActivityResultCallback<*>>()
private val code = AtomicInteger(0)
fun <O> register(
owner: LifecycleOwner,
callback: ActivityResultCallback<O>
): ActivityResultLauncher {
val requestCode = code.getAndIncrement()
requestCodeToCallback.put(requestCode, callback)
val lifecycle = owner.lifecycle
lifecycle.addObserver(LifecycleEventObserver { _, event ->
if (Lifecycle.Event.ON_DESTROY == event) {
unregister(requestCode)
}
})
return ActivityResultLauncher {
intent -> invoke(requestCode, intent)
}
}
abstract operator fun invoke(rc: Int, intent: Intent?)
private fun unregister(requestCode: Int) {
requestCodeToCallback.remove(requestCode)
}
fun dispatchResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
val callback = requestCodeToCallback[requestCode] ?: return false
doDispatch(resultCode, data, callback)
return true
}
@Suppress("UNCHECKED_CAST")
private fun <O> doDispatch(
resultCode: Int, data: Intent?,
callback: ActivityResultCallback<O?>?
) {
if (resultCode == Activity.RESULT_OK)
callback?.onGetResult(data?.extras?.get(OUTPUT_EXTRA) as O?)
}
}
感谢:
How AndroidX changes the way we work with Activities and Fragments
A first look at AndroidX Activity Result APIs