Code Monkey home page Code Monkey logo

t-mvvm's Introduction

T-MVVM 努力打造简单实用的开发框架

艺术帮是一款艺术图片应用,采用LiveData+ViewModel+RxJava+okHttp+Retrofit+Glide架构的项目,仅用于学习交流,不断完善中

项目github地址:https://github.com/SelfZhangTQ/T-MVVM
apk下载地址

T-MVVM 2.0版本更新说明

 * 基于LiveBus事件除去ViewModel和Repository接口耦合。

项目初衷 在此之前一直使用MVP,MVP缺点:

  • Presenter中除了逻辑以外,还有大量的View->Model,Model->View的逻辑操作,造成Presenter臃肿,维护困难。
  • 对UI的渲染放在了Presenter中,所以UI和Presenter的交互会过于频繁。
  • Presenter过多地渲染了UI,往往会使得它与特定的UI的交互频繁。一旦UI变动,Presenter也需要变
  • 接口暴增,可以说代码量成倍增长,让人无法忍受,

MVVM的出现提高可维护性。解决了MVP大量的手动View和Model同步的问题,但并非救世主。没有最好的框架,只有最适合自己的,

此项目未使用:DataBinding原由

  • 数据绑定增加Bug调试难度。
  • 复杂的页面,model也会很大,虽然使用方便了也很容易保证了数据的一致性,当时长期持有,不利于释放内存。
  • 数据双向绑定不利于View重用。

工程代码说明

image

无图无真相

image image
image image
image image

基于ViewModel,LiveData,Retrofit,Rxjava实现T-MVVM体系结构的架构,泛型限定,深度解耦。

ViewModel优点:
 * 同步关联生命周期,
 * 数据共享 ,
 * 复用性强 ,
LiveData优点:
 * 确保UI界面的数据状态,
 * 没有内存泄漏,不会因为Activity的不可见导致Crash,
 * 不用再人为的处理生命周期,
 * 共享资源,

此架构未使用DataBinding原由:

* 数据绑定增加Bug调试难度,
* 复杂的页面,model也会很大,虽然使用方便了也很容易保证了数据的一致性,当时长期持有,不利于释放内存,
* 数据双向绑定不利于View重用,

###T-MVVM架构分层代码 1:先定义BaseViewModel基类

 /**
  * @author:tqzhang on 18/7/26 16:15
  */
  public class BaseViewModel<T extends BaseRepository> extends AndroidViewModel {

    public T mRepository;

    public BaseViewModel(@NonNull Application application) {
        super(application);
        mRepository = TUtil.getNewInstance(this, 0);
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        if (mRepository != null) {
           mRepository.unSubscribe();
        }

   }
 }

BaseViewModel通过泛型类型参数BaseRepository子类初始化Repository数据仓库,同时在activity/fragment走onDestroy()生命周期方法时 BaseViewModel回调onCleared,即页面销毁是用来取消网络请求或资源释放等操作。

正常开发一般不建议直接通过ViewModel获取网络数据,这里我们将工作交给一个新的模块Repository。Repository只负责数据处理,提供干净的api,方便切换数据来源。

2:再定义BaseRepository

public abstract class BaseRepository {

     protected ApiService apiService;

     public BaseRepository() {
             if (null == apiService) {
                 apiService = HttpHelper.getInstance().create(ApiService.class);
             }
     }

     private CompositeSubscription mCompositeSubscription;

     protected void addSubscribe(Subscription subscription) {
            if (mCompositeSubscription == null) {
                mCompositeSubscription = new CompositeSubscription();
            }
           mCompositeSubscription.add(subscription);
      }

     public void unSubscribe() {
             if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
            mCompositeSubscription.clear();
    }
}

BaseRepository中内容相对简单,主要是获取ApiService和网络请求订阅容器,方便管理网络请求。

