手把手帶你搭建Mvp+Dagger架構

0. 序言

  • 以前寫過一篇名爲"看完不會寫MVP架構我跪搓板"的博文,收到一些閱讀者的建議,但願可以對Rxjava的生命週期進行管理以及添加Dagger到MVP架構中,因此今天抽一點時間寫一篇拿來便可於實戰的Demo。

1. 博文目錄

  • 添加依賴php

  • 建立項目基本目錄css

  • 實現Modeljava

  • 定義契約接口NewsInfoContractreact

  • 實現Presenterandroid

  • 實現Viewbash

  • 補充網絡配置代碼網絡

  • 適配Android28網絡請求架構

  • 添加Dagger2ide

  • 實現RetrofitManager單例學習

2. 添加依賴

implementation 'com.squareup.retrofit2:retrofit:2.5.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
    implementation 'io.reactivex.rxjava2:rxjava:2.2.4'
    implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
    implementation 'com.squareup.okhttp3:okhttp:3.12.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.5.0'
    implementation 'com.trello.rxlifecycle2:rxlifecycle-android-lifecycle:2.2.1'
複製代碼

3. 建立項目基本目錄


4. 實現Model

  • 建立實體類NewsInfo:
public class NewsInfo {
    private String reason;
    private ResultBean result;
    ...
    public static class ResultBean {
        private String stat;
        private List<DataBean> data;
        ...
        public static class DataBean {
            private String uniquekey;
            private String title;
            private String date;
            private String category;
            private String author_name;
            private String url;
            private String thumbnail_pic_s;
            private String thumbnail_pic_s02;
            private String thumbnail_pic_s03;
            ...
        }
    }
}
複製代碼
  • 定義獲取網絡數據的接口類NetTask:
public interface NetTask {
    void execute(LifecycleProvider lifecycleProvider, String type, LoadTasksCallBack callBack);
}
複製代碼
public interface LoadTasksCallBack {
    void OnSuccess(NewsInfo newsInfo);
    void OnStart();
    void onFailed();
    void onFinish();
}
複製代碼
  • 編寫NetTask的實現類NewsInfoTask:
public class NewsInfoTask implements NetTask {

    private Disposable mDisposable;

    private NewsInfoTask() {

    }

    public static NewsInfoTask getInstance() {
        return NewsInfoTaskHolder.sNewsInfoFask;
    }

    private static class NewsInfoTaskHolder {
        private static final NewsInfoTask sNewsInfoFask = new NewsInfoTask();
    }

    @Override
    public void execute(LifecycleProvider lifecycleProvider,String type, final LoadTasksCallBack callBack) {

        RetrofitManager.getInstance().getRetrofit(Constant.BASEURL).create(NewsService.class)
                .getNewsInfo(type, BuildConfig.NewKey)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(lifecycleProvider.<Long>bindUntilEvent(Lifecycle.Event.ON_DESTROY))
                .subscribe(new Observer<NewsInfo>() {
                    @Override
                    public void onSubscribe(Disposable disposable) {
                        mDisposable= disposable;
                        callBack.OnStart();
                    }

                    @Override
                    public void onNext(NewsInfo newsInfo) {
                        callBack.OnSuccess(newsInfo);
                    }

                    @Override
                    public void onError(Throwable e) {
                        callBack.onFailed();
                    }

                    @Override
                    public void onComplete() {
                        callBack.onFinish();
                        mDisposable.dispose();
                    }
                });
    }
}
複製代碼

6. 定義契約接口NewsInfoContract:

public interface NewsInfoContract {
    interface Presenter{
        void getNewsInfo(LifecycleProvider lifecycleProvider, String type);
    }

    interface View {
        void setNewsInfo(NewsInfo newsInfo);
        void showLoading();
        void hideLoading();
        void showError();
    }
}
複製代碼

7. 實現Presenter

  • 編寫NewsInfoContract.Presenter接口的實現類NewsInfoPresenter:
