Code Monkey home page Code Monkey logo

hegaojian / jetpackmvvm Goto Github PK

View Code? Open in Web Editor NEW
3.0K 45.0 592.0 81.68 MB

:chicken::basketball:一个Jetpack结合MVVM的快速开发框架,基于MVVM模式集成谷歌官方推荐的JetPack组件库:LiveData、ViewModel、Lifecycle、Navigation组件 使用Kotlin语言,添加大量拓展函数,简化代码 加入Retrofit网络请求,协程,帮你简化各种操作,让你快速开发项目

Home Page: https://github.com/hegaojian/JetpackMvvm

License: Apache License 2.0

Kotlin 88.60% Java 11.40%
mvvm jetpack viewmodel lifecycle databinding livedata retrofit kotlin okhttp mvvm-jetpack

jetpackmvvm's Introduction

Platform GitHub license GitHub license

🐔🐔🐔JetPackMvvm

  • 基于MVVM模式集成谷歌官方推荐的JetPack组件库:LiveData、ViewModel、Lifecycle、Navigation组件
  • 使用kotlin语言,添加大量拓展函数,简化代码
  • 加入Retrofit网络请求,协程,帮你简化各种操作,让你快速请求网络

演示Demo

已用该库重构了我之前的玩安卓项目,利用Navigation组件以单Activity+Fragment架构编写,优化了很多代码,对比之前的mvp项目,开发效率与舒适度要提高了不少,想看之前MVP的项目可以去 https://github.com/hegaojian/WanAndroid

效果图展示

项目效果图

APK下载:

1.如何集成

  • 1.1 在root's build.gradle中加入Jitpack仓库
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
  • 1.2 在app's build.gradle中添加依赖
dependencies {
  ...
  implementation 'com.github.hegaojian:JetpackMvvm:1.2.7'
}
  • 1.3 在app's build.gradle中,android 模块下按需开启DataBinding与ViewBinding
AndroidStudio 4.0 以下版本------>
android {
    ...
    dataBinding {
        enabled = true 
    }
    viewBinding {
        enabled = true
    }
}

AndroidStudio 4.0及以上版本 ------>
android {
    ...
   buildFeatures {
        dataBinding = true
        viewBinding = true
    }
}
 

2.继承基类

一般我们项目中都会有一套自己定义的符合业务需求的基类 BaseActivity/BaseFragment,所以我们的基类需要继承本框架的Base类

  • 不想用Databinding与ViewBinding-------可以继承 BaseVmActivity/BaseVmFragment
  • 用Databinding-----------可以继承BaseVmDbActivity/BaseVmDbFragment**
  • 用Viewbinding-----------可以继承BaseVmVbActivity/BaseVmVbFragment**

Activity:

abstract class BaseActivity<VM : BaseViewModel, DB : ViewDataBinding> : BaseVmDbActivity<VM, DB>() {
     /**
     * 当前Activity绑定的视图布局Id abstract修饰供子类实现
     */
    abstract override fun layoutId(): Int
    /**
     * 当前Activityc创建后调用的方法 abstract修饰供子类实现
     */
    abstract override fun initView(savedInstanceState: Bundle?)

    /**
     * 创建liveData数据观察
     */
    override override fun createObserver()


    /**
     * 打开等待框 在这里实现你的等待框展示
     */
    override fun showLoading(message: String) {
       ...
    }

    /**
     * 关闭等待框 在这里实现你的等待框关闭
     */
    override fun dismissLoading() {
       ...
    }
}

Fragment:

abstract class BaseFragment<VM : BaseViewModel,DB:ViewDataBinding> : BaseVmDbFragment<VM,DB>() {
   
    abstract override fun initView(savedInstanceState: Bundle?)

    /**
     * 懒加载 只有当前fragment视图显示时才会触发该方法 abstract修饰供子类实现
     */
    abstract override fun lazyLoadData()

    /**
     * 创建liveData数据观察 懒加载之后才会触发
     */
    override override fun createObserver()
  
    /**
     * Fragment执行onViewCreated后触发的方法 
     */
    override fun initData() {

    }
    
   /**
     * 打开等待框 在这里实现你的等待框展示
     */
    override fun showLoading(message: String) {
       ...
    }

    /**
     * 关闭等待框 在这里实现你的等待框关闭
     */
    override fun dismissLoading() {
       ...
    }
}

3.编写一个登录功能

  • 3.1 创建LoginViewModel类继承BaseViewModel
