网站建设设计技术方案模板,衡水网站建设在哪里,wordpress缩略图设置,网站缺点Activity Results APIActivity Result API提供了用于注册结果、启动结果以及在系统分派结果后对其进行处理的组件。—Google官方文档https://developer.android.google.cn/training/basics/intents/result?hlzh-cn一句话解释#xff1a;官方Jetpack组件用于代替startActivity…Activity Results APIActivity Result API提供了用于注册结果、启动结果以及在系统分派结果后对其进行处理的组件。—Google官方文档https://developer.android.google.cn/training/basics/intents/result?hlzh-cn一句话解释官方Jetpack组件用于代替startActivityForResult()/onActivityResult()。看完文档会发现能代替startActivityForResult()但也并没有好用到哪去。其实startActivityForResult()的调用并不麻烦复杂页面的使用做一下简单的封装即可。核心痛点在onActivityResult()的结果回调必须在Activity/Fragment中导致我们在处理一些复杂的跳转逻辑时总是要反复横跳。Activity Result API的出现貌似可以解决这一痛点页面返回结果直接通过回调就可以获得还可以自定义跳转协议进一步封装简化。本来是那么的美好然而在activity-ktx:1.2.0-beta02版本之后变得让人望而却步。https://developer.android.google.cn/jetpack/androidx/releases/activity?hlzh-cn#1.2.0-beta02行为变更现在尝试使用 Lifecycle 已达到 STARTED 的 LifecycleOwner 调用 register() 时ActivityResultRegistry 会抛出 IllegalStateException。(b/165435866)熟悉Activity Results API的都知道页面返回结果的回调函数是在registerForActivityResult()方法里面的这就导致了两个问题1. 跟startActivityForResult()/onActivityResult()一样的痛点调launch跳转页面获取返回结果后还是要回到Activity/Fragment中处理。2. 生命周期STARTED前注册意味着我们必须提前注册而无法在点击使用时注册只能在BaseActvity中封装。但是遵循了高聚合低耦合的思想封装在BaseActvity中的方案我们是万万拒绝的。接下来我们就来探讨如何在不封装BaseActvity的情况下只调用一个带回调的函数实现startActivityForResult()/onActivityResult()。解决思路非Activity Results API方案其实早在Activity Results API问世前我们项目中就有使用一个空视图GhostFragment作为中转回调的方案来实现。大概的思路如下Activty/Fragment——add GhostFragment——onAttach中startActivityForResult——GhostFragment onActivityResult接收结果——callback回调给Activty/Fragment代码实现GhostFragment.kthttps://github.com/iDeMonnnnnn/DeMon-ARA/blob/main/app/src/main/java/com/demon/ara/ghost/GhostFragment.ktclass GhostFragment : Fragment() {private var requestCode -1private var intent: Intent? nullprivate var callback: ((result: Intent?) - Unit)? nullfun init(requestCode: Int, intent: Intent, callback: ((result: Intent?) - Unit)) {this.requestCode requestCodethis.intent intentthis.callback callback}private var activityStarted falseoverride fun onAttach(activity: Activity) {super.onAttach(activity)if (!activityStarted) {activityStarted trueintent?.let { startActivityForResult(it, requestCode) }}}override fun onAttach(context: Context) {super.onAttach(context)if (!activityStarted) {activityStarted trueintent?.let { startActivityForResult(it, requestCode) }}}override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if (resultCode Activity.RESULT_OK requestCode this.requestCode) {callback?.let { it1 - it1(data) }}}override fun onDetach() {super.onDetach()intent nullcallback null}
}Ghost.kthttps://github.com/iDeMonnnnnn/DeMon-ARA/blob/main/app/src/main/java/com/demon/ara/ghost/Ghost.ktobject Ghost {var requestCode 0set(value) {field if (value Integer.MAX_VALUE) 1 else value}inline fun launchActivityForResult(starter: FragmentActivity?,intent: Intent,crossinline callback: ((result: Intent?) - Unit)) {starter ?: returnval fm starter.supportFragmentManagerval fragment GhostFragment()fragment.init(requestCode, intent) { result -callback(result)fm.beginTransaction().remove(fragment).commitAllowingStateLoss()}fm.beginTransaction().add(fragment, GhostFragment::class.java.simpleName).commitAllowingStateLoss()}}看到这里有同学就会质疑了每次都添加一个Fragment就为了回调简化代码这不浪费内存么值得么第一次看到这个代码我也是迟疑的直到我看了Glide的源码。https://github.com/bumptech/glide使用了Glide库的同学开发中肯定有遇到如下报错You cannot start a load for a destroyed activity放一个Glide源码的片段 NonNullpublic RequestManager get(NonNull FragmentActivity activity) {if (Util.isOnBackgroundThread()) {return get(activity.getApplicationContext());} else {assertNotDestroyed(activity);frameWaiter.registerSelf(activity);FragmentManager fm activity.getSupportFragmentManager();return supportFragmentGet(activity, fm, /*parentHint*/ null, isActivityVisible(activity));}}TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)private static void assertNotDestroyed(NonNull Activity activity) {if (Build.VERSION.SDK_INT Build.VERSION_CODES.JELLY_BEAN_MR1 activity.isDestroyed()) {throw new IllegalArgumentException(You cannot start a load for a destroyed activity);}} 看到这里大家应该是明白了这个方案在Glide中被大家“发扬光大”了而已。Activity Results API方案再来思考一下如何使用Activity Results API实现。根据前文提到的Activity Results API我们想要去解决的两个问题• 回调最好能在launch中处理。• 在Activity/Fragment中自动注册。1. 回调改造在launch中处理这里借鉴了优雅地封装 Activity Result API的思路非常巧妙。https://blog.csdn.net/c10WTiybQ1Ye3/article/details/119430078DeMonActivityResult.kthttps://github.com/iDeMonnnnnn/DeMon-ARA/blob/main/core/src/main/java/com/demon/core/DeMonActivityResult.ktclass DeMonActivityResultI, O(caller: ActivityResultCaller, contract: ActivityResultContractI, O) {/*** 直接点击返回键或者直接finish是否会触发回调* 用于处理一些特殊情况如只要返回就刷新等* 注意此时回调返回的值或者{ActivityResult#getData()}应该为空需要做好判空处理*/private var isNeedBack falseprivate var launcher: ActivityResultLauncherI? caller.registerForActivityResult(contract) {if (isNeedBack) {callback?.onActivityResult(it)} else {if (it ! null) {if (it is ActivityResult) {if (it.resultCode Activity.RESULT_OK) callback?.onActivityResult(it)} else {callback?.onActivityResult(it)}}}callback null}private var callback: ActivityResultCallbackO? nullJvmOverloadsfun launch(input: I, isNeedBack: Boolean false, callback: ActivityResultCallbackO?) {this.callback callbackthis.isNeedBack isNeedBacklauncher?.launch(input)}2. 在Activity/Fragment中自动注册谈到Activity生命周期监听有个始终绕不开的接口类Application.ActivityLifecycleCallbacks。废话不多说我要在onActivityCreatedregister由于Activity Result API是自动反注册的所以我们不用关心unRegister。然后就是register后怎么拿到ActivityResultLauncher呢经过上一步的改造后我们需要拿到的是DeMonActivityResult恕在下才识浅薄只能想到用HashMap。 //临时存储DeMonActivityResultval resultMap mutableMapOfString, DeMonActivityResultIntent, ActivityResult()大概的思路如下onActivityCreated——时间戳生成唯一key——key putExtra存Activty——register得到Result——将Result与key存HashMapActivty——getStringExtra得key——HashMap得Result——Result.launch启动——launch回调得返回结果Fragment的生命周期监听与Activty类似可以通过注册并实现抽象类FragmentLifecycleCallbacks//注册监听Fragment生命周期
activity.supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentCallbacks, false)
//反注册取消监听Fragment生命周期
activity.supportFragmentManager.unregisterFragmentLifecycleCallbacks(it)因此Fragment与Activity中的实现方法基本一致。3. 实现代码DeMonActivityCallbacks.kthttps://github.com/iDeMonnnnnn/DeMon-ARA/blob/main/core/src/main/java/com/demon/core/lifecycle/DeMonActivityCallbacks.ktobject DeMonActivityCallbacks : Application.ActivityLifecycleCallbacks {private val TAG DeMonActivityCallbacksconst val DEMON_ACTIVITY_KEY DeMon_Activity_Keyval DEMON_FRAGMENT_KEY DeMon_Fragment_Key//临时存储FragmentCallbacksval callbackMap mutableMapOfString, DeMonFragmentCallbacks()//临时存储DeMonActivityResultval resultMap mutableMapOfString, DeMonActivityResultIntent, ActivityResult()override fun onActivityCreated(activity: Activity, p1: Bundle?) {if (activity is FragmentActivity) {val mapKey: String activity.javaClass.simpleName System.currentTimeMillis()Log.i(TAG, onActivityCreated: mapKey$mapKey)//注册val fragmentCallbacks DeMonFragmentCallbacks()callbackMap[mapKey] fragmentCallbacksactivity.supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentCallbacks, false)val result DeMonActivityResult(activity, ActivityResultContracts.StartActivityForResult())activity.intent.putExtra(DEMON_ACTIVITY_KEY, mapKey)resultMap[mapKey] result}}override fun onActivityDestroyed(activity: Activity) {if (activity is FragmentActivity) {val mapKey activity.intent.getStringExtra(DEMON_ACTIVITY_KEY)Log.i(TAG, onActivityDestroyed: mapKey$mapKey)if (!mapKey.isNullOrEmpty()) {callbackMap[mapKey]?.let { activity.supportFragmentManager.unregisterFragmentLifecycleCallbacks(it) }//移除callbackMap.remove(mapKey)resultMap.remove(mapKey)}}}override fun onActivityStarted(p0: Activity) {}override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {}override fun onActivityResumed(p0: Activity) {}override fun onActivityPaused(p0: Activity) {}override fun onActivityStopped(p0: Activity) {}
}篇幅原因Fragment生命周期监听和实现可见DeMonFragmentCallbacks.kt。https://github.com/iDeMonnnnnn/DeMon-ARA/blob/main/core/src/main/java/com/demon/core/lifecycle/DeMonFragmentCallbacks.kt我们这里固定注册的是ActivityResultContracts.StartActivityForResult()可能又会又同学觉得这样无法自定义跳转协定太不灵活了。其实不然我们可以封装扩展Intent比如大神陈小缘的ActivityMessenger。https://github.com/Ifxcyr/ActivityMessenger我们这个库也是按照这个思路对Intent进行了扩展使用起来一样很方便可以看本库的源码 。https://github.com/iDeMonnnnnn/DeMon-ARA接下来我们只需要在Application中registerActivityLifecycleCallbacks(DeMonActivityCallbacks)即可。值得注意的是registerActivityLifecycleCallbacks每次调用就是在回调集合中添加一个ActivityLifecycleCallbacks对象集合中的每个ActivityLifecycleCallbacks都可以收到回调因此可以注册多个。简单处理一下获取DeMonActivityResult的逻辑JvmStatic
fun getActivityResult(NonNull activity: FragmentActivity): DeMonActivityResultIntent, ActivityResult? {activity.run {val mapKey intent.getStringExtra(DeMonActivityCallbacks.DEMON_ACTIVITY_KEY)return if (!mapKey.isNullOrEmpty()) {DeMonActivityCallbacks.resultMap[mapKey]} else {null}}
}接下来我们可以在Activty/Fragment按照如下Java代码中使用即可DeMonActivityResultIntent, ActivityResult result DeMonAraHelper.getActivityResult(JavaActivity.this);
if (result ! null) {result.launch(new Intent(this, TestJumpActivity.class), true,data - {if (data.getData() ! null) {String str data.getData().getStringExtra(tag);binding.text.setText(跳转页面返回值 str);} else {binding.text.setText(我是返回键返回的没有返回值~);}});
} 走到这里我们就实现了我们最初的目标调用一个带回调的函数实现startActivityForResult()/onActivityResult()。而且如果是Kotlin中进一步扩展调用只会更简单如forActivityResult(pairIntentActResultActivity()) {val str it?.getStringExtra(tag) ?: text.text 跳转页面返回值$str
}Benchmark我们简单测试一下以下四种方式直接执行100次时的性能。测试代码可见BenchmarkActivity.kthttps://github.com/iDeMonnnnnn/DeMon-ARA/blob/main/app/src/main/java/com/demon/ara/BenchmarkActivity.kt测试机型小米5内存方面测试过程中使用AndroidStudio Profiler监测的内存波动基本一致。源码文章中的所以代码都可见DeMon-ARAhttps://github.com/iDeMonnnnnn/DeMon-ARA参考致谢优雅地封装 Activity Result APIhttps://blog.csdn.net/c10WTiybQ1Ye3/article/details/119430078ActivityMessengerhttps://github.com/Ifxcyr/ActivityMessengerGlidehttps://github.com/bumptech/glide