好了,介绍一下Dagger2吧!
Dagger2 是Google 的新一代依赖注入框架(依赖注入不讲,你都看到这篇文章了,那你应该懂,如果不懂,请度娘、谷哥之,此文不废话),Dagger2是Dagger1的分支,但两个框架没有严格的继承关系,亦如Struts1 和Struts2 的关系!
那就有人问了,为什么要用Dagger2?
回答:解耦(DI的特性),易于测试(DI的特性),高效(不使用反射,google官方说名比Dagger快13%),易混淆(apt方式生成代码,混淆后依然正常使用)
打开官网,映入眼帘的第一句话便是:
Dagger ‡ A fast dependency injector for Android and Java. - Google
如果你是个想要很简单,并且速度很快的就能上手使用Dagger2的客官,你可以点击窗口右上角的X,关闭该文章了!
Dagger2的学习曲线相对比较陡峭,需要理解的概念也较多,需要一点一点的理解,Dagger2概念还是推荐看下面这篇文章。
本文只讲基础概念和使用,具体的概念请参见官方文档或其他博文!
这个注解是用来说明该注解下方的属性或方法需要依赖注入。(如果使用在类构造方法上,则该类也会被注册在DI容器中作为注入对象。很重要,理解这个,就能理解Presenter注入到Activity的步骤!)
在@Module注解的类中,使用@Provider注解,说明提供依赖注入的具体对象
简单说就是,可以通过Component访问到Module中提供的依赖注入对象。假设,如果有两个Module,AModule、BModule,如果Component只注册了AModule,而没有注册BModule,那么BModule中提供的对象,无法进行依赖注入!
该注解从名字上就能知道,它是子Component,会完全继承父Component的所有依赖注入对象!
被注解的对象,在App中是单例存在的!
用来标注依赖注入对象的适用范围。
因为Dagger2 的以来注入是使用类型推断的,所以同一类型的对象就无法区分,可以使用@Named注解区分同一类型对象,可以理解为对象的别名!
项目根目录下的build.gradle。根目录、根目录、根目录,重要事情说三遍!
在dependencies代码块中加入
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
加完成后的build.gradle如下
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
//配置DBFlow
}
}
allprojects {
repositories {
maven { url "https://www.jitpack.io" }
jcenter()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
如果你使用DB-Flow 或 ButterKnif已经添加过了,就不用添加了!
allprojects的内容不需要与我贴出来的完全一致。
使用Dagger2的项目下的build.gradle,一般都是app文件夹下的。
在build.gradle文件顶部 apply plugin: ‘com.android.application’ 下方添加
apply plugin: 'android-apt'
dependencies代码块中添加Dagger2的依赖关系
//使用APT生成工具,生成需要的DI代码
apt 'com.google.dagger:dagger-compiler:2.5'
//JSR250的jar包,使用这个和使用glassFish的那个一样,仅为了使用@Inject 和@Named注解
provided 'javax.annotation:jsr250-api:1.0'
//Dagger2 的依赖
compile 'com.google.dagger:dagger:2.5'
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
该类用于区分与@Sigleton或其他@Scope的作用域。
我们首先来分析一下,需要哪些类是单例的,单例创建的,都和Application关联起来。
1、提供 shredPreference,创建AppModule
@Module
public class AppModule {
private Context context;
public AppModule(DaggerApplication application) {
this.context = application;
}
@Singleton
@Provides
public Context ProviderApplicationContext(){
return context;
}
@Singleton
@Provides
@Named("default")
public SharedPreferences providerDefaultSharedPreferences(){
return PreferenceManager.getDefaultSharedPreferences(context);
}
@Singleton
@Provides
@Named("encode")
public SharedPreferences providerEncodeSharedPreferences(){
return context.getSharedPreferences("encode",Context.MODE_PRIVATE);
}
}
2、因为是使用的Retrofit 所以要提供 OkhttpClient ,RetrofitClient
创建OkhttpModule
@Module
public class OkhttpModule {
@Singleton
@Provides
@Named("cache")
public OkHttpClient providerAutoCacheOkHttpClient(){
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
Interceptor cacheInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
String cacheControl = request.cacheControl().toString();
if (TextUtils.isEmpty(cacheControl)) {
cacheControl = "public, max-age=" + 3600 * 6 + " ,max-stale=2419200";
}
return response.newBuilder()
.header("Cache-Control", cacheControl)
.removeHeader("Pragma")
.build();
}
};
return new OkHttpClient.Builder()
.addNetworkInterceptor(interceptor)
.addNetworkInterceptor(cacheInterceptor)
.retryOnConnectionFailure(true)
.connectTimeout(10, TimeUnit.SECONDS)
.build();
}
@Singleton
@Provides
@Named("default")
public OkHttpClient providerOkHttpClient(){
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return new OkHttpClient.Builder()
.addNetworkInterceptor(interceptor)
.retryOnConnectionFailure(true)
.connectTimeout(10, TimeUnit.SECONDS)
.build();
}
}
创建RetrofitModule
@Module
public class RetrofitModule {
@Singleton
@Provides
public LocalRetrofit providerLocalRetrofit(@Named("default") OkHttpClient okHttpClient){
return new LocalRetrofit(okHttpClient);
}
@Singleton
@Provides
public TaobaoRetrofit providerTaobaoRetrofit(@Named("cache") OkHttpClient okHttpClient){
return new TaobaoRetrofit(okHttpClient);
}
}
这里因为有时候的http请求是针对多个地址的,所以我又封装了两个提供retrofit的类
TaobaoRetrofit
private static final String BASE_URL = "http://ip.taobao.com/";
private static Retrofit retrofit;
public TaobaoRetrofit(OkHttpClient okHttpClient) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(FastJsonConverterFactory.create())
.build();
}
public Retrofit getRetrofit() {
return retrofit;
}
LocalRetrofit
public class LocalRetrofit {
private static final String BASE_URL = "http://xxxxxx.xxx.xxxx/";
private static Retrofit retrofit;
public LocalRetrofit(OkHttpClient okHttpClient) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(FastJsonConverterFactory.create())
.build();
}
public Retrofit getRetrofit() {
return retrofit;
}
}
我希望网络请求中的Service对调用者是黑盒,调用者只需要知道调用哪个Service即可,创建过程不需要了解,所以又提供了ServiceModule
LocalServiceModule
@Module
public class LocalServiceModule {
@Singleton
@Provides
public UserService providerUserService(LocalRetrofit retrofit){
return retrofit.getRetrofit().create(UserService.class);
}
}
TaobaoIPLocationServiceModule
@Module
public class TaobaoIPLocationServiceModule {
@Singleton
@Provides
public TaobaoIPLocationService proidverIPLocationServiceModule(TaobaoRetrofit taoBaoRetrofitClient) {
return taoBaoRetrofitClient.getRetrofit().create(TaobaoIPLocationService.class);
}
}
单例的module 创建完毕!
@Singleton
//关键代码在这!component会把Module里的提供的对象,注册到容器里
@Component(modules = {AppModule.class,
OkhttpModule.class,
RetrofitModule.class,
LocalServiceModule.class,
TaobaoIPLocationServiceModule.class})
public interface AppComponent {
//SubComponent 继承当前Component
MainComponent addSub(MainModule mainModule);
}
点击绿色下箭头按钮,make project。
Dagger2会自动生成Dagger前缀的Dagger注入工具。
public class DaggerApplication extends Application {
private static AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
}
public static DaggerApplication get(Context context) {
return (DaggerApplication) context.getApplicationContext();
}
private void setupApplicationComponent() {
//Dagger开头的注入类DaggerAppComponent
appComponent = DaggerAppComponent.builder()
//此时appModule方法是过时方法,因为我们没有使用到任何一个module中提供的对象
.appModule(new AppModule(this))
.build();
}
//获取AppComponent 以便于SubComponent继承
public AppComponent getAppComponent() {
if(appComponent == null){
this.setupApplicationComponent();
}
return appComponent;
}
}
提供了那么多对象,到底怎么用???
下面是真正的正片、正片、正片。
[码字。。。。好累 (?_?)]
因为Activity的类的构造器,我们无法加入@Inject注解,所以必须提供Module才能提供View接口的实例化对象。
@Module
public class MainModule {
private MainContract.View view;
//构造方法传递View 接口的实例化对象
public MainModule(MainContract.View view){
this.view = view;
}
//在DI容器中提供View接口的实例化对象
@ActivityScope
@Provides
public MainContract.View providerView(){
return view;
}
}
//生命周期管理
@ActivityScope
//很重要!这个Component应该是AppComponent的子Component,所以要使用这个注解
//不使用@Component注解的Dependents属性是因为希望能统一管理子Component
@Subcomponent(modules = MainModule.class)
public interface MainComponent {
//方法参数中,只能传递被注入对象!要在哪个类中注入,写哪个类,注入到父类没用!
void inject(MainActivity mainActivity);
}
在AppComponent类中添加一行
MainComponent addSub(MainModule mainModule);
代码如下
@Singleton
@Component(modules = {AppModule.class,
OkhttpModule.class,
RetrofitModule.class,
LocalServiceModule.class,
TaobaoIPLocationServiceModule.class})
public interface AppComponent {
MainComponent addSub(MainModule mainModule);
}
1、创建MainContract(不需要改造)
public interface MainContract {
interface View{
void showLocationInfo(TaobaoIPLocationInfo taobaoIPLocationInfo);
void showError(String message);
}
interface presenter{
}
}
2、创建Presenter(注意@Inject)
public class MainPresenter implements MainContract.presenter {
private final MainContract.View view;
private final SharedPreferences sharedPreferences;
private final TaobaoIPLocationService locationService;
private final UserService userService;
//此处关键,用来提供Presenter 的实例化对象
@Inject
public MainPresenter(MainContract.View view,
//注入Default SharedPreferences
@Named("default") SharedPreferences sharedPreferences,
TaobaoIPLocationService locationService,
UserService userService) {
this.view = view;
this.sharedPreferences = sharedPreferences;
this.locationService = locationService;
this.userService = userService;
}
//IP定位测试
public void main(){
locationService.getIPInfo("myip")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
view.showError(e.getMessage());
}
@Override
public void onNext(TaobaoIPLocationInfo taobaoIPLocationInfo) {
view.showLocationInfo(taobaoIPLocationInfo);
}
});
}
}
3、Activity(需要关注addSub方法、Inject方法)
public class MainActivity extends AppCompatActivity implements MainContract.View{
//注入presenter 对象
@Inject
MainPresenter mainPresenter;
private TextView city;
private TextView cityCode;
private TextView ip;
private TextView isp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupActivityComponent();
bindView();
mainPresenter.main();
}
private void bindView() {
city = (TextView) findViewById(R.id.city);
cityCode = (TextView) findViewById(R.id.cityCode);
ip = (TextView) findViewById(R.id.ip);
isp = (TextView) findViewById(R.id.isp);
}
/**
* 初始化属于自己Activity的Component对象
* 本例将MainComponent添加成为AppComponent的子Component
*/
private void setupActivityComponent() {
DaggerApplication.get(this)
.getAppComponent()
//将AppComponent继承然后转换成MainComponent
//MainModule的构造器中传递的是View接口的实例化对象
.addSub(new MainModule(this))
//注入到当前类中
.inject(this);
}
/**
* MVP Presenter 中的回调
* @param taobaoIPLocationInfo IP定位后的返回信息
*/
@Override
public void showLocationInfo(TaobaoIPLocationInfo taobaoIPLocationInfo) {
city.setText(String.format("定位城市:%s", taobaoIPLocationInfo.getData().getCity()));
cityCode.setText(String.format("定位城市代码:%s", taobaoIPLocationInfo.getData().getCity_id()));
ip.setText(String.format("地位地区IP:%s", taobaoIPLocationInfo.getData().getIp()));
isp.setText(String.format("isp服务提供商:%s", taobaoIPLocationInfo.getData().getIsp()));
}
/**
* MVP Presenter 中的回调
*/
@Override
public void showError(String message) {
Toast.makeText(this,message,Toast.LENGTH_LONG).show();
}
}
https://github.com/ChineseLincoln/Dagger2Mvp