class LoginViewModel : BaseViewModel() {
  
}
  • 3.2 创建LoginFragment 继承基类传入相关泛型,第一个泛型为你创建的LoginViewModel,第二个泛型为ViewDataBind,保存fragment_login.xml后databinding会生成一个FragmentLoginBinding类。(如果没有生成,试着点击Build->Clean Project)
class LoginFragment : BaseFragment<LoginViewModel, FragmentLoginBinding>() {
    
    /**
     *  初始化操作
     */
    override fun initView(savedInstanceState: Bundle?) {
        ...
    }
    
    /**
     *  fragment 懒加载
     */
    override fun lazyLoadData() { 
        ...
    }
}

4.网络请求(Retrofit+协程)

  • 4.1 新建请求配置类继承 BaseNetworkApi 示例:
class NetworkApi : BaseNetworkApi() {

   companion object {
         
        val instance: NetworkApi by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { NetworkApi() }

        //双重校验锁式-单例 封装NetApiService 方便直接快速调用
        val service: ApiService by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            instance.getApi(ApiService::class.java, ApiService.SERVER_URL)
        }
    }
   
    /**
     * 实现重写父类的setHttpClientBuilder方法,
     * 在这里可以添加拦截器,可以对 OkHttpClient.Builder 做任意你想要做的*操作
     */
    override fun setHttpClientBuilder(builder: OkHttpClient.Builder): OkHttpClient.Builder {
        builder.apply {
            //示例:添加公共heads,可以存放token,公共参数等, 注意要设置在日志拦截器之前,不然Log中会不显示head信息
            addInterceptor(MyHeadInterceptor())
            // 日志拦截器
            addInterceptor(LogInterceptor())
            //超时时间 连接、读、写
            connectTimeout(10, TimeUnit.SECONDS)
            readTimeout(5, TimeUnit.SECONDS)
            writeTimeout(5, TimeUnit.SECONDS)
        }
        return builder
    }

    /**
     * 实现重写父类的setRetrofitBuilder方法,
     * 在这里可以对Retrofit.Builder做任意*操作,比如添加GSON解析器,protobuf等
     */
    override fun setRetrofitBuilder(builder: Retrofit.Builder): Retrofit.Builder {
        return builder.apply {
            addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
            addCallAdapterFactory(CoroutineCallAdapterFactory())
        }
    }
}
  • 4.2如果你请求服务器返回的数据有基类(没有可忽略这一步)例如:
{
    "data": ...,
    "errorCode": 0,
    "errorMsg": ""
}

该示例格式是 玩Android Api返回的数据格式,如果errorCode等于0 请求成功,否则请求失败 作为开发者的角度来说,我们主要是想得到脱壳数据-data,且不想每次都判断errorCode==0请求是否成功或失败 这时我们可以在服务器返回数据基类中继承BaseResponse,实现相关方法:

data class ApiResponse<T>(var errorCode: Int, var errorMsg: String, var data: T) : BaseResponse<T>() {

    // 这里是示例,wanandroid 网站返回的 错误码为 0 就代表请求成功,请你根据自己的业务需求来编写
    override fun isSucces() = errorCode == 0

    override fun getResponseCode() = errorCode

    override fun getResponseData() = data

    override fun getResponseMsg() = errorMsg

}
  • 4.3 在ViewModel中发起请求,所有请求都是在viewModelScope中启动,请求会发生在IO线程,最终回调在主线程上,当页面销毁的时候,请求会统一取消,不用担心内存泄露的风险,框架做了2种请求使用方式

1、将请求数据包装给ResultState,在Activity/Fragment中去监听ResultState拿到数据做处理

class RequestLoginViewModel: BaseViewModel {

  //自动脱壳过滤处理请求结果,自动判断结果是否成功
    var loginResult = MutableLiveData<ResultState<UserInfo>>()
    
  //不用框架帮脱壳
    var loginResult2 = MutableLiveData<ResultState<ApiResponse<UserInfo>>>()
    