3:然后自定义AbsLifecycleFragment基类继承BaseFragment,BaseFragment可自行编写。如不需要使用T-MVVM,可自行继承BaseFragment,互不影响。

 public abstract class AbsLifecycleFragment<T extends BaseViewModel> extends BaseFragment  {

       protected T mViewModel;
       /**
        * init view
        * @param state
        */
       @Override
       public void initView(Bundle state) {
            mViewModel = VMProviders(this, TUtil.getInstance(this, 0));
          if (null != mViewModel) {
            dataObserver();
          }
       }

      /**
       * create ViewModelProviders
       *
       * @return ViewModel
       */
      protected <T extends ViewModel> T VMProviders(BaseFragment fragment, @NonNull Class<T> modelClass) {
           return ViewModelProviders.of(fragment).get(modelClass);
      }

      protected void dataObserver() {

      }

     /**
      * 获取网络数据
      */
      protected void getRemoteData() {

      }

    }

在initView方法中通过BaseViewModel子类泛型类型参数获取Class,在通过ViewModelProviders.of(fragment).get(modelClass))实例化ViewModel, 到此我们的基类基本编写完毕。

4:下面我们以一个简单业务实战下,获取文章列表。

4-1:ArticleFragment

/**
* @author:tqzhang on 18/7/2 14:40
*/
public class ArticleFragment extends AbsLifecycleFragment<ArticleViewModel> {
 protected TRecyclerView mRecyclerView;
 protected StaggeredGridLayoutManager layoutManager;
 protected DelegateAdapter adapter;

 public static ArticleFragment newInstance() {
    return new ArticleFragment();
 }

 @Override
 public int getLayoutResId() {
    return R.layout.fragment_list;
 }

 @Override
 public void initView(Bundle state) {
    super.initView(state);
    layoutManager=new new StaggeredGridLayoutManager(1,  StaggeredGridLayoutManager.VERTICAL);
    //获取网络数据
    getRemoteData();
 }

 //初始化adapter
 public void initAdapter(){
   adapter= new DelegateAdapter.Builder<>()
           .bindArray(ArticleInfoVo.class, new ArticleRem1ItemHolder(context)
            , new ArticleRem2ItemHolder(context)
            , new ArticleRem3ItemHolder(context))
           .bind(HeaderVo.class, new HeaderViewHolder(context, rogressStyle.Pacman))
           .bind(FootVo.class, new FootViewHolder(context, ProgressStyle.Pacman))
           .build();


 //数据观察
 @Override
 protected void dataObserver() {
    mViewModel.getArticleList().observe(this, articleVo -> {
        if (null != articleVo) {
           mRecyclerView.refreshComplete(articleVo.data.list, false);
        }
    });
 }
 //获取网络数据
 @Override
 protected void getRemoteData() {
     mViewModel.getArticleList(typeId, lastId);
 }
}

我们可以看出来ArticleFragment中只有UI初始化,发请网络请求action以及数据观察,列表展示用了TRecyclerView,欢迎star哦面向holder开发高复用,多类型的刷新库,从此只关心你的列表的Item展示。 通过泛型除去了MVP中通过接口传递信息的大量代码, 从此see you Mass implementation of interfaces。

4-1:ArticleViewModel

/**
* @author:tqzhang on 18/7/26 16:15
*/
public class ArticleViewModel extends BaseViewModel<ArticleRepository> {

     private MutableLiveData<ArticleVo> mArticleData;

     public ArticleViewModel(@NonNull Application application) {
          super(application);
     }

     public LiveData<ArticleVo> getArticleList() {
          if (mArticleData == null) {
              mArticleData = new MutableLiveData<>();
          }
         return mArticleData;
     }