public class NewsInfoPresenter implements NewsInfoContract.Presenter,LoadTasksCallBack {
    private NetTask mNetTask;
    private NewsInfoContract.View mView;

    public NewsInfoPresenter(NetTask netTask,NewsInfoContract.View view) {
        mNetTask = netTask;
        mView = view;
    }

        @Override
    public void getNewsInfo(LifecycleProvider lifecycleProvider, String type) {
        mNetTask.execute(lifecycleProvider,type,this);
    }

    @Override
    public void OnSuccess(NewsInfo newsInfo) {
        mView.setNewsInfo(newsInfo);
    }

    @Override
    public void OnStart() {
        mView.showLoading();
    }

    @Override
    public void onFailed() {
        mView.showError();
        mView.hideLoading();
    }

    @Override
    public void onFinish() {
        mView.hideLoading();
    }
}  
複製代碼

6. 實現View

public class MainActivity extends AppCompatActivity implements NewsInfoContract.View {

    private NewsInfoContract.Presenter mPresenter = new NewsInfoPresenter(NewsInfoTask.getInstance(),this);
    LifecycleProvider<Lifecycle.Event> lifecycleProvider = AndroidLifecycle.createLifecycleProvider(this);
    private TextView mNew_Content;
    private Dialog mDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mNew_Content = findViewById(R.id.tv_content);
        mDialog = new ProgressDialog(this);
        mDialog.setTitle(R.string.dialog_get_info);
        findViewById(R.id.bt_get_news).setOnClickListener(v -> {
            mPresenter.getNewsInfo(lifecycleProvider,Constant.DEFAULT_TYPE);
        });
    }

    @Override
    public void setNewsInfo(NewsInfo newsInfo) {
        if (newsInfo != null &amp;&amp; newsInfo.getResult() != null &amp;&amp; newsInfo.getResult().getData() != null) {
            mNew_Content.setText(newsInfo.getResult().getData().get(0).getTitle());
        }
    }

    @Override
    public void showLoading() {
        mDialog.show();
    }

    @Override
    public void hideLoading() {
        if (mDialog.isShowing())
            mDialog.dismiss();
    }

    @Override
    public void showError() {
        Toast.makeText(this, R.string.toast_net_tip, Toast.LENGTH_SHORT).show();
    }

}
複製代碼

7. 補充網絡配置代碼

  • Retrofit 管理類:
public class RetrofitManager {

    private static Retrofit sRetrofit = null;
    private static String sUrl = "";
    private static final int TIMEOUT = 20;

    private RetrofitManager() {
    }

    public static RetrofitManager getInstance() {
        return RetrofitManagerHolder.sInstance;
    }

    private static class RetrofitManagerHolder {
        private static final RetrofitManager sInstance = new RetrofitManager();
    }

    public Retrofit getRetrofit(String baseUrl) {
        sUrl = baseUrl;
        if (sRetrofit == null) {
            return create();
        } else {
            return sRetrofit;
        }
    }

