1. 程式人生 > >從零開始搭建一個主流專案框架(二)—MVP+Dagger2

從零開始搭建一個主流專案框架(二)—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,專案結構圖如下:

專案結構圖.png

  這裡給出的是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層物件出來。下一篇,我們就把網路請求加上

專案連結