    public void getArticleList(String lectureLevel1, String lastId) {
          mRepository.loadArticleRemList(new CallBack<ArticleVo>() {
        @Override
        public void onNext(ArticleVo articleObject) {
            mArticleData.postValue(articleObject);
        }

        @Override
        public void onError(String e) {

        }            }
    });
}

ArticleViewModel中持有数据观察容器LiveData和真正发起网络请求动作,在接收到服务端返回的数据通过 mArticleData.postValue(articleObject);通知Observer数据的更改,此处需注意的是,setValue方法只能在主线程中调用,postValue可以在任何线程中调用,如果是在后台子线程中更新LiveData的值,必须调用postValue。

4-3:ArticleRepository

/**
 * @author:tqzhang on 18/7/28 13:00
 */
 public class ArticleRepository extends BaseRepository {

      public void loadArticleRemList(final CallBack<ArticleVo> listener) {
          addSubscribe(apiService.getArticleRemList()
                .compose(RxSchedulers.io_main())
                .subscribe(new RxSubscriber<ArticleVo>() {

                    @Override
                    public void onSuccess(ArticleVo articleObject) {
                        listener.onNext(articleObject);
                    }

                    @Override
                    public void onFailure(String msg) {
                        listener.onError(msg);
                    }
            }));

}

最后我们的ArticleRepository中就提供数据,此处只提供了网络层的数据,在实际应用中可拆分类loacl data和remote data,可根据项目需求自行处理。

第三方库

  • okHttp
  • Retrofit
  • RxJava
  • LiveData
  • ViewModel
  • ButterKnife
  • Glide
  • Gson
  • BottomNavigationBar(轻量级底部导航栏)
  • LoadState (统一管理错误页面库)
  • gsyVideoPlayer(视频播放)
  • TRecyclerView (面向ViewHolder开发的刷新库,多类型item终结者,好不好用你试试就知道)
  • ...

声明

这个属于个人开发作品,仅做学习交流使用。诸位勿传播于非技术人员,拒绝用于商业用途,数据均属于非正常渠道获取。

t-mvvm's People

Contributors

selfzhangtq avatar zhangtianqiu 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

t-mvvm's Issues

有些问题

1..ViewModel和Repository应该是一对多的关系,这个问题倒是好解决
2..数据与视图绑定这块,有没有什么好的点子,DataBingding虽然有很多缺点,但是数据与视图绑定这块,确实是实现了一种解决方案,虽然写在xml里面有点反人类

滑动bug

测试手机OPPOR15,首页点击网格布局区域的问答条目进入详情页,快速滑动3页左右,APP闪屏开始进入重启状态,必现

多个网络请求

一个页面打开之后需要访问多个接口获取页面数据怎么处理

数据源LiveData不在ViewModel里?

如果数据源的LiveData不在ViewModel里, 那么configChange以后, Activity重建后, 也无法从ViewModel里获取到数据, 还要重新请求. 直接通过ViewModel来调用Repository通过LiveDataBus发送数据感觉和MVP的**是一样的. ViewModel里并没有保存数据啊

LiveBus同一个key只能添加一个观察者,第二个观察者无法收到通知

` public static class LiveBusData extends MutableLiveData {

    private boolean isFirstSubscribe;

    LiveBusData(boolean isFirstSubscribe) {
        this.isFirstSubscribe = isFirstSubscribe;
    }

    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        super.observe(owner, new ObserverWrapper<>(observer, isFirstSubscribe));
    }
}

private static class ObserverWrapper<T> implements Observer<T> {

    private Observer<T> observer;

    private boolean isChanged;

    private ObserverWrapper(Observer<T> observer, boolean isFirstSubscribe) {
        this.observer = observer;
        isChanged = isFirstSubscribe;
    }

    @Override
    public void onChanged(@Nullable T t) {
        if (isChanged) {
            if (observer != null) {
                observer.onChanged(t);
            }
        } else {
            isChanged = true;
        }
    }

}`

问题再isChanged 和 isFirstSubscribe 标志位上,请修改~

运行报错

2019-11-20 14:39:45.358 3733-3733/? E/DelegateAdapter: 0---0 2019-11-20 14:39:45.362 3733-3733/? E/AndroidRuntime: FATAL EXCEPTION: main Process: com.code.mvvm, PID: 3733 java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference at com.code.mvvm.widget.banner.BannerItemView.lambda$onBindViewHolder$0(BannerItemView.java:45) at com.code.mvvm.widget.banner.-$$Lambda$BannerItemView$fKWkbLq_F2J7xDSWr9Y1RkJGKeo.bindView(Unknown Source:4) at com.code.mvvm.widget.banner.BannerView.setBannerView(BannerView.java:245) at com.code.mvvm.widget.banner.BannerItemView.onBindViewHolder(BannerItemView.java:43) at com.code.mvvm.widget.banner.BannerItemView.onBindViewHolder(BannerItemView.java:24)

ViewModel 跟Repository 应该是存在一对多的关系的,另外V程跟M层没有实现解耦

现在demo中包括底层的架构设计都是ViewModel 跟 Repository 一一对应的关系,实际开发过程中比如说qa页面是需要判断有没有登陆的。类似这样的时候是不是就会产生一对多的关系。看了下demo项目中的代码我发现Activity层直接引用了Repository的类。比如一些常量,这样有一些轻微的依赖关系。不用databinding还导致了另外的一个问题就是 在Activity (view 层)要引用到实体类。综上m层跟v层还是存在耦合性。后续需要优化。

升级到Aamdroid X 以后 ViewModelProviders 无法创建Viewmode 实例

image

Cannot create an instance of class com.keisdom.assistant.core.vm.LoginViewModel
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2740)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2801)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1548)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:163)
at android.app.ActivityThread.main(ActivityThread.java:6372)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:791)