    private Retrofit create() {

        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        if (BuildConfig.DEBUG) {
            loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        } else {
            loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
        }

        OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(loggingInterceptor)
                .connectTimeout(TIMEOUT,TimeUnit.SECONDS)
                .readTimeout(TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(TIMEOUT, TimeUnit.SECONDS)
                .retryOnConnectionFailure(true).build();

        sRetrofit = new Retrofit.Builder()
                .baseUrl(sUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(okHttpClient)
                .build();
        return sRetrofit;
    }
}
複製代碼
  • 網絡請求接口服務類NewsService:
public interface NewsService {

    @GET("toutiao/index")
    Observable<NewsInfo> getNewsInfo(@Query("type") String type, @Query("key")String key);
}
複製代碼

8. 適配Android28網絡請求

  • 在res目錄下建立名爲xml的文件夾,並在文件夾裏面建立名爲network_security_config的xml文件:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>
複製代碼
  • AndroidManifest配置文件中添加配置:
android:networkSecurityConfig="@xml/network_security_config"
複製代碼

9. 添加Dagger2

9.0 分析

這裏咱們須要把NewsInfoTask和MainActivity注入到了Presenter中,把Presenter注入到了MainActivity中。

9.1 Dagger2實現NewsInfoTask單例

  • 去掉NewsInfoTask如下代碼:
private NewsInfoTask() {

    }

    public static NewsInfoTask getInstance() {
        return NewsInfoTaskHolder.sNewsInfoFask;
    }

    private static class NewsInfoTaskHolder {
        private static final NewsInfoTask sNewsInfoFask = new NewsInfoTask();
    }
複製代碼
  • 新建名爲NetTaskModule的Module:
@Module
public class NetTaskModule {
    @Singleton
    @Provides
    public NetTask provideNewsInfoTask(){
        return new NewsInfoTask();
    }
}
複製代碼

說明:生成NewsInfoTask的實例對象,並用@Singleton修飾。

  • 新建名爲NetTaskComponent的Component:
@Singleton
@Component(modules = NetTaskModule.class)
public interface NetTaskComponent {
    NetTask getNetTask();
}
複製代碼

說明:
① NetTaskModule 中的方法provideNewsInfoTask用@Singleton修飾,NetTaskComponent也必須用@Singleton修飾。
② NetTaskComponent中不須要指明注入的目標,而須要提供實例對象的時候,能夠用「NetTask getNetTask();」這種形式表示,指明返回的是NetTask,返回值很重要。

  • 實現NewsInfoTask在App全局單例:
    @Singleton能夠保證局部單例,即NetTaskComponent下的注入目標中NewsInfoTask的內存地址都是同一個,而一旦建立其餘Component並關聯NetTaskModule,此時建立出的NewsInfoTask的內存地址就會發生變化,因此保證全局單例咱們只能初始化一次Component,而初始化的地方就是Application:
public class App extends Application {

    private NetTaskComponent mNetTaskComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        mNetTaskComponent = DaggerNetTaskComponent.builder().build();
    }

    public static App get(Context context){
        return (App)context.getApplicationContext();
    }

    public NetTaskComponent getNetTaskComponent() {
        return mNetTaskComponent;
    }
}
複製代碼

說明:這裏經過.builder().build()來獲取NetTaskComponent。須要NewsInfoTask只經過NetTaskComponent,就能夠保證NewsInfoTask對象的內存地址惟一了。

9.2 Presenter注入MainActivity

  • @Inject修飾構造方法
@Inject
    public NewsInfoPresenter(NewsInfoContract.View view,NetTask netTask) {
        mView = view;
        mNetTask = netTask;
    }
複製代碼

說明:@Inject修飾構造方法意思是告訴Dagger2能夠用這個構造方法構建NewsInfoPresenter。只有構造方法上有@Inject註解修飾,NewsInfoPresenter才能夠對外提供實例對象。

  • NewsInfoContract.View添加setPresenter方法
public interface NewsInfoContract {
    interface Presenter{
        void getNewsInfo(LifecycleProvider lifecycleProvider, String type);
    }

    interface View {
        void setNewsInfo(NewsInfo newsInfo);
        void showLoading();
        void hideLoading();
        void showError();
        void setPresenter(NewsInfoPresenter presenter);
    }
}
複製代碼
  • NewsInfoPresenter建立setPresenter方法,並用@Inject修飾
@Inject
    void setPresenter(){
        mView.setPresenter(this);
    }
複製代碼

說明:@Inject修飾方法的意思是方法注入,這裏是把NewsInfoPresenter注入給MainActivity。方法注入是在構造方法後執行的。方法注入須要構造方法使用@Inject註解修飾,否則方法注入沒法執行。

