理解简述
Android开发技术层出不穷。站在别人肩膀上看风景,的确省了不少事。前一段时间为了锻炼下自己的能力,就从鸿洋大神微信公众号的推送里面,下了个完整的项目,想试着自己跟着写一下,也算是追随下主流的脚步吧,这里给下下载的链接点击打开链接,作品的简介:微阅是一款使用 MVP + Retrofit2 + Rxjava + dagger2 等框架开发的阅读软件。包括新闻、视频、煎蛋三个模块,项目的大部分功能已经完成。 另有 kotlin版本。然后lz就下了项目在电脑上先跑一遍,然后就懵逼了,之前也没接触过dagger2,然后就上网搜了一下:是用来降低各个类之间的耦合度的一个依赖注入框架。
先说下大致的理解吧。假设我们有个类,叫做A,
然后我们有个类叫做B,然后我们需要在A里面使用到B,那么我们之前的做法就是在A里面对B进行实例化,但是这样一来,A和B之间的耦合度自然而然的高了。但是,我们使用dagger2以后的操作是:
1.首先将B的构造方法加上注解@inject;2.在我们的A里面添加@inject B b;这样就完成了B的实例化,我们就可以在A里面随意的使用B的方法和参数了。
其次,如果B的构造方法中有参数的话,如果参数为其他的类的话,则我们需要对这个类进行inject注解,实现B对该类的依赖,当然,有很多情况下,我们构造方法中的参数是不能使用inject注解的,比如第三方库。这时候的操作就是通过添加module来实现对第三方库的依赖,当我们使用module的时候,必须新建的一个文件是component,这个文件的作用就是将module和我们的类A链接起来,这样我们在A里面使用B的时候,系统就会去查找B的构造方法中的参数的提供者,就会通过component然后查找到module,我们在module里面向外部提供了我们需要的这个参数。
有个简图可以看下
我们在module中定义提供我们需要的参数的方法,然后再component里面定义inject方法,链接我们需要使用到module提供参数的类。具体的dagger2 的语法,大家在百度一搜就可以搜到,这里只是通过比较通俗的理解,来理解dagger2的使用。
另外一点就是在看这个项目的时候,发现新建了两个module。一个是提供我们网络请求的module,还有一个是application的module,然后对应的是两个component,我们在httpComponent里面实现inject方法,然后将这个component依赖给applicationComponent,在applicationComponent中,提供的方法是包括我们在两个module中通过provide注解的方法,applicationComponent的使用的地方是application中,然后httpComponent的使用是在我们具体的类中。
项目中的实际应用
跟着具体的代码我们来具体理解下dagger2的使用。
1.@inject
具体的应用就是我们的activity和presenter,我们对presenter的构造方法进行注解
public class ArticleReadPresenter extends BasePresenter<ArticleReadContract.View> implements ArticleReadContract.Presenter {
NewsApi mNewsApi;
@Inject
public ArticleReadPresenter(NewsApi newsApi) {
this.mNewsApi = newsApi;
}
然后在activity里面写下对应的inject,系统就会通过二者之间的对应关系,查找我们依赖类的实例
public abstract class BaseActivity<T1 extends BaseContract.BasePresenter> extends SupportActivity implements IBase, BaseContract.BaseView {
protected View mRootView;
protected Dialog mLoadingDialog = null;
Unbinder unbinder;
@Nullable
@BindView(R.id.SimpleMultiStateView)
SimpleMultiStateView mSimpleMultiStateView;
@Nullable
@Inject
protected T1 mPresenter;
这样,我们在activity里面就可以使用presenter,而不用再去通过new来实例化。
2.@module
我们可以看到presenter中的构造方法是带参的,这里的api的设定就是进行我们的retrofit的一些初始化操作,前文也提到了第三方库是不能通过inject来注解的,所以这里我们新建了一个module文件来提供我们需要的这个api文件
@Module
public class HttpModule {
@Provides
OkHttpClient.Builder provideOkHttpClient() {
// 指定缓存路径,缓存大小100Mb
Cache cache = new Cache(new File(MyApp.getContext().getCacheDir(), "HttpCache"),
1024 * 1024 * 100);
return new OkHttpClient().newBuilder().cache(cache)
.retryOnConnectionFailure(true)
.addInterceptor(RetrofitConfig.sLoggingInterceptor)
.addInterceptor(RetrofitConfig.sRewriteCacheControlInterceptor)
.addNetworkInterceptor(RetrofitConfig.sRewriteCacheControlInterceptor)
.connectTimeout(10, TimeUnit.SECONDS);
}
// @Provides
// Retrofit.Builder provideBuilder(OkHttpClient okHttpClient) {
// return new Retrofit.Builder()
// .addConverterFactory(GsonConverterFactory.create())
// .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
// .client(okHttpClient);
// }
@Provides
NewsApi provideNetEaseApis(OkHttpClient.Builder builder) {
builder.addInterceptor(RetrofitConfig.sQueryParameterInterceptor);
Retrofit.Builder retrofitBuilder = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(builder.build());
return NewsApi.getInstance(retrofitBuilder
.baseUrl(ApiConstants.sIFengApi)
.build().create(NewsApiService.class));
}
@Provides
JanDanApi provideJanDanApis(OkHttpClient.Builder builder) {
Retrofit.Builder retrofitBuilder = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(builder.build());
return JanDanApi.getInstance(retrofitBuilder
.baseUrl(ApiConstants.sJanDanApi)
.build().create(JanDanApiService.class));
}
我们可以看到首先是将我们新建的module文件通过@module给注解,告诉系统,这是个module文件,然后在里面写我们提供参数的方法,可以看到方法的命名是provide+参数,首先第一个参数是初始化OK HTTP client,因为在下面两个方法中,是需要使用到的。下面的两个方法就是提供我们需要的api方法,可以看到我们是进行了retrofit的初始化。
我们还新建了一个ApplicationModule文件:
@Module
public class ApplicationModule {
private Context mContext;
public ApplicationModule(Context context) {
this.mContext = context;
}
@Provides
MyApp provideApplication() {
return (MyApp) mContext.getApplicationContext();
}
@Provides
Context provideContext() {
return mContext;
}
提供了application和context。
3.component
component就是起到桥梁的作用。
先看HTTPComponent,
@Component(dependencies = ApplicationComponent.class)
public interface HttpComponent {
void inject(VideoFragment videoFragment);
void inject(DetailFragment detailFragment);
void inject(JdDetailFragment jdDetailFragment);
void inject(ImageBrowseActivity imageBrowseActivity);
void inject( com.will.weiyue.ui.news.DetailFragment detailFragment);
void inject(ArticleReadActivity articleReadActivity);
void inject(NewsFragment newsFragment);
}
里面定义了很多的inject方法,里面的参数代表的是,谁需要使用到我们在module中定义的方法的类。同时还可以看到这个接口的注解是通过component然后是dependencies,通过这个方式,我们就实现了其他的component对本component的依赖。
再看ApplixationComponent.
@Component(modules = {ApplicationModule.class,HttpModule.class})
public interface ApplicationComponent {
MyApp getApplication();
NewsApi getNetEaseApi();
JanDanApi getJanDanApi();
Context getContext();
}
本component的中的方法都是我们在上面提供的module中通过provide注解的方法。
4.application Component使用的地方
applicationcomponent使用是在application中进行了applicationmodule以及HTTPmodule的初始化。
mApplicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.httpModule(new HttpModule())
.build();
5.inject方法中的参数对应的类中的实现
DaggerHttpComponent.builder()
.applicationComponent(appComponent)
.build()
.inject(this);
在我们的activity方法中写上上面的代码,就实现了我们的activity和我们的module之间通过component之间的链接,就可以使用了。
本文也算写完了,简单来说,如果我们的被依赖类的构造方法是没有参数的话,或者参数是可以通过inject注解的类的话,那么我们只需要使用inject分别注解依赖类和在被依赖类中inject就行;如果被依赖的类的构造方法牵扯到不能使用inject的话,则需要使用module来提供我们的这个参数,然后通过component来实现这种链接关系。
具体的使用方法以及参考这篇博文Dagger2从入门到放弃再到恍然大悟,感觉看完自己参考着项目也算是简单的理解了dagger2的使用方法吧,关键是不能只看,跟着写写才能明白其中的奥妙。