编译不通过

你确认过现在的项目是可以正常编译通过的吗,项目类的结构有错误了,在mvvm这个liably里的base包里和根目录下有一样的类我看了下代码都一样的

如果我想在页面中加上网络请求时等待的弹窗,是否有较好的办法

比如在网络请求开始前显示loading 弹窗,网络请求结束时销毁弹窗,ViewModel里面调用Model层获取数据,那么在调用Model层前显示loading,请求结束时销毁loading,而ViewModel不建议应用View层,我该如何通知View层去显示或者销毁loading弹窗呢,暂时我想到的办法是另外每个页面都需要单独定义一个LiveData去设置显示还是销毁,然后View层观察他的值的变化。

想问下有没有更好的办法。

编译报错

环境:AS3.0.1 Gradle4.1
image
在app的AndroidManifest.xml添加了下面代码还是无法解决
image

Bug 闪退

点击【活动】 闪退 必现 OPPO R11s android7.1.1 ColorOS版本V3.2
错误日志:
2018-10-10 14:52:11.378 24098-24098/com.code.mvvm E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.code.mvvm, PID: 24098
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.code.mvvm/com.code.mvvm.core.view.common.CommonActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2868)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2929)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1648)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:185)
at android.app.ActivityThread.main(ActivityThread.java:6615)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:916)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:806)
Caused by: java.lang.NullPointerException
at com.code.mvvm.util.Preconditions.checkNotNull(Preconditions.java:12)
at com.code.mvvm.core.vm.ActivityViewModel.getActivityList(ActivityViewModel.java:36)
at com.code.mvvm.core.view.activity.ActivityListFragment.getNetWorkData(ActivityListFragment.java:60)
at com.code.mvvm.core.view.activity.ActivityListFragment.lazyLoad(ActivityListFragment.java:49)
at com.mvvm.base.BaseFragment.onViewCreated(BaseFragment.java:47)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1430)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1750)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1819)
at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:797)
at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2590)
at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2377)
at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2332)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2239)
at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3231)
at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:3181)
at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:192)
at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:572)
at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:177)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1249)
at android.app.Activity.performStart(Activity.java:7024)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2831)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2929) 
at android.app.ActivityThread.-wrap12(ActivityThread.java) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1648) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:185) 
at android.app.ActivityThread.main(ActivityThread.java:6615) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:916) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:806) 

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.