问题总结
问题1:OPPO手机默认关闭 设置===通知与状态栏===通知管理===APP名称打开通知
问题2:怎么加入接口不熟悉 activity/fragment
问题3:在不熟悉逻辑的情况下,在什么地方加入逻辑
问题4:Kotlin 、dagge 不够熟悉
问题5:前期准备不充分,浪费了很多时间,不明确时间节点
尝试过的错误方法
错误方法1:HomeFragment 处理逻辑;问题:如果从界面Fragment到注册界面回来,没弹出
错误方法2:登录过后,方法拦截; 问题:如果输入账号,验证码后就会弹出Toast
正确的方法(◔ ‸◔? (⊙.⊙)):
依次往下调用 SetPasswordActivity SetPasswordViewModel SetPasswordPresenter AccountDataService---AccountDataManagerImpl对比代码(无用)
data class ToastModel( val content: String?, val type: String? )结构
SetPasswordActivity.kt @Route(path = ARouters.Account.SET_PASSWORD) class SetPasswordActivity : InjectedActivity(R.layout.activity_set_password) { @Inject lateinit var viewModel: SetPasswordViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initView() } private fun initView() { // 提交按钮 btn_submit.clickThrottle { viewModel.setPassword(ket_password.text.toString(), ket_invite_code?.text?.toString()) } } } @Keep data class ToastEntity( @SerializedName("content") var content: String? = null, @SerializedName("type") var type: String? = null ):Parcelable{ constructor(parcel: Parcel) : this( parcel.readString(), parcel.readString() ) override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeString(content) parcel.writeString(type) } override fun describeContents(): Int { return 0 } companion object CREATOR : Parcelable.Creator<ToastEntity> { override fun createFromParcel(parcel: Parcel): ToastEntity { return ToastEntity(parcel) } override fun newArray(size: Int): Array<ToastEntity?> { return arrayOfNulls(size) } } } SetPasswordViewModel.kt fun setPassword(password: String, inviteCode: String?) = launch { 。。。 .onSuccess { accountDataService?.getToastContent() } 。。。 } AuthService.kt interface AuthService { /** * 获取toast 列表 */ @GET("/message/toast/list/") suspend fun getTokenStatus(): Response.List<ToastEntity> } AccountDataManagerImpl.kt @Suppress("SENSELESS_COMPARISON", "USELESS_IS_CHECK") @Route(path = ARouters.Account.ACCOUNT_DATA_SERVICE, name = "账户数据服务") class AccountDataManagerImpl : AccountDataManager, AccountDataService, BaseDataServiceImpl<AuthService>() { override fun getToastContent() { safetyLaunch { delay(2000) safeApiCall { service.getTokenStatus() }.onSuccess { if (it?.list.isNullOrEmpty()) { return@onSuccess } info(it?.list?.get(0)?.content) }.onFailure { it.handle() } } } } AccountDataService.kt.api interface AccountDataService : IProvider { /** * 获取toast 列表 */ fun getToastContent() }
关键代码详细解释
Coroutine 是轻量级的线程
使用 Coroutine 必须要先创建一个对应的 CoroutineScope
一般而言,在应用中具有生命周期的组件应该实现 CoroutineScope 接口,并负责该组件内 Coroutine 的创建和管理
为了方便创建 Coroutine,在 CoroutineScope 上有很多扩展函数,比如 launch、async、actor、cancel 等
GlobalScope 是 CoroutineScope 的一个单例实现
Job 代表 launch 创建的一个 Coroutine 实例即可,通过这个 job 对象可以控制这个 Coroutine 实例,比如调用 cancel 函数可以取消执行
suspend 修饰符,它可以告诉编译器,该函数需要在协程中执行
/** * 安全启动. * @param block suspend CoroutineScope.() -> Unit * @return JobWrapper */ fun safetyLaunch(block: suspend CoroutineScope.() -> Unit): JobWrapper { val jobWrapper = JobWrapper() jobWrapper.job = GlobalScope.launch(CoroutinesDispatchers.ui + rootJob) { runCatching { block.invoke(this) }.onFailure { jobWrapper.onFailure?.invoke(it) ?: Logger.e(it, "safetyLaunch") } } return jobWrapper }
延迟2s
delay(2000)异常捕获
/** * 封装api请求,并进行异常捕捉处理. * @param call suspend () -> Response<T> * @return Result<T> */ suspend fun <T : Any> safeApiCall(call: suspend () -> Response<T>): Result<T> = try { Result.Success(call.invoke().unwrap()) } catch (e: Throwable) { Result.Error(e) }list为空判断
if (it?.list.isNullOrEmpty()) { return@onSuccess } info(it?.list?.get(0)?.content) }