| @@ -238,6 +238,8 @@ | |||
| <entry key="..\:/xuekaole/XKLLocal/app/src/main/res/layout/dictionary_floating_layout.xml" value="0.5" /> | |||
| <entry key="..\:/xuekaole/XKLLocal/app/src/main/res/layout/fragment_course_pack.xml" value="0.3338541666666667" /> | |||
| <entry key="..\:/xuekaole/XKLLocal/app/src/main/res/layout/fragment_learn_center.xml" value="0.3338541666666667" /> | |||
| <entry key="..\:/xuekaole/XKLLocal/app/src/main/res/layout/fragment_statistics_time_top.xml" value="0.23385416666666667" /> | |||
| <entry key="..\:/xuekaole/XKLLocal/app/src/main/res/layout/item_statics_for_time.xml" value="0.24791666666666667" /> | |||
| <entry key="..\:/xuekaole/XKLLocal/lib/common/src/main/res/drawable/ic_search.xml" value="0.2212962962962963" /> | |||
| </map> | |||
| </option> | |||
| @@ -1,9 +1,8 @@ | |||
| package com.xkl.cdl.data.repository | |||
| import android.net.IpPrefix | |||
| import android.util.LruCache | |||
| import androidx.lifecycle.MutableLiveData | |||
| import com.suliang.common.AppConfig | |||
| import com.suliang.common.eventbus.SingleLiveData | |||
| import com.suliang.common.extension.diskIo2Main | |||
| import com.suliang.common.util.file.FileUtil | |||
| import com.xkl.cdl.data.AppConstants | |||
| @@ -33,10 +32,12 @@ object AudioCache { | |||
| } | |||
| /** 通知查到的录音 */ | |||
| private lateinit var audioLiveData : MutableLiveData<String?> | |||
| private lateinit var audioLiveData : SingleLiveData<String?> | |||
| fun initAudioLiveData() : MutableLiveData<String?> { | |||
| audioLiveData = MutableLiveData<String?>() | |||
| fun initAudioLiveData() : SingleLiveData<String?> { | |||
| if (!this::audioLiveData.isInitialized){ | |||
| audioLiveData = SingleLiveData<String?>() | |||
| } | |||
| return audioLiveData | |||
| } | |||
| @@ -3,6 +3,7 @@ package com.xkl.cdl.module | |||
| import android.app.Activity | |||
| import android.app.Application | |||
| import android.os.Bundle | |||
| import com.suliang.common.base.LibApplication | |||
| import com.suliang.common.util.ActivityStackManager | |||
| import com.suliang.common.util.LogUtil | |||
| import com.suliang.common.util.file.FileUtil | |||
| @@ -25,34 +26,34 @@ import kotlin.properties.Delegates | |||
| * create 2022/3/14 11:27 | |||
| * Describe: | |||
| */ | |||
| class XKLApplication : Application() { | |||
| class XKLApplication : LibApplication() { | |||
| companion object{ | |||
| val mobileCache:MobileCache by lazy { | |||
| val file : String = File(FileUtil.getSaveDirFile("db"), "mydb").absolutePath | |||
| Mobile_cache.new_(file) | |||
| } | |||
| private var instance : XKLApplication by Delegates.notNull() | |||
| fun instance() = instance | |||
| // private var instance : XKLApplication by Delegates.notNull() | |||
| fun instance() = LibApplication.instance() | |||
| } | |||
| override fun onCreate() { | |||
| super.onCreate() | |||
| instance = this | |||
| // instance = this | |||
| SQLiteDatabase.loadLibs(this) | |||
| LogUtil.e(UUID.randomUUID().toString().replace("-","")) | |||
| // LogUtil.e(UUID.randomUUID().toString().replace("-","")) | |||
| //初始MMKV存储 | |||
| val rootDir = MMKV.initialize(this) | |||
| LogUtil.e(rootDir) | |||
| setRxJavaErrorHandler() | |||
| // val rootDir = MMKV.initialize(this) | |||
| // LogUtil.e(rootDir) | |||
| // setRxJavaErrorHandler() | |||
| // HookMobileCache().hook() | |||
| // registerActivityLifecycleCallbacks(lifecycleCallback) | |||
| } | |||
| /*** | |||
| /* *//*** | |||
| * 避免 调用多次onError。正常来说第一次onError会走正常的Observer处理,其他会走ErrorHandler。通过此方法捕捉多次的error | |||
| */ | |||
| *//* | |||
| private fun setRxJavaErrorHandler(){ | |||
| RxJavaPlugins.setErrorHandler(Consumer { e -> | |||
| e.printStackTrace() | |||
| @@ -78,7 +79,7 @@ class XKLApplication : Application() { | |||
| }) | |||
| } | |||
| */ | |||
| private object lifecycleCallback : Application.ActivityLifecycleCallbacks { | |||
| private var count = 0 | |||
| @@ -44,22 +44,26 @@ class StatisticsTimeTopFragment : BaseFragmentVM<FragmentStatisticsTimeTopBindin | |||
| //监听时间选项变化 | |||
| vm.timeStatisticsPositionLiveData.observe(this) { position -> | |||
| if (!vm.isInitStatisticsResponse()) { | |||
| vm.getStatistics().observe(this) { | |||
| if (it) { | |||
| (binding.rvTime.adapter as AdapterStaticsTime).setData(vm.timeValuesList[position]) | |||
| } | |||
| } | |||
| } else { | |||
| // if (!vm.isInitStatisticsResponse()) { | |||
| // vm.getStatistics().observe(this) { | |||
| // if (it) { | |||
| // (binding.rvTime.adapter as AdapterStaticsTime).setData(vm.timeValuesList[position]) | |||
| // } | |||
| // } | |||
| // } else { | |||
| (binding.rvTime.adapter as AdapterStaticsTime).setData(vm.timeValuesList[position]) | |||
| } | |||
| // } | |||
| } | |||
| } | |||
| override fun loadData() { | |||
| //设置选中加载数据 | |||
| vm.timeStatisticsPositionLiveData.value = binding.tabLayoutTime.selectedTabPosition | |||
| // vm.timeStatisticsPositionLiveData.value = binding.tabLayoutTime.selectedTabPosition | |||
| vm.getStatistics().observe(this) { | |||
| if (it) { | |||
| (binding.rvTime.adapter as AdapterStaticsTime).setData(vm.timeValuesList[binding.tabLayoutTime.selectedTabPosition]) | |||
| } | |||
| } | |||
| } | |||
| private fun initTabLayout() { | |||
| @@ -55,7 +55,11 @@ open class StatisticsTimeTopFragmentViewModel : BaseViewModel() { | |||
| protected fun initTimeStaticItem(statistics : AppApi.Statistics) : MutableList<TimeStatisticItem> { | |||
| val result = mutableListOf<TimeStatisticItem>() | |||
| //有效学习时长 | |||
| result.add(initValideTime(statistics)) //添加有效学习时常 | |||
| result.add(initEfficiency(statistics)) //效率 | |||
| result.add(initCourseCount(statistics)) //课程 | |||
| result.add(initLearnProgress(statistics)) //已学进度 | |||
| /* //有效学习时长 | |||
| val timeStaticItem_1 = TimeStatisticItem().apply { | |||
| name = "有效学习时长" | |||
| backGround = R.drawable.shape_rounder_4_red_a5 | |||
| @@ -83,9 +87,10 @@ open class StatisticsTimeTopFragmentViewModel : BaseViewModel() { | |||
| } | |||
| } | |||
| }else{ | |||
| timeStaticItem_1.time = "0" | |||
| timeStaticItem_1.unit = "" | |||
| timeStaticItem_1.time = "" | |||
| timeStaticItem_1.unit = "不足1分钟" | |||
| } | |||
| initIncr(timeStaticItem_1,statistics.sdIncr.toDouble()) | |||
| //增量不为0 | |||
| if (statistics.sdIncr != 0L ){ | |||
| val hour_1 = statistics.sdIncr / 3600000.0 | |||
| @@ -99,11 +104,15 @@ open class StatisticsTimeTopFragmentViewModel : BaseViewModel() { | |||
| //小于1分钟 秒数 | |||
| else -> timeStaticItem_1.incr = "${sencond}秒" | |||
| } | |||
| if (statistics.sdIncr > 0){ | |||
| timeStaticItem_1.incr = "+${timeStaticItem_1.incr}" | |||
| } | |||
| }else{ | |||
| timeStaticItem_1.incr = "" | |||
| } | |||
| initIncr(timeStaticItem_1,statistics.sdIncr.toDouble()) | |||
| result.add(timeStaticItem_1) | |||
| result.add(timeStaticItem_1)*/ | |||
| //综合学习效率 | |||
| /* //综合学习效率 | |||
| val timeStaticItem_2 = TimeStatisticItem().apply { | |||
| backGround = R.drawable.shape_rounder_4_theme_a5 | |||
| name = "综合学习效率" | |||
| @@ -131,20 +140,128 @@ open class StatisticsTimeTopFragmentViewModel : BaseViewModel() { | |||
| time = initShow(statistics.ts) | |||
| initIncr(this,statistics.tsIncr) | |||
| } | |||
| result.add(timeStaticItem_4) | |||
| result.add(timeStaticItem_4)*/ | |||
| return result | |||
| } | |||
| private fun initValideTime(statistics : AppApi.Statistics) : TimeStatisticItem { | |||
| val result = TimeStatisticItem().apply { | |||
| name = "有效学习时长" | |||
| backGround = R.drawable.shape_rounder_4_red_a5 | |||
| //时长显示 | |||
| if (statistics.sd != 0L) { | |||
| val hour = statistics.sd / 3600000.0 | |||
| val minute = statistics.sd / 60000.0 | |||
| when { | |||
| //大于1小时 | |||
| hour > 1 -> { | |||
| time =initShow(hour) | |||
| unit = "小时" | |||
| } | |||
| //大于1分钟 | |||
| minute > 1 -> { | |||
| //取整和保留一位小数的值相同,则使用取整的值显示,否则使用保留小数的值显示 | |||
| time =initShow(minute) | |||
| unit = "分钟" | |||
| } | |||
| //小于1分钟 | |||
| else -> { | |||
| time = "" | |||
| unit = "不足1分钟" | |||
| } | |||
| } | |||
| }else{ | |||
| time = "" | |||
| unit = "不足1分钟" | |||
| } | |||
| //增量处理 | |||
| incr = if (statistics.sdIncr != 0L ) { | |||
| val hour_1 = statistics.sdIncr / 3600000.0 | |||
| val minute_1 = statistics.sdIncr / 60000.0 | |||
| val sencond = statistics.sdIncr / 1000 | |||
| val temp : String = when { | |||
| //大于1小时 | |||
| abs(hour_1) > 1 -> if(statistics.sdIncr > 0) "${initShow(hour_1)}小时" else "-${initShow(abs(hour_1))}小时" | |||
| //大于1分钟 | |||
| abs(minute_1) > 1 ->if(statistics.sdIncr > 0) "${initShow(minute_1)}分钟" else "-${initShow(abs(minute_1))}分钟" | |||
| //小于1分钟 秒数 | |||
| else -> "${sencond}秒" | |||
| } | |||
| if (statistics.sdIncr > 0){ | |||
| "+${temp}" | |||
| } else temp | |||
| } else "" | |||
| //增量颜色和箭头处理 | |||
| initIncr(this,statistics.sdIncr.toDouble()) | |||
| } | |||
| return result | |||
| } | |||
| private fun initEfficiency(statistics : AppApi.Statistics) : TimeStatisticItem { | |||
| val result = TimeStatisticItem().apply { | |||
| backGround = R.drawable.shape_rounder_4_theme_a5 | |||
| name = "综合学习效率" | |||
| unit = "%" | |||
| time = initShow(statistics.se) | |||
| initIncr(this,statistics.seIncr) | |||
| //增量 | |||
| incr = when { | |||
| statistics.seIncr == 0.0 -> "" | |||
| statistics.seIncr > 0 -> "+${initShow(statistics.seIncr)}$unit" | |||
| else -> "${initShow(statistics.seIncr)}$unit" | |||
| } | |||
| } | |||
| return result | |||
| } | |||
| private fun initCourseCount(statistics : AppApi.Statistics) : TimeStatisticItem { | |||
| val result = TimeStatisticItem().apply { | |||
| backGround = R.drawable.shape_rounder_4_green_a5 | |||
| name = "已学课程" | |||
| unit = "/${CourseManager.getTotalCourseSize()}个" | |||
| time = "${statistics.sc}" | |||
| initIncr(this,statistics.scIncr.toDouble()) | |||
| //增量 | |||
| incr = when { | |||
| statistics.scIncr == 0L -> "" | |||
| statistics.scIncr > 0 -> "+${statistics.scIncr}" | |||
| else -> "${statistics.scIncr}" | |||
| } | |||
| } | |||
| return result | |||
| } | |||
| private fun initLearnProgress(statistics : AppApi.Statistics) : TimeStatisticItem { | |||
| val result = TimeStatisticItem().apply { | |||
| backGround = R.drawable.shape_rounder_4_purple_a5 | |||
| name = "已学进度" | |||
| unit = "%" | |||
| time = initShow(statistics.ts) | |||
| initIncr(this,statistics.tsIncr) | |||
| //增量 | |||
| incr = when { | |||
| statistics.tsIncr == 0.0 -> "" | |||
| statistics.tsIncr > 0 -> "+${initShow(statistics.tsIncr)}$unit" | |||
| else -> "${initShow(statistics.tsIncr)}$unit" | |||
| } | |||
| } | |||
| return result | |||
| } | |||
| /** 格式化数据,决定显示小数还是不显示 */ | |||
| private fun initShow(value:Double):String{ | |||
| val temp = abs(value) | |||
| //保留一位小数 向下取舍 | |||
| val formatFloor = NumberUtils.formatFloor(value, "0.0") | |||
| val formatFloor = NumberUtils.formatFloor(temp, "0.0") | |||
| val toDouble = formatFloor.toDouble() | |||
| //取整 | |||
| val formatFloor_1 = NumberUtils.formatFloor(value, "0") | |||
| val toDouble_1 = formatFloor.toDouble() | |||
| val formatFloor_1 = NumberUtils.formatFloor(temp, "0") | |||
| val toDouble_1 = formatFloor_1.toDouble() | |||
| //取整和保留一位小数的值相同,则使用取整的值显示,否则使用保留小数的值显示 | |||
| return when (toDouble_1) { | |||
| return (if (value < 0 ) "-" else "") + when (toDouble_1) { | |||
| toDouble -> formatFloor_1 | |||
| else -> formatFloor | |||
| } | |||
| @@ -154,14 +271,10 @@ open class StatisticsTimeTopFragmentViewModel : BaseViewModel() { | |||
| private fun initIncr(timeStaticItem_1 : TimeStatisticItem, sdIncr : Double) { | |||
| timeStaticItem_1.run { | |||
| when{ | |||
| sdIncr == 0.0 -> { | |||
| incr = "" | |||
| imgDrawable = R.drawable.ic_keep | |||
| } | |||
| sdIncr == 0.0 -> imgDrawable = R.drawable.ic_keep | |||
| sdIncr > 0 -> { | |||
| imgDrawable = R.drawable.ic_rise | |||
| incrTextColor = R.color.green_1 | |||
| incr = "+$incr" | |||
| } | |||
| else -> { | |||
| imgDrawable = R.drawable.ic_decline | |||
| @@ -18,6 +18,7 @@ import com.xkl.cdl.module.m_memo.MemoFragment | |||
| import com.xkl.cdl.module.m_my.MyFragment | |||
| import com.xkl.cdl.module.m_service_center.ServiceCenterFragment | |||
| import com.xkl.cdl.module.m_statics.StaticsFragment | |||
| import com.xkl.cdl.module.splash.SplashActivity | |||
| import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX | |||
| import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly | |||
| @@ -41,7 +42,11 @@ class MainActivity : BaseActivityVM<ActivityMainBinding, MainActivityViewModel>( | |||
| override fun initViewModel(): MainActivityViewModel { | |||
| return ViewModelProvider(this)[MainActivityViewModel::class.java] | |||
| } | |||
| override fun protectApp() { | |||
| startActivity(SplashActivity::class.java) | |||
| finish() | |||
| } | |||
| override fun initActivity(savedInstanceState: Bundle?) { | |||
| //点击事件 | |||
| @@ -25,12 +25,13 @@ import java.util.concurrent.TimeUnit | |||
| @SuppressLint("CustomSplashScreen") | |||
| class SplashActivity : BaseActivity<ActivitySplashBinding>() { | |||
| override fun onCreate(savedInstanceState : Bundle?) { | |||
| override fun onCreateOwn(savedInstanceState : Bundle?) { | |||
| if (!isTaskRoot) { | |||
| finish() | |||
| return | |||
| } | |||
| super.onCreate(savedInstanceState) | |||
| XKLApplication.instance().isLaunch = true | |||
| super.onCreateOwn(savedInstanceState) | |||
| } | |||
| override fun initActivity(savedInstanceState : Bundle?) { | |||
| @@ -0,0 +1,66 @@ | |||
| package com.suliang.common.base | |||
| import android.app.Application | |||
| import android.database.sqlite.SQLiteDatabase | |||
| import com.suliang.common.util.LogUtil | |||
| import com.tencent.mmkv.MMKV | |||
| import io.reactivex.rxjava3.exceptions.UndeliverableException | |||
| import io.reactivex.rxjava3.functions.Consumer | |||
| import io.reactivex.rxjava3.plugins.RxJavaPlugins | |||
| import java.io.IOException | |||
| import java.util.* | |||
| import kotlin.properties.Delegates | |||
| /** | |||
| * @Author: suliang | |||
| * @create: 2022/8/23 11:07 | |||
| * @Description: | |||
| */ | |||
| open class LibApplication : Application(){ | |||
| //是否启动标记,用于避免应用重启时的问题 | |||
| var isLaunch = false | |||
| companion object { | |||
| protected var instance : LibApplication by Delegates.notNull() | |||
| @JvmStatic | |||
| fun instance() = instance | |||
| } | |||
| override fun onCreate() { | |||
| super.onCreate() | |||
| instance = this | |||
| //初始MMKV存储 | |||
| val rootDir = MMKV.initialize(this) | |||
| LogUtil.e(rootDir) | |||
| setRxJavaErrorHandler() | |||
| // HookMobileCache().hook() | |||
| // registerActivityLifecycleCallbacks(lifecycleCallback) | |||
| } | |||
| /*** | |||
| * 避免 调用多次onError。正常来说第一次onError会走正常的Observer处理,其他会走ErrorHandler。通过此方法捕捉多次的error | |||
| */ | |||
| private fun setRxJavaErrorHandler(){ | |||
| RxJavaPlugins.setErrorHandler(Consumer { e -> | |||
| e.printStackTrace() | |||
| LogUtil.e( "RxJavaErrorHandler --> \n $e") | |||
| if (e is UndeliverableException) { | |||
| return@Consumer | |||
| } else if (e is IOException) { | |||
| // fine, irrelevant network problem or API that throws on cancellation | |||
| return@Consumer | |||
| } else if (e is InterruptedException) { | |||
| // fine, some blocking code was interrupted by a dispose call | |||
| return@Consumer | |||
| } else if (e is NullPointerException || e is IllegalArgumentException) { | |||
| // that's likely a bug in the application | |||
| Thread.currentThread().uncaughtExceptionHandler.uncaughtException(Thread.currentThread(), e) | |||
| return@Consumer | |||
| } else if (e is IllegalStateException) { | |||
| // that's a bug in RxJava or in a custom operator | |||
| Thread.currentThread().uncaughtExceptionHandler.uncaughtException(Thread.currentThread(), e) | |||
| return@Consumer | |||
| } | |||
| LogUtil.e( "RxJavaErrorHandler --> unknown exception = \n $e") | |||
| }) | |||
| } | |||
| } | |||
| @@ -1,10 +1,12 @@ | |||
| package com.suliang.common.base.activity | |||
| import android.content.ComponentName | |||
| import android.content.Intent | |||
| import android.graphics.Color | |||
| import android.os.Bundle | |||
| import android.widget.Toast | |||
| import androidx.annotation.StringRes | |||
| import com.suliang.common.base.LibApplication | |||
| import com.suliang.common.base.LoadingDialog | |||
| import com.suliang.common.base.ViewBehavior | |||
| import com.suliang.common.util.ActivityStackManager | |||
| @@ -20,6 +22,14 @@ abstract class UIBaseActivity : LifecycleLogActivity(), ViewBehavior { | |||
| override fun onCreate(savedInstanceState : Bundle?) { | |||
| super.onCreate(savedInstanceState) | |||
| onCreateOwn(savedInstanceState) | |||
| } | |||
| protected open fun onCreateOwn(savedInstanceState : Bundle?) { | |||
| if (!LibApplication.instance().isLaunch) { | |||
| protectApp() | |||
| return | |||
| } | |||
| //入栈 | |||
| ActivityStackManager.addActivity(this) | |||
| initFirst() | |||
| @@ -27,6 +37,21 @@ abstract class UIBaseActivity : LifecycleLogActivity(), ViewBehavior { | |||
| loadData() | |||
| } | |||
| /** | |||
| * 当Application启动的时候, LibApplication的isLaunch为false | |||
| * 在SplashActivity启动的时候改变该值为true | |||
| * 当为true时,app回到前台,即 应用没有重启 | |||
| * 当为false时,即应用重启了 | |||
| */ | |||
| protected open fun protectApp() { | |||
| startActivity(Intent().apply { | |||
| component = ComponentName(applicationContext, "com.xkl.cdl.module.main.MainActivity") | |||
| addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) //清空栈里MainActivity之上的所有Activity | |||
| }) | |||
| finish() | |||
| } | |||
| /*** | |||
| * 实例化binding, | |||
| */ | |||
| @@ -86,10 +111,9 @@ abstract class UIBaseActivity : LifecycleLogActivity(), ViewBehavior { | |||
| val showTime = if (event.showLong) Toast.LENGTH_LONG else Toast.LENGTH_LONG | |||
| event.content?.let { | |||
| Toast.makeText(this, it, showTime).show() | |||
| } ?: let { | |||
| Toast.makeText(this, event.contentResId!!, showTime).show() | |||
| } | |||
| ?: let { | |||
| Toast.makeText(this, event.contentResId!!, showTime).show() | |||
| } | |||
| } | |||
| protected fun showToast(text : String, showLong : Boolean = false) { | |||
| @@ -108,11 +132,10 @@ abstract class UIBaseActivity : LifecycleLogActivity(), ViewBehavior { | |||
| if (findFragment is LoadingDialog) { | |||
| findFragment.dialog?.show() | |||
| } | |||
| } ?: let { | |||
| val loadingDialog = LoadingDialog.newInstance("") | |||
| loadingDialog.show(supportFragmentManager, "loading") | |||
| } | |||
| ?: let { | |||
| val loadingDialog = LoadingDialog.newInstance("") | |||
| loadingDialog.show(supportFragmentManager, "loading") | |||
| } | |||
| } else { //关闭 | |||
| findFragment?.let { | |||
| if (it is LoadingDialog) { | |||
| @@ -40,7 +40,7 @@ object LiveDataBus { | |||
| if (isSticky){ | |||
| bus[key] = MutableLiveData() | |||
| }else{ | |||
| bus[key] = NonStickyMutableLivedata() | |||
| bus[key] = SingleLiveData() | |||
| } | |||
| } | |||
| return bus[key] as MutableLiveData<T> | |||
| @@ -1,81 +0,0 @@ | |||
| package com.suliang.common.eventbus | |||
| import androidx.lifecycle.LifecycleOwner | |||
| import androidx.lifecycle.LiveData | |||
| import androidx.lifecycle.MutableLiveData | |||
| import androidx.lifecycle.Observer | |||
| import java.lang.Exception | |||
| import java.lang.NullPointerException | |||
| import java.lang.reflect.Method | |||
| /** | |||
| * author suliang | |||
| * create 2022/3/15 10:51 | |||
| * Describe: 非粘性LiveData ,需要先设置监听再发送数据,即后注册的监听收不到以前的监听 | |||
| * 事件总线的LiveData 非粘性事件传false(刚注册时如果有事件则不发送) 粘性事件传true | |||
| * @param isSticky false非粘性事件 true粘性事件 | |||
| */ | |||
| internal class NonStickyMutableLivedata<T> : MutableLiveData<T>() { | |||
| // override fun observe(owner : LifecycleOwner, observer : Observer<in T>) { | |||
| // super.observe(owner, ObserverWrapper<T>(observer)) | |||
| // } | |||
| // | |||
| // class ObserverWrapper<T>(var observer:Observer<in T>) : Observer<T> { | |||
| // var isSticky = false | |||
| // override fun onChanged(t : T) { | |||
| // if (isSticky){ | |||
| // observer.onChanged(t) | |||
| // }else{ | |||
| // isSticky = true | |||
| // } | |||
| // } | |||
| // } | |||
| override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { | |||
| super.observe(owner, observer) | |||
| hook(observer) | |||
| } | |||
| override fun removeObserver(observer : Observer<in T>) { | |||
| super.removeObserver(observer) | |||
| } | |||
| private fun hook(observer : Observer<in T>) = try { | |||
| //得到 mLastVersion | |||
| //获取到LiveData的类中的mObserver对象 | |||
| // SafeIterableMap<Observer<? super T> , ObserverWrapper> mObservers | |||
| val liveDataClass = LiveData::class.java | |||
| val declaredField = liveDataClass.getDeclaredField("mObservers") | |||
| declaredField.isAccessible = true | |||
| //获取到这个成员变量的对象 | |||
| val mObserVersObject = declaredField.get(this) | |||
| //得到map对应的class对象 | |||
| val mObserversClass = mObserVersObject.javaClass | |||
| //获取到mObservers对象的get方法 | |||
| val get : Method = mObserversClass.getDeclaredMethod("get", Any::class.java) | |||
| get.isAccessible = true | |||
| //执行get方法,获取到对象 | |||
| val invokeEntry = get.invoke(mObserVersObject, observer) | |||
| //定义一个空的对象 | |||
| val observerWrapper = if (invokeEntry is Map.Entry<*, *>) { | |||
| invokeEntry.value | |||
| } else throw NullPointerException("observerWrapper is null") | |||
| //得到observerWrapper的对象,编译擦除问题会引起多态冲突,所以用getSupperClass | |||
| //getClass 返回对应的当前正在运行是的类所对应的 | |||
| val mLastVersion = observerWrapper!!::class.java.superclass.getDeclaredField("mLastVersion") | |||
| mLastVersion.isAccessible = true | |||
| //得到mVersion | |||
| val mVersion = liveDataClass.getDeclaredField("mVersion") | |||
| mVersion.isAccessible = true | |||
| //把mVersion数据填入到mLastVersion中 | |||
| val mVersionValue = mVersion.get(this) | |||
| mLastVersion.set(observerWrapper, mVersionValue) | |||
| } catch (e : Exception) { | |||
| e.printStackTrace() | |||
| } | |||
| } | |||
| @@ -0,0 +1,97 @@ | |||
| package com.suliang.common.eventbus | |||
| import androidx.lifecycle.LifecycleOwner | |||
| import androidx.lifecycle.LiveData | |||
| import androidx.lifecycle.MutableLiveData | |||
| import androidx.lifecycle.Observer | |||
| import java.lang.Exception | |||
| import java.lang.NullPointerException | |||
| import java.lang.reflect.Method | |||
| import java.util.concurrent.atomic.AtomicBoolean | |||
| /** | |||
| * author suliang | |||
| * create 2022/3/15 10:51 | |||
| * Describe: https://blog.csdn.net/bzb123321/article/details/98210342 | |||
| */ | |||
| class SingleLiveData<T> : MutableLiveData<T>() { | |||
| /*非粘性LiveData ,需要先设置监听再发送数据,即后注册的监听收不到以前的监听 | |||
| * 事件总线的LiveData 非粘性事件传false(刚注册时如果有事件则不发送) 粘性事件传true | |||
| * @param isSticky false非粘性事件 true粘性事件*/ | |||
| // override fun observe(owner : LifecycleOwner, observer : Observer<in T>) { | |||
| // super.observe(owner, ObserverWrapper<T>(observer)) | |||
| // } | |||
| // | |||
| // class ObserverWrapper<T>(var observer:Observer<in T>) : Observer<T> { | |||
| // var isSticky = false | |||
| // override fun onChanged(t : T) { | |||
| // if (isSticky){ | |||
| // observer.onChanged(t) | |||
| // }else{ | |||
| // isSticky = true | |||
| // } | |||
| // } | |||
| // } | |||
| companion object{ | |||
| private val mPending : AtomicBoolean = AtomicBoolean(false) | |||
| } | |||
| override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { | |||
| super.observe(owner, Observer { | |||
| if (mPending.compareAndSet(true,false)){ | |||
| observer.onChanged(it) | |||
| } | |||
| }) | |||
| // hook(observer) | |||
| } | |||
| override fun setValue(value : T?) { | |||
| mPending.set(true) | |||
| super.setValue(value) | |||
| } | |||
| public fun call(){ | |||
| setValue(null) | |||
| } | |||
| // override fun removeObserver(observer : Observer<in T>) { | |||
| // super.removeObserver(observer) | |||
| // } | |||
| // private fun hook(observer : Observer<in T>) = try { | |||
| // //得到 mLastVersion | |||
| // //获取到LiveData的类中的mObserver对象 | |||
| // // SafeIterableMap<Observer<? super T> , ObserverWrapper> mObservers | |||
| // val liveDataClass = LiveData::class.java | |||
| // val declaredField = liveDataClass.getDeclaredField("mObservers") | |||
| // declaredField.isAccessible = true | |||
| // //获取到这个成员变量的对象 | |||
| // val mObserVersObject = declaredField.get(this) | |||
| // //得到map对应的class对象 | |||
| // val mObserversClass = mObserVersObject.javaClass | |||
| // //获取到mObservers对象的get方法 | |||
| // val get : Method = mObserversClass.getDeclaredMethod("get", Any::class.java) | |||
| // get.isAccessible = true | |||
| // //执行get方法,获取到对象 | |||
| // val invokeEntry = get.invoke(mObserVersObject, observer) | |||
| // //定义一个空的对象 | |||
| // val observerWrapper = if (invokeEntry is Map.Entry<*, *>) { | |||
| // invokeEntry.value | |||
| // } else throw NullPointerException("observerWrapper is null") | |||
| // | |||
| // //得到observerWrapper的对象,编译擦除问题会引起多态冲突,所以用getSupperClass | |||
| // //getClass 返回对应的当前正在运行是的类所对应的 | |||
| // val mLastVersion = observerWrapper!!::class.java.superclass.getDeclaredField("mLastVersion") | |||
| // mLastVersion.isAccessible = true | |||
| // //得到mVersion | |||
| // val mVersion = liveDataClass.getDeclaredField("mVersion") | |||
| // mVersion.isAccessible = true | |||
| // //把mVersion数据填入到mLastVersion中 | |||
| // val mVersionValue = mVersion.get(this) | |||
| // mLastVersion.set(observerWrapper, mVersionValue) | |||
| // | |||
| // } catch (e : Exception) { | |||
| // e.printStackTrace() | |||
| // } | |||
| } | |||