MVP模式做的尝试

纯粹是个人学习总结,如有不对的地方请吐槽。
目录结构

app模块下的目录结构

image.png

app目录下放全局配置文件,包括Application
base目录一眼就看清是什么
di目录存放dagger有关的文件
ui这个目录也很清楚

mvplibrary模块下的目录

image.png

config是配置有关的文件
delegate是监听Activity和Fragment生命周期的文件,这里就是不需要继承的关键代码
di同样是dagger相关的文件,我这用的是dagger2
mvp就是我们的mvp各个模块的基类以及接口
screen是存放dp和sp设配文件的工具类,后面会将怎么使用
sharedpre不是很形象,仔细看还是能看出是SharedPreferences相关类
后面的目录就不介绍了

下面就从如何使用说起

先来看看library下的Activity基类

public abstract class LibBaseActivity extends AppCompatActivity implements IActivity, IView {

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    initInject();//执行注入
    initData();//初始化
}

/************************{@link IActivity 接口实现}************************/

@Override
public View getLayoutView() {
    return null;
}

@Override
public boolean eventBus() {
    return false;
}

@Override
public boolean fragment() {
    return false;
}

/************************{@link IView 接口实现}************************/

@Override
public void showLoading() {

}

@Override
public void hideLoading() {

}

@Override
public void showMessage(String message) {
    Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}

/************************其他方法************************/
/**
 * 获取AppComponent
 *
 * @return AppComponent
 */
protected AppComponent getAppComponent() {
    return AppDelegate.sAppDelegate.getAppComponent();
  }
}

关于IActivity接口我们先来看看delegate目录下的文件


image.png

再来看看IActivity里面的方法

public interface IActivity {

/**
 * 获取当前布局Id
 *
 * @return
 */
int getLayoutId();

/**
 * 执行注入方法
 */
void initInject();

/**
 * 获取当前显示的布局
 *
 * @return
 */
View getLayoutView();

/**
 * 子类做初始化操作
 */
void initData();

/**
 * 是否有事件绑定
 *
 * @return
 */
boolean eventBus();

/**
 * 是否使用fragment
 *
 * @return
 */
boolean fragment();
}

eventBus这个方法用于判断是否需要事件监听,返回true就完成了事件注册和事件注销
fragment这个方法用于判断是否需要监听fragment生命周期。

是不是觉得,看到这里还是不清楚到底是怎么回事,不用担心,这很正常,下面就来介绍如何不需要继承也能监听activity和Fragment的生命周期。

神奇之处就在于Application.ActivityLifecycleCallbacks和FragmentManager.FragmentLifecycleCallbacks对就是它们让我们脱离继承也能监听activity和Fragment的生命的周期,也许有很多小伙伴都不知道这个(我以前我也是其中之一),只是写着写着,就发现继承也存在不便(当你你已经继承了一个类,如果使用到其他第三方库,它也需要继承他的类,这时就JJ了)。

下面来看看监听的具体实现AppDelegate类,监听的关键代码

/**
 * 在application的onCreate方法中调用
 */
public void onCreate() {
    mActivityLifecycle = new ActivityLifecycle();
    mApplication.registerActivityLifecycleCallbacks(mActivityLifecycle);
    sAppDelegate = this;
}

第一行创建activity监听器,第二行设置监听器,fragment监听器后面介绍

然后在Application中创建并调用onCreate方法这样就完成了监听

 mAppDelegate = new AppDelegate(sApp);
    //这个方法执行之后会监听每个activity和fragment的生命周期
    //建议在Application的onCreate方法里面调用
    mAppDelegate.onCreate();

ActivityLifecycleCallbacks具体有那些回调方法可以自行百度,这里就不介绍了。

上面的没有看懂也没关系,只需要看懂下面的如何调用就可以了
总结一下如何调用:

public class App extends Application {
        private AppDelegate mAppDelegate;
        //当前app实例对象
        public static App sApp;
        //全局上下文对象
        public static Context sContext;
    
        @Override
        public void onCreate() {
            super.onCreate();
            sApp = this;
            sContext = this;
            mAppDelegate = new AppDelegate(sApp);
            //这个方法执行之后会监听每个activity和fragment的生命周期
            //建议在Application的onCreate方法里面调用
            mAppDelegate.onCreate();
            initInject();//初始化全局注入
        }
    
        /**
         * 初始化全局注入
         */
        public void initInject() {
            /**初始化注入,这个方法调用了之后就会调用{@link com.junwu.mvplibrary.config.IConfigModule#applyOptions(ConfigModule.Builder)}方法配置参数,
             * 接着就会调用{@link com.junwu.mvplibrary.config.IRegisterApiModule#registerComponents(IRepositoryManager)}方法,设置api接口
             * 可以在测试接口确定了之后在调用该方法,比如:测试阶段需要选择测试服务器地址,选择之后再调用这个方法
             **/
            mAppDelegate.injectRegApiService(new AppConfigModule(), new RegisterApiModule());
        }
 }
关键代码就三句话:
mAppDelegate = new AppDelegate(sApp);
mAppDelegate.onCreate();
mAppDelegate.injectRegApiService(new AppConfigModule(), new RegisterApiModule());
前面两句很好理解,最后一句就是设置OkHttp、Retrofit、RxCache的配置类和关于Retrofit、RxCache的api接口service类的配置

到这里,Activity和Fragment的生命周期监听部分和控件的初始化和事件注册,已经介绍完了,下面继续看关于MVP部分。