  fun login(username: String, password: String){
   //1.在 Activity/Fragment的监听回调中拿到已脱壳的数据(项目有基类的可以用)
        request(
            { HttpRequestCoroutine.login(username, password) }, //请求体
            loginResult,//请求的结果接收者,请求成功与否都会改变该值,在Activity或fragment中监听回调结果,具体可看loginActivity中的回调
            true,//是否显示等待框,,默认false不显示 可以默认不传
            "正在登录中..."//等待框内容,可以默认不填请求网络中...
        )
        
   //2.在Activity/Fragment中的监听拿到未脱壳的数据,你可以自己根据code做业务需求操作(项目没有基类的可以用)
        requestNoCheck(
          {HttpRequestCoroutine.login(username,password)},
          loginResult2,
          true,
          "正在登录中...") 
}


class LoginFragment : BaseFragment<LoginViewModel, FragmentLoginBinding>() {
    
    private val requestLoginRegisterViewModel: RequestLoginRegisterViewModel by viewModels()
    
    /**
     *  初始化操作
     */
    override fun initView(savedInstanceState: Bundle?) {
        ...
    }
    
    /**
     *  fragment 懒加载
     */
    override fun lazyLoadData() { 
        ...
    }
    
    override fun createObserver(){
      //脱壳
       requestLoginRegisterViewModel.loginResult.observe(viewLifecycleOwner,
            Observer { resultState ->
                parseState(resultState, {
                    //登录成功 打印用户
                    it.username.logd()
                }, {
                    //登录失败(网络连接问题,服务器的结果码不正确...异常都会走在这里)
                    showMessage(it.errorMsg)
                })
            })
    
       //不脱壳
       requestLoginRegisterViewModel.loginResult2.observe(viewLifecycleOwner, Observer {resultState ->
               parseState(resultState,{
                   if(it.errorCode==0){
                       //登录成功 打印用户名
                       it.data.username.logd()
                   }else{
                       //登录失败
                       showMessage(it.errorMsg)
                   }
               },{
                   //请求发生了异常
                   showMessage(it.errorMsg)
               })
           })
   } 
}

2、 直接在当前ViewModel中拿到请求结果

class RequestLoginViewModel : BaseViewModel() {
    
  fun login(username: String, password: String){
   //1.拿到已脱壳的数据(项目有基类的可以用)
     request({HttpRequestCoroutine.login(username,password)},{
             //请求成功 已自动处理了 请求结果是否正常
             it.username.logd()
         },{
             //请求失败 网络异常,或者请求结果码错误都会回调在这里
             it.errorMsg.logd()
         },true,"正在登录中...")
        
   //2.拿到未脱壳的数据,你可以自己根据code做业务需求操作(项目没有基类或者不想框架帮忙脱壳的可以用)
       requestNoCheck({HttpRequestCoroutine.login(username,password)},{
            //请求成功 自己拿到数据做业务需求操作
            if(it.errorCode==0){
                //结果正确
                it.data.username.logd()
            }else{
                //结果错误
                it.errorMsg.logd()
            }
        },{
            //请求失败 网络异常回调在这里
            it.errorMsg.logd()
        },true,"正在登录中...")
}
 

注意:使用该请求方式时需要注意,如果该ViewModel并不是跟Activity/Fragment绑定的泛型ViewModel,而是

val mainViewModel:MainViewModel by viewModels() 或者 val mainViewModel:MainViewModel by activityViewModels() 获取的 如果请求时要弹出loading,你需要在Activity | Fragment中添加以下代码:

addLoadingObserve(viewModel)

4.4 开启打印日志开关

设置全局jetpackMvvmLog变量 是否打开请求日志,默认false不打印,如需要打印日志功能,请设值为 true

5.获取ViewModel

  • 5.1我们的activity/fragment会有多个ViewModel,按传统的写法感觉有点累
 val mainViewModel = ViewModelProvider(this,
            ViewModelProvider.AndroidViewModelFactory(application)).get(MainViewModel::class.java)

**现在官方Ktx有拓展函数可以轻松调用

//在activity中获取当前Activity级别作用域的ViewModel
 private val mainViewModel:MainViewModel by viewModels()
 
//在activity中获取Application级别作用域的ViewModel(注,这个是本框架提供的,Application类继承框架的BaseApp才有用)
 private val mainViewModel by lazy { getAppViewModel<MainViewModel>()}

//在fragment中获取当前Fragment级别作用域的ViewModel
 private val mainViewModel:MainViewModel by viewModels()

//在fragment中获取父类Activity级别作用域的ViewModel
private val mainViewModel:MainViewModel by activityViewModels()

//在fragment中获取Application级别作用域的ViewModel(注,这个是本框架提供的,Application类继承框架的BaseApp才有用)
private val mainViewModel by lazy { getAppViewModel<MainViewModel>()}

6.写了一些常用的拓展函数

