從零開始搭建一個主流專案框架(二)—MVP+Dagger2
個人部落格:haichenyi.com。感謝關注
接著上一篇簡單的框架,沒有看過的同鞋可以去喵一眼。上一篇我們搭好了簡單的框架,初始化一次的內容丟在Application裡面,所有的activity繼承一個類BaseActivity,還有Fragment繼承的一個類BaseFragment
現在我們來加上MVP,不懂MVP的同鞋可以看一下,我前面寫過的三種主流框架的對比。我們先匯入dagger2的兩個包,程式碼如下:
implementation 'com.google.dagger:dagger:2.14.1'
annotationProcessor "com.google.dagger:dagger-compiler:2.14.1"
第一步
新建BasePresenter介面,BaseMvpPresenter類去實現BasePresenter介面,程式碼如下
package com.shfzwkeji.smartwardrobe.base;
/**
* Author: 海晨憶.
* Date: 2017/12/21
* Desc: 不帶mvp的presenter的基類
*/
public interface BasePresenter<T extends BaseView> {
void attachView(T baseView);
void detachView();
}
package com.haichenyi.myproject.base;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:帶mvp的presenter的基類
*/
public class BaseMvpPresenter<T extends BaseView> implements BasePresenter<T> {
protected T baseView;
@Override
public void attachView(T baseView) {
this.baseView = baseView;
}
@Override
public void detachView() {
this.baseView = null;
}
}
這裡就只有兩個方法,一個是繫結view,還有一個是在ondestory方法裡面解除繫結的方法,用來保證P層的生命週期和V層同步,避免了,當V層銷燬的時候,P層仍然存在造成的記憶體洩漏。
第二步
新建BaseMvpActivity
package com.haichenyi.myproject.base;
import javax.inject.Inject;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:帶MVP的Activity
*/
public abstract class BaseMvpActivity<T extends BasePresenter> extends BaseActivity{
@Inject
protected T basePresenter;
@Override
@SuppressWarnings("unchecked")
protected void initView() {
super.initView();
initInject();
if (null != basePresenter) {
basePresenter.attachView(this);
}
}
protected abstract void initInject();
@Override
protected void onDestroy() {
if (null != basePresenter) {
basePresenter.detachView();
basePresenter = null;
}
super.onDestroy();
}
}
運用dagger2註解的方式,生成P層,這裡我們在用P層之前得先生成P層,所以initject方法一定要在basePresenter用之前呼叫,因為他就是生成P層的程式碼。
怎麼生成呢?dagger我們一般都命名成di層,所以,我們先建立di層的package,專案結構圖如下:
這裡給出的是mvp+dagger加入之後的專案結構。我們重點看選中的di層,裡面有4個package分別是component,module,qualifier,scope四個包,至於他們的作用分別是什麼,請自行百度,google,dagger的用法。我這裡先貼出這幾個類,介面的程式碼:
ActivityComponent
package com.haichenyi.myproject.di.component;
import com.haichenyi.myproject.MainActivity;
import com.haichenyi.myproject.di.module.ActivityModule;
import com.haichenyi.myproject.di.scope.ActivityScope;
import dagger.Component;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
@ActivityScope
@Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
void inject(MainActivity mainActivity);
}
AppComponent
package com.haichenyi.myproject.di.component;
import com.haichenyi.myproject.di.module.AppModule;
import com.haichenyi.myproject.di.module.HttpModule;
import javax.inject.Singleton;
import dagger.Component;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
@Singleton
@Component(modules = {AppModule.class, HttpModule.class})
public interface AppComponent {
}
ActivityModule
package com.haichenyi.myproject.di.module;
import dagger.Module;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
@Module
public class ActivityModule {
}
AppModule
package com.haichenyi.myproject.di.module;
import com.haichenyi.myproject.base.MyApplication;
import dagger.Module;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
@Module
public class AppModule {
private MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
}
HttpModule
package com.haichenyi.myproject.di.module;
import dagger.Module;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
@Module
public class HttpModule {
}
ActivityScope
package com.haichenyi.myproject.di.scope;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Scope;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
這幾個類,接口裡面基本上都沒有內容,因為這幾個類都是後面才會用的到的,這裡我直接貼出來,說起來方便一些。還需要加兩個方法,在MyApplication裡面加如下方法:
/**
* 獲取AppComponent.
*
* @return AppComponent
*/
public static synchronized AppComponent getAppComponent() {
if (null == appComponent) {
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(getInstance()))
.httpModule(new HttpModule())
.build();
}
return appComponent;
}
在BaseActivity裡面加如下方法:
protected ActivityComponent getActivityComponent() {
return DaggerActivityComponent.builder()
.appComponent(MyApplication.getAppComponent())
.activityModule(new ActivityModule())
.build();
}
加完這兩個方法之後,肯定會有錯誤提示,重新編譯一遍專案就可以了,如果重新編譯一遍,還是不行,請重新對比一下,哪裡不一樣。
第三步
就是關於mvp的了,從上面圖應該看到了,有一個presenter包,和contract包,我們之前有一篇部落格講過,MVP就是多了很多個介面,這些介面寫在哪呢?就在contract層
MainContract 程式碼如下:
package com.haichenyi.myproject.contract;
import com.haichenyi.myproject.base.BasePresenter;
import com.haichenyi.myproject.base.BaseView;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
public interface MainContract {
interface IView extends BaseView{
}
interface Presenter extends BasePresenter<IView>{
void loadData();
}
}
這裡我需要說明的就是Presenter介面繼承的是IVew,不是BaseView,頁面變化的方法都是在IView接口裡面定義,邏輯處理,網路請求方法都是在Presenter接口裡面定義
MainPresenter 程式碼如下
package com.haichenyi.myproject.presenter;
import com.haichenyi.myproject.base.BaseMvpPresenter;
import com.haichenyi.myproject.contract.MainContract;
import javax.inject.Inject;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
public class MainPresenter extends BaseMvpPresenter<MainContract.IView>
implements MainContract.Presenter {
@Inject
MainPresenter() {
}
@Override
public void loadData() {
baseView.showTipMsg("載入資料");
}
}
這裡我需要說明的是注意繼承BaseMvpPresenter傳的是MainContract.IView,不是BaseView,實現MainContract.Presenter介面,還有一點就是注意構造方法,上面有註解,這裡的loadData裡面應該是我們的網路請求邏輯,這裡我放到後面一篇在說,這裡我先就直接Toast,表示走了這個方法
第四步
就是MainActivity,這裡我貼出程式碼
package com.haichenyi.myproject;
import android.os.Bundle;
import com.haichenyi.myproject.base.BaseMvpActivity;
import com.haichenyi.myproject.contract.MainContract;
import com.haichenyi.myproject.presenter.MainPresenter;
public class MainActivity extends BaseMvpActivity<MainPresenter> implements MainContract.IView {
@Override
protected int getLayoutId(Bundle savedInstanceState) {
return R.layout.activity_main;
}
@Override
protected void initData() {
super.initData();
initToolbar(true, false, true).setMyTitle("主頁").setMoreTitle("更多");
basePresenter.loadData();
}
@Override
protected void initInject() {
getActivityComponent().inject(this);
}
}
這裡我需要說明的是繼承BaseMvpActivity,泛型直接傳MainPresenter,然後,實現MainContract.IView介面,直接用basePresenter呼叫方法,需要實現initInject方法,只要是是繼承BaseMvpActivity的activity,都需要在ActivityComponent()裡面註冊一邊。比方說,LoginActivity也是繼承的BaseMvpActivity,辣麼,在di層的component包下面的ActivityComponent接口裡面定義一個方法
void inject(LoginActivity loginActivity);
在LoginActivity的initInject方法裡面寫同樣的程式碼
getActivityComponent().inject(this);
就像這樣寫就可以了。
總結
寫到這裡,mvp+dagger2基本上完成了,MVP的目的就是解藕,把業務邏輯,網路請求丟在P層,頁面不發生變化,就只用改P層邏輯,從而達到了解藕的目的。dagger2簡化了程式碼,並且,它有著全域性單例模式,和區域性單例模式,優化了我們的記憶體,減少了記憶體浪費。不用每次都去new一個P層物件出來。下一篇,我們就把網路請求加上