我们在写Activity和Fragment的时候一般都有base类,这里有两种选择,一就是自己写base类但是必须要实现IActivity和IFragment接口就行,也可以直接继承LibBaseActivityLibBaseFragment,这里面实现也比较简单,就是实现了IActivityIFragment两个接口。

再来看看dagger的配置,如果对dagger不熟悉的就自行查阅相关文档
这篇关于MVP模式对dagger的要求不高,只需要知道基本概念即可,如果实在不想看dagger的直接照搬也是可以的。
dagger相关的文件Component、Module、Scope以及注解Inject
AppComponent这个就是让Inject和Module产生关系的类
Module类有:AppModule配置Model的,关于更多Module

举个例子来看看,可能会更清楚

@ViewScope
@Component(modules = {ViewModule.class, ModelModule.class, UtilsModule.class}, dependencies = AppComponent.class)
public interface IViewComponent {
    /*****************************activity注入***************************/
//    void inject(StartActivity activity);
    /*****************************Fragment注入***************************/
    void inject(StartFragment fragment);
    void inject(HomeFragment fragment);
    /*****************************其他注入***************************/
}

从这里不难看出这个Component依赖AppComponent、ViewModule、ModelModule、UtilsModule

AppComponent在mvpLibary模块下:

@Singleton
@Component(modules = {AppModule.class, ClientHttpModule.class, ConfigModule.class})
public interface AppComponent {

    /**
     * 注入
     *
     * @param delegate AppDelegate
     */
    void inject(AppDelegate delegate);

    /**
     * 获取当前application对象
     *
     * @return Application
     */
    Application getApplication();

    /**
     * 获取OkHttpClient
     */
    OkHttpClient getOkHttp();

    /**
     * 获取Retrofit
     */
    Retrofit getRetrofit();

    /**
     * 获取RxCache
     */
    RxCache getRxCache();

    /**
     * 获取RxCacheBuild对应的实体
     */
    RxCacheBuilderEntity getRxCacheBuilderEntity();

    /**
     * 获取IRepositoryManager
     */
    IRepositoryManager getIRepositoryManager();
}

这个就像是基础模块的获取类

ViewModule,这个必须同你写的代码是一个Module下

@Module
public class ViewModule {

    private IView mIView;
    private Activity mActivity;

    public ViewModule(IView view) {
        this(null, view);
    }

    public ViewModule(Activity activity, IView view) {
        mActivity = activity;
        this.mIView = view;
    }

    @ViewScope
    @Provides
    public Activity provideActivity() {
        return mActivity;
    }

    @ViewScope
    @Provides
    public IView provideIView() {
        return mIView;
    }
}

这里的IView和Presenter里面的IView以及Activity实现的IView有密切关系

ModelModule类的代码

@Module
public class ModelModule {

    @ViewScope
    @Provides
    StartContract.Model provideStartContractModel(StartModel startModel) {
        return startModel;
    }
}

UtilsModule就不介绍了,它是用于配置一些工具类的注入,为了思路清晰就单独分出来了

然后在看看LibBasePresenter

public class LibBasePresenter<M extends IModel, V extends IView> implements IPresenter {
    protected M mModel;
    protected V mView;
    //是否注册了eventBus事件
    private boolean isEventBus = false;

    /**
     * 处理IView的所有业务逻辑
     *
     * @param m model 数据来源,网络、文件、数据库等数据
     */
    public LibBasePresenter(IModel m) {
        this.mModel = (M) m;
        onStart();
    }

    /**
     * 处理IView的所有业务逻辑
     *
     * @param v IView的子类接口实现类
     */
    public LibBasePresenter(IView v) {
        this(null, v);
    }

    /**
     * 处理IView的所有业务逻辑
     *
     * @param m model 提供网络、文件、数据库等数据来源
     * @param v IView的子类接口实现类
     */
    public LibBasePresenter(IModel m, IView v) {
        if (m != null)
            this.mModel = (M) m;
        if (v != null)
            this.mView = (V) v;
        onStart();
    }

    @Override
    public void onStart() {
        if (useEventBus()) {
            registerEventBus();
        }
    }

    @Override
    public void onDestroy() {
        //解除订阅
        if (mCompositeDisposable != null) {
            mCompositeDisposable.clear();
        }
        if (isEventBus) {
            EventBus.getDefault().unregister(this);
            isEventBus = false;
        }
        if (mModel != null) {
            mModel.onDestroy();
        }
        mModel = null;
        mView = null;
    }

    /**
     * 注册EventBus事件
     */
    protected void registerEventBus() {
        EventBus.getDefault().register(this);
        isEventBus = true;
    }
    /**
     * 是否注册事件
     *
     * @return
     */
    protected boolean useEventBus() {
        return false;
    }
}

请看构造函数Model和View都是可以为空的,这就可以根据业务来判断虚部需要Model模块,有的MVP是吧Model模块和Presenter是融合在一起的,对于一些业务不是很复杂的可以这么做,如果业务比较多还是建议将Model和Presenter区分开。

看在这里基本上应该已经看懂了,下面就来总结一下:
1:IViewComponent将所有需要注入的类都用它来注入
2:ModelModule所有的Model都有它来提供
3:ViewModule里面将所有的Activity和Fragment都视为IView,在LibBasePresenter里面会自动转换为对应的子类,减少了IView的注册
4:还是要将代码自己梳理一遍,具体可以参照MVPAttempt
这里打个小广告:关于MVPAttempt框架的应用这里有个app已经上线,有兴趣的可以下载下来看看
所有的注入都由IViewComponent来完成,可能会太死板,如果有特殊注入就可以按照dagger完整的方式写一套注入来完成,这样也是可以的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容