 算了不写了,这个不重要,想具体看的话可以在
 me.hgj.jetpackmvvm.ext.util
 me.hgj.jetpackmvvm.ext.view
 的包中看,反正你也可以自己写,按照自己的喜好与需求来

7.混淆

-keep class me.hgj.jetpackmvvm.**{*;}
################ ViewBinding & DataBinding ###############
-keepclassmembers class * implements androidx.viewbinding.ViewBinding {
  public static * inflate(android.view.LayoutInflater);
  public static * inflate(android.view.LayoutInflater, android.view.ViewGroup, boolean);
  public static * bind(android.view.View);
}

联系

  • QQ交流群:419581249

License

 Copyright 2019, hegaojian(何高建)       
  
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at 
 
       http://www.apache.org/licenses/LICENSE-2.0 

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

jetpackmvvm's People

Contributors

doracoin avatar hegaojian avatar iostyle avatar zhushenwudi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jetpackmvvm's Issues

navigation-runtime依赖?

你的项目里
api 'androidx.navigation:navigation-runtime:2.3.0'
但是官方文档里使用的是
api "androidx.navigation:navigation-fragment-ktx:2.3.0"
api "androidx.navigation:navigation-ui-ktx:2.3.0"
这之间有什么区别吗

整个项目写的逻辑清晰,特别是navigation这块以前没用明白过,这里再次感谢作者。

希望和作者讨论一下关于Navigation 的问题

最近项目已经快完工了。但是在这段时间的开发中发现Navigation 在某些需求上会有一些缺陷。

项目内集成了网易云信,作为即时IM的库。网易提供了一个UI库,其中【聊天会话】是一个Activity。这就很尴尬了。

目前使用Navigation 的时候。app基本就是一个MainActivity。然后在上面加了一个mainfragment+viewpager2.

在从mainfragment跳转到【聊天会话Activity】后。如果就只在【聊天会话Activity】一个页面还好。但是出现了一个十分难办的需求。

【聊天会话Activity】 ---> productFragment——>favoriteFragment 这种聊天过程中点击某个对话气泡跳转的具体页面的需求。

productFragment和favoriteFragment都是在MainActivity上面Navigation导航图内的页面。

目前我只能新建了一个【TempActivity+tempFragment】,再在上面弄一个Navigation导航图,把productFragment和favoriteFragment复制一份添加上去。

然后形成了【MainActivity+mainfragment】---->【聊天会话Activity】--->【TempActivity+tempFragment】

这样变扭的形式。

老实说我知道这样的实现很蠢,但是确实遇到了这个难题。

目前只能打算在项目上线之后再慢慢把【聊天会话Activity】用fragment实现,纳入【MainActivity+mainfragment】的体系。

不知道如果是作者你遇到这种情况,会怎么处理

HeadInterceptor 的bug

我正在将此项目改造成适用于我公司业务的项目,在此过程中发现在给HeadInterceptor 传递header时有些问题
image
如图所示,从登录界面到首界面 HeadInterceptor 内方法体被调用时 headers 参数始终为空

关于在viewModel 之外请求数据的情况

在我的实际使用中,有的页面因为功能比较杂,导致在 viewModel 之外需要请求数据,偏偏请求数据的这个文件已经 继承了其他的类了,无非再次继承 viewModel,这样一来,哪怕在这个文件中 new ViewModel 也不行,发起请求后的回调就回不去了。个人比较菜,所有咨询下大佬这种情况有什么好一点的解决办法。

给菜鸡作者提个bug

Demo中进入SettingFragment(设置界面)后,不管做什么操作,退出该Fragment后,操作其他的任何界面变多了以后会变的卡顿(可以试试项目模块)......如果直接写一个SettingActivity去包裹SettingFragment,退出该SettingActivity就不会有卡顿的问题。
目前发现原因大概是PreferenceFragmentCompat该类的锅,记录一下放在这里,大家如果发现有好的解决方案也可以提一下

能够多增加点测试用例

我搜索到的开源项目测试用例都少的可怜,不知道是不是我得姿势不对。希望能补充些测试用例,很想知道android工程师开发应用时如何进行自测

有什么方法可以 代替 onresume吗?

目前使用的是修改过的navigation,理论不会触发重绘,但是也带来了新的问题,
A ---> B 再从B --->A 时,假如不考虑viewpager之类的情况

有什么好办法可以在A页面监听返回事件吗? 类似activity的onresume

大概就是某个页面需要返回时刷新一下

ResultState包装对象后,只能在observe拿到数据吗?

比如我在viewmodel中定义了titleData,我会多次调用getProjectTitleData这个接口
var titleData: MutableLiveData<ResultState<ArrayList<ClassifyResponse>>> = MutableLiveData()

此时在getProjectTitleData()中我想先判断拿到 titleData中的ArrayList做一些操作,再决定是否继续调用网络请求。

但是目前被ResultState包装对象后,就拿不到resultState.data这个字段了。这个ResultState应该怎么写才能暴露出类似getData()这样的方法?

我看谷歌的文档
https://developer.android.google.cn/jetpack/docs/guide#addendum
似乎也是这样,感觉很不方便啊

提个bug,拦截返回事假会使 其他的fragment无法通过onBackPressedDispatcher拦截返回事件

