0. 序言
- 之前寫過一篇名為"看完不會寫MVP架構(gòu)我跪搓板"的博文,收到一些閱讀者的建議,希望能夠?qū)xjava的生命周期進(jìn)行管理以及添加Dagger到MVP架構(gòu)中,所以今天抽一點(diǎn)時間寫一篇拿來即可于實戰(zhàn)的Demo。假如對基本的MVP架構(gòu)不甚了解的可先看上述博文,因為這篇博文重點(diǎn)講解Dagger部分,博文地址:http://www.itdecent.cn/p/44399bf0c8d2 (有些地方還是講得不夠清楚,有些地方?jīng)]有講,會對Dagger方面的文章進(jìn)行再次梳理)
- Demo的技術(shù)組成:Mvp+Retrofit2+Rxjava2+RxLifeCycle2+Dagger2,其中RxLifeCycle2即為管理Rxjava生命周期的三方庫。
- 有些類代碼較多,只拿核心代碼呈現(xiàn),完整的項目地址見文尾。
1. 博文目錄
- 添加依賴
- 創(chuàng)建項目基本目錄
- 實現(xiàn)Model
- 定義契約接口NewsInfoContract
- 實現(xiàn)Presenter
- 實現(xiàn)View
- 補(bǔ)充網(wǎng)絡(luò)配置代碼
- 適配Android28網(wǎng)絡(luò)請求
- 添加Dagger2
- 實現(xiàn)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. 創(chuàng)建項目基本目錄

4. 實現(xiàn)Model
- 創(chuàng)建實體類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;
...
}
}
}
- 定義獲取網(wǎng)絡(luò)數(shù)據(jù)的接口類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的實現(xiàn)類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. 實現(xiàn)Presenter
- 編寫NewsInfoContract.Presenter接口的實現(xiàn)類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. 實現(xiàn)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 && newsInfo.getResult() != null && 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. 補(bǔ)充網(wǎng)絡(luò)配置代碼
- 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;
}
}
- 網(wǎng)絡(luò)請求接口服務(wù)類NewsService:
public interface NewsService {
@GET("toutiao/index")
Observable<NewsInfo> getNewsInfo(@Query("type") String type, @Query("key")String key);
}
8. 適配Android28網(wǎng)絡(luò)請求
- 在res目錄下創(chuàng)建名為xml的文件夾,并在文件夾里面創(chuàng)建名為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實現(xiàn)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中不需要指明注入的目標(biāo),而需要提供實例對象的時候,可以用“NetTask getNetTask();”這種形式表示,指明返回的是NetTask,返回值很重要。
- 實現(xiàn)NewsInfoTask在App全局單例:
@Singleton可以保證局部單例,即NetTaskComponent下的注入目標(biāo)中NewsInfoTask的內(nèi)存地址都是同一個,而一旦創(chuàng)建其他Component并關(guān)聯(lián)NetTaskModule,此時創(chuàng)建出的NewsInfoTask的內(nèi)存地址就會發(fā)生變化,所以保證全局單例我們只能初始化一次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對象的內(nèi)存地址唯一了。
9.2 Presenter注入MainActivity
- @Inject修飾構(gòu)造方法
@Inject
public NewsInfoPresenter(NewsInfoContract.View view,NetTask netTask) {
mView = view;
mNetTask = netTask;
}
說明:@Inject修飾構(gòu)造方法意思是告訴Dagger2可以用這個構(gòu)造方法構(gòu)建NewsInfoPresenter。只有構(gòu)造方法上有@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創(chuàng)建setPresenter方法,并用@Inject修飾
@Inject
void setPresenter(){
mView.setPresenter(this);
}
說明:@Inject修飾方法的意思是方法注入,這里是把NewsInfoPresenter注入給MainActivity。方法注入是在構(gòu)造方法后執(zhí)行的。方法注入需要構(gòu)造方法使用@Inject注解修飾,不然方法注入無法執(zhí)行。
- MainActivity中用@Inject修飾變量NewsInfoPresenter:
@Inject
NewsInfoPresenter mPresenter;
說明:@Inject修飾變量意思是NewsInfoPresenter需要依賴注入。
- MainActivity實現(xiàn)setPresenter方法
@Override
public void setPresenter(NewsInfoPresenter presenter) {
mPresenter = presenter;
}
綜上:以上幾步完成了Persenter注入到MainActivity。
9.3 NewsInfoTask和MainActivity注入到了Presenter
- 創(chuàng)建名為ActivityScoped的自定義Scope:
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScoped {
}
- 創(chuàng)建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。
- 創(chuàng)建名為MainActivityComponent的Component:
@ActivityScoped
@Component(modules = NewsInfoModule.class,dependencies =NetTaskComponent.class)
public interface MainActivityComponent {
void inject(MainActivity mainActivity);
}
說明:
① dependencies意思是把NetTaskComponent的內(nèi)容也拿過來注入。
② @ActivityScoped之所以創(chuàng)建自定義Scope是因為如果dependencies中的NetTaskComponent用了@Singleton修飾,這里就不能使用@Singleton修飾了。
- 修改MainActivity完成注入:
DaggerMainActivityComponent.builder().newsInfoModule(new NewsInfoModule(this))
.netTaskComponent(App.get(this).getNetTaskComponent()).build().inject(this);
說明:因為Presenter需要兩個參數(shù),所以這里一句話就把需要的兩個參數(shù)傳入了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需要的兩個參數(shù)。然后看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的構(gòu)造方法,我們看下:
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。他們調(diào)用給了get方法:
@Override
public NewsInfoPresenter get() {
return MembersInjectors.injectMembers(
newsInfoPresenterMembersInjector,
new NewsInfoPresenter(viewProvider.get(), netTaskProvider.get()));
}
說明:看到這里,你會清晰的看到NetTask和MainActivity注入到了Presenter,而這個get方法何時調(diào)用的呢?我們看下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;
說明:到這里會發(fā)現(xiàn)其實inject的目的就是把兩個參數(shù)賦值給this中的presenter,而賦值離不開new,自然離不開構(gòu)造方法,而構(gòu)造方法那里也離不開@Inject。而這也正是Dagger2的原理所在,通過工廠方法把實例對象賦值給注入目標(biāo)的用@Inject所修飾的成員變量。
10. 實現(xiàn)RetrofitManager單例
- 去除之前實現(xiàn)單例模式的以下代碼
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,因為是單例模式,從創(chuàng)建就開始存在直到App應(yīng)用程序退出,所以不會有內(nèi)存泄露的情況。
- MainActivity和NewsInfoTask中修改獲取NetTaskComponent的代碼。
11. 代碼下載地址:
https://github.com/OnlyYouMyLove/Mvp_Dagger2
這篇文章的Model和View是通過Build參數(shù)的方式通過Presenter的構(gòu)造方法注入;還可以Model單獨(dú)注入到Presenter,View通過Build參數(shù)的方式注入到Presenter,這里不再給出代碼,感興趣的可以留言。
12. 后續(xù)
如果大家喜歡這篇文章,歡迎點(diǎn)贊;如果想看更多 Dagger 方面的技術(shù),歡迎關(guān)注!