  • MainActivity中用@Inject修飾變量NewsInfoPresenter:
@Inject
    NewsInfoPresenter mPresenter;
複製代碼

說明:@Inject修飾變量意思是NewsInfoPresenter須要依賴注入。

  • MainActivity實現setPresenter方法
@Override
    public void setPresenter(NewsInfoPresenter presenter) {
        mPresenter = presenter;
    }
複製代碼

綜上:以上幾步完成了Persenter注入到MainActivity。

9.3 NewsInfoTask和MainActivity注入到了Presenter

  • 建立名爲ActivityScoped的自定義Scope:
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScoped {
}
複製代碼
  • 建立NewsInfoModule的Module:
@Module
public class NewsInfoModule {

    private NewsInfoContract.View mView;

    public NewsInfoModule(NewsInfoContract.View view) {
        mView = view;
    }

    @Provides
    public NewsInfoContract.View provideNewsInfoContractView() {
        return mView;
    }

}
複製代碼

說明:這裏是爲了將NewsInfoContract.View注入給MainActivity。

  • 建立名爲MainActivityComponent的Component:
@ActivityScoped
@Component(modules = NewsInfoModule.class,dependencies =NetTaskComponent.class)
public interface MainActivityComponent {
    void inject(MainActivity mainActivity);
}
複製代碼

說明:
① dependencies意思是把NetTaskComponent的內容也拿過來注入。
② @ActivityScoped之因此建立自定義Scope是由於若是dependencies中的NetTaskComponent用了@Singleton修飾,這裏就不能使用@Singleton修飾了。

  • 修改MainActivity完成注入:
DaggerMainActivityComponent.builder().newsInfoModule(new NewsInfoModule(this))
                .netTaskComponent(App.get(this).getNetTaskComponent()).build().inject(this);
複製代碼

說明:由於Presenter須要兩個參數,因此這裏一句話就把須要的兩個參數傳入了Presenter。咱們看下DaggerMainActivityComponent的源碼:

public Builder newsInfoModule(NewsInfoModule newsInfoModule) {
      this.newsInfoModule = Preconditions.checkNotNull(newsInfoModule);
      return this;
    }

    public Builder netTaskComponent(NetTaskComponent netTaskComponent) {
      this.netTaskComponent = Preconditions.checkNotNull(netTaskComponent);
      return this;
    }
複製代碼

說明:.newsInfoModule和.netTaskComponent接收Presenter須要的兩個參數。而後看build():

public MainActivityComponent build() {
      if (newsInfoModule == null) {
        throw new IllegalStateException(NewsInfoModule.class.getCanonicalName() + " must be set");
      }
      if (netTaskComponent == null) {
        throw new IllegalStateException(NetTaskComponent.class.getCanonicalName() + " must be set");
      }
      return new DaggerMainActivityComponent(this);
    }
複製代碼

說明:這裏把build()所在的Build傳入了DaggerMainActivityComponent的構造方法,咱們看下:

private DaggerMainActivityComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }
複製代碼

說明:再看下initialize方法:

this.provideNewsInfoContractViewProvider =
        NewsInfoModule_ProvideNewsInfoContractViewFactory.create(builder.newsInfoModule);

    this.getNetTaskProvider =
        new Factory<NetTask>() {
          private final NetTaskComponent netTaskComponent = builder.netTaskComponent;

          @Override
          public NetTask get() {
            return Preconditions.checkNotNull(
                netTaskComponent.getNetTask(),
                "Cannot return null from a non-@Nullable component method");
          }
        };

    this.newsInfoPresenterProvider =
        NewsInfoPresenter_Factory.create(
            newsInfoPresenterMembersInjector,
            provideNewsInfoContractViewProvider,
            getNetTaskProvider);
複製代碼

說明:newsInfoModule獲得provideNewsInfoContractViewProvider,netTaskComponent獲得getNetTaskProvider,而後把二者,放入NewsInfoPresenter_Factory.create方法中:

public static Factory<NewsInfoPresenter> create( MembersInjector<NewsInfoPresenter> newsInfoPresenterMembersInjector, Provider<NewsInfoContract.View> viewProvider, Provider<NetTask> netTaskProvider) {
    return new NewsInfoPresenter_Factory(
        newsInfoPresenterMembersInjector, viewProvider, netTaskProvider);
  }
複製代碼

說明:再看NewsInfoPresenter_Factory

public NewsInfoPresenter_Factory( MembersInjector<NewsInfoPresenter> newsInfoPresenterMembersInjector, Provider<NewsInfoContract.View> viewProvider, Provider<NetTask> netTaskProvider) {
    assert newsInfoPresenterMembersInjector != null;
    this.newsInfoPresenterMembersInjector = newsInfoPresenterMembersInjector;
    assert viewProvider != null;
    this.viewProvider = viewProvider;
    assert netTaskProvider != null;
    this.netTaskProvider = netTaskProvider;
  }
複製代碼

說明:用viewProvider封裝了NewsInfoContract.View;用netTaskProvider封裝了NetTask。他們調用給了get方法:

@Override
  public NewsInfoPresenter get() {
    return MembersInjectors.injectMembers(
        newsInfoPresenterMembersInjector,
        new NewsInfoPresenter(viewProvider.get(), netTaskProvider.get()));
  }
複製代碼

說明:看到這裏,你會清晰的看到NetTask和MainActivity注入到了Presenter,而這個get方法什麼時候調用的呢?咱們看下inject方法:

@Override
  public void inject(MainActivity mainActivity) {
    mainActivityMembersInjector.injectMembers(mainActivity);
  }
複製代碼

說明:再接着看injectMembers方法:

@Override
    public void injectMembers(MainActivity instance) {
      if (instance == null) {
        throw new NullPointerException("Cannot inject members into a null reference");
      }
      instance.mPresenter = mPresenterProvider.get();
    }
複製代碼

說明:mPresenterProvider是誰呢:

private final Provider<NewsInfoPresenter> mPresenterProvider;
複製代碼

說明:到這裏會發現其實inject的目的就是把兩個參數賦值給this中的presenter,而賦值離不開new,天然離不開構造方法,而構造方法那裏也離不開@Inject。而這也正是Dagger2的原理所在,經過工廠方法把實例對象賦值給注入目標的用@Inject所修飾的成員變量。

10. 實現RetrofitManager單例

  • 去除以前實現單例模式的如下代碼
private RetrofitManager() {
    }

    public static RetrofitManager getInstance() {
        return RetrofitManagerHolder.sInstance;
    }

    private static class RetrofitManagerHolder {
        private static final RetrofitManager sInstance = new RetrofitManager();
    }
複製代碼
  • NetTaskModule中添加如下方法:
@Singleton
    @Provides
    public RetrofitManager provideRetrofitManager(){
        return new RetrofitManager();
    }
複製代碼
  • NetTaskComponent中添加如下方法:
@Singleton
@Component(modules = NetTaskModule.class)
public interface NetTaskComponent {
    NetTask getNetTask();
    RetrofitManager getRetrofitManager();
}
複製代碼
  • 修改Applicaion的代碼:
public class App extends Application {

    private static NetTaskComponent mNetTaskComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        mNetTaskComponent = DaggerNetTaskComponent.builder().build();
    }

    public static NetTaskComponent getNetTaskComponent() {
        return mNetTaskComponent;
    }
}
複製代碼

說明:以前的方式須要傳入Context,可是NewsInfoTask中並無Context,因此咱們這裏修改成static,由於是單例模式,從建立就開始存在直到App應用程序退出,因此不會有內存泄露的狀況。

  • MainActivity和NewsInfoTask中修改獲取NetTaskComponent的代碼。

+qq羣457848807:。獲取以上高清技術思惟圖,以及相關技術的免費視頻學習資料

相關文章
相關標籤/搜索