    override fun onBackPressed() {
        val nav = Navigation.findNavController(this, R.id.nav_host_fragment)
        if (nav.currentDestination != null && nav.currentDestination!!.id != R.id.mainFragment) {
            //如果当前界面不是主页,那么直接调用返回即可
            nav.navigateUp()
        } else {
            //是主页
            if (System.currentTimeMillis() - exitTime > 2000) {
                ToastUtils.showShort("再按一次退出程序")
                exitTime = System.currentTimeMillis()
            } else {
                super.onBackPressed()
            }
        }
    }

这样子重写MainActivity的onBackPressed()方式拦截返回事假会使 其他的fragment无法通过onBackPressedDispatcher拦截返回事件。
参考:https://developer.android.google.cn/guide/navigation/navigation-custom-back#kotlin
参考:https://www.jianshu.com/p/aec2cde0a5f0

我目前是在MainFragment添加onBackPressedDispatcher拦截返回键,处理返回2次退出app事件
在SubFragment添加onBackPressedDispatcher,拦截返回键,处理之前提到的Navigation 的出栈事件

登录页面全屏问题是这么设置的?

我的里面点击任意一项都会跳转到登录页面,而登录页面也是一个fragment 窗口加载的,可是登录页面的是一个全屏的页面,这个全屏怎么设置的。

kotlin-android-extensions转viewbinding

谷歌残忍的抛弃了kotlin-android-extensions,鸡哥是否有计划把项目里的kotlin-android-extensions转成viewbinding?
主要是我自己太菜了,不知道怎么改。。所以想看下大佬的代码然后ctrl+C,ctrl+V一下

网络请求返回的 data 为 null 时奔溃

首先感谢大佬的框架,集成到项目中挺方便的。最近在使用中碰到一些问题,咨询一下。
1.我这边返回的数据格式就是那种有 code,message,data 的样子
2.所以我网络请求一般都是用自动脱壳的方式,但是这边有个问题是,当返回的数据 data 是 null的时候,app就会奔溃
报错信息:
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter it

此处的 it 是请求成功后,在 parseState(resultState,{ 这里的it })
3. 用不脱壳的方式应该是ok的,因为自己可以处理进行判空呀啥的。但是所有的请求接口,总会有的接口有时候返回 null 有时候有数据,这样的话岂不是所有的接口都需要使用不脱壳的方式嘛。
4.咨询一下大佬有没有更好的解决方式,或者怎么弄会更合理

UnPeekLiveData的疑问

🐔哥
me.hgj.jetpackmvvm.callback.livedata.event.EventLiveData和me.hgj.jetpackmvvm.callback.livedata.UnPeekLiveData有啥区别呀?为啥感觉都是防止旧数据倒灌的情况?

目前被修改过的Navigation是否能做到类似replace的功能

比如有 A . B . C . D 四个fragment。
正常跳转的话的 A ->B ->C ->D
此时的回退栈:
A --B--C--D

如果有个场景是 A ->B -替换->C-替换->D
此时的回退栈:
A -- D

如果拿作者的demo举例子的话就是
首页 --> 搜索页 -->搜索结果页

如果要求 [搜索页] 点击搜索跳转到 [搜索结果页] 后, [搜索页]从回退栈中移除
此时的回退栈
首页 --搜索结果页

类似淘宝 京东的搜索页都是这样的逻辑

我看了官方的Navigation文档,google推荐是使用popUpTo 和 popUpToInclusive来清除当前页和目标页面之间的栈元素。但是感觉使用起来还需要拦截回退事件,不够自然

作者有什么建议或者思路呢

大佬,关于livedate更新数据的回调的问题请教一下

我看项目使用livedata更新数据,是在外面有包装了一层ListDataUiState or UpdateUiState。如果是列表还好,如果出现异常,可以使用arrayListOf()设置默认值,但是UpdateUiState如果请求时出现异常,那包装成UpdateUiState对象,data该怎么穿,我尝试设置成data: T?和T: Any都不行,我看项目中data类型全都是Int,可以设置0,我的都是数据类型,不知道该怎么穿,麻烦大佬看一下

在重构?

什么时候可以重构完?准备开新项目了

navigation切换fragment时 快速点击可能会崩溃

me.hgj.jetpackmvvm.ext.NavigationExt 中 NavController.navigateAction方法建议改为以下代码:

fun NavController.navigateAction(resId: Int, bundle: Bundle? = null, interval: Long = 500) {
val currentTime = System.currentTimeMillis()
if (currentTime >= lastTapTime + interval) {
lastTapTime = currentTime
try {
navigate(resId, bundle)
} catch (ignore: Exception) {}
}
}

当 fragment 中 action 的 duration设置为 0 时,连续点击两个不同的跳转会导致如下崩溃:

java.lang.IllegalArgumentException: Navigation action/destination com.xxx.xxx:id/action_aFragment_to_bFragment cannot be found from the current destination Destination(com.xxx.xxx:id/cFragment) label=cFragment class=com.xxx.xxx.ui.fragment.cFragment

作者为何用AndroidViewModel?

之前一直关注作者写的demo,今天下了新demo看,改动好大啊,尤其是新的BaseViewModel。并没有看到在ViewModel里用到application。再就是初始化viewmodel的方式着实累了点。可以参考private val requestViewModel :RequestAriticleViewModel by viewModels(),直接使用ktx中的扩展函数就好。

fragment切换时有卡顿

fragment用懒加载,切换动画还没执行完毕时数据就已经加载好了,这时页面会有渲染卡顿,有什么好办法解决吗?暂时用postDelayed 500 可以解决

关于多个fragment共用一个viewmodel的情况

me.hgj.jetpackmvvm.demo D: loginFragment--------->onCreate
me.hgj.jetpackmvvm.demo D: loginFragment--------->onStart
me.hgj.jetpackmvvm.demo D: loginFragment--------->onResume
me.hgj.jetpackmvvm.demo D: loginFragment--------->LoginRegisterViewModel@5df2ec
me.hgj.jetpackmvvm.demo D: loginFragment--------->RequestLoginRegisterViewModel@68c86b5
me.hgj.jetpackmvvm.demo D: RegisterFrgment--------->onCreate
me.hgj.jetpackmvvm.demo D: RegisterFrgment--------->onStart
me.hgj.jetpackmvvm.demo D: RegisterFrgment--------->onResume
me.hgj.jetpackmvvm.demo D: RegisterFrgment--------->LoginRegisterViewModel@b754ad6
me.hgj.jetpackmvvm.demo D: RegisterFrgment--------->RequestLoginRegisterViewModel@5424157

我注意到LoginFragment和 RegisterFrgment 共用了2个ViewModel,但是因为是使用getViewModel(获取当前fragment上下文的方法)获取的,所以这两个ViewModel并不是同一个对象。
假设我在LoginFragment和RegisterFrgment 的RequestLoginRegisterViewModel都内持有了数据库的操作对象,那么我在RegisterFrgment 被销毁的时候回调rViewModel的onCleared()中把它close了。那么会影响到LoginFragment的RequestLoginRegisterViewModel内的数据库的操作对象吗?

关于网络请求,后台数据请求code 200 data为空的时候出错

用了你的框架来重构 发现 后台如果没设置参数data为空的时候 由于kotlin的原因 用BaseViewModel.request请求 在BaseViewModelExt.kt解析数据的时候 会报出 Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter it的错误
如果用requestNoCheck来请求 在解析又会多一步操作来处理 -0 - 不用想用这个方法 。
请问有什么办法来处理这个问题吗

是修改了Navigation使它支持Fragment复用吗?

原版的Navigation我知道会每次都销毁View。很蛋疼

那么框架内的FragmentNavigator 就是重写了replace的逻辑,变成了show和hide的吧

里面是否有用到反射?我担心如果用了反射以后谷歌一刀切不允许反射了出问题

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.