Dagger2在AndroidStudio中的应用以及MVP

Dagger2环境搭建(android studio):Module:app-->gradle配置(红色部分)
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        // the latest version of the android-apt plugin
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
    }
}
android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"
    defaultConfig {
        applicationId "com.test.yqman.mydaggertest"
        minSdkVersion 17
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.2.0'

    compile 'javax.inject:javax.inject:1'
    compile 'javax.annotation:javax.annotation-api:1.2'
    compile 'com.google.dagger:dagger:2.0'
    apt 'com.google.dagger:dagger-compiler:2.0'
    provided 'org.glassfish:javax.annotation:10.0-b28'
}

使用:
Module
@Module
public class SecondModule {
    @Provides MyDataService providerMyDataService_sbdf(){ 
        return new MyDataService();
    }
}

@Module
public class MyModule {
    MainActivity mainActivity;
    public MyModule(MainActivity mainActivity){
        this.mainActivity = mainActivity;
    }
    @Provides MainActivity providerMainActivity_dafwe(){
        return mainActivity;
    }
    @Provides MyData providerMyData_xdsf(){
        return new MyData();
    }
    @Provides MyPresenter MyPresenter(MainActivity mainActivity,MyDataService myDataService){
        return new MyPresenter(mainActivity,myDataService);
    }
}
说明:
  • 这里使用了两个注释@Module 、@Provides;
  • 前者声明这是个Module类
  • 后者声明这个方法可以提供什么对象;重要的是返回值,方法名不重要;当然为了让别人看的清楚这个方法返回什么类型对象;方法名可以采用provider+返回类名的格式;
  • 简单概述:方法返回值指明提供的依赖类型(重点);方法参数指明将要消耗的依赖对象
  • 注意:
    • 用Provider标注且返回值类型相同的方法只能出现一次!!!
    • 方法中的参数必须在当前componet或者下一级component中的modules中能够找到一个提供相关对象的方法;
    • 即:
      • MainActivity mainActivity来自于MyModule类中的@Provides MainActivity
      • MyDataService myDataService来自于SecondModule类中的@Provides MyDataService

Component
@Component(modules = {MyModule.class,SecondModule.class})
public interface MyComponent {
    void inject_vsd(MainActivity mainActivity);
    ClientClass inject_asd(ClientClass clientClass);
    MyPresenter providerMyPresent_dfafd();
}

@Component(dependencies= MyComponent.class)
public interface MySecondComponent {
    void inject_faewf(ClientClass clientClass);
}
说明:
  • 这里使用了注释@Component(modules = {MyModule.class,SecondModule.class})声明这是个Component
  • 这个类可以声明两种方法:
    • 指明消耗依赖类:
      • 这类方法的格式是void xsajklf(MyClass myclass)或者 MyClass xsajklf(MyClass myclass);
      • 即返回的对象要么为void要么为消耗依赖类对象自己;
      • 简单概述:方法返回值随意,方法参数指明消耗依赖类,方法名随意只不过建议使用inject命名
    • 返回类实例的对象普通方法:
      • 这类方法格式是ProviderClassA xdasdfas();
      • 这类方法的使用场景主要是当另一个Component使用了dependencies关键字时,即@Component(dependencies = MyComponent.class);该Componet给上一层的Component所能提供的依赖类对象;
      • 简单概述:方法返回值指明提供的依赖类型,方法参数为空
  • 注意:这里定义的Component是一个接口

消耗依赖类:
public class MainActivity extends Activity {
    @Inject MyPresenter myPresenter;
    @Inject MyData myDatabase1;
    @Inject MyData  myDatabase2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout);
        if(myPresenter==null) Log.e("test", "presenter is null");
        MyComponent m = DaggerMyComponent.builder().myModule(new MyModule(this)).secondModule(new SecondModule()).build();
        m.inject_vsd(this);

        //证明注入成功
        Log.e("test", "---------dagger test-------------");
        if(myPresenter==null){  Log.e("test", "Dagger failed");  }
        else{   Log.e("test", "Dagger success");  }

        //默认情况注入对象是否单例
        Log.e("test", "---------single test-------------");
        if(myDatabase1==myDatabase2){   Log.e("test", "single model");   }
        else{  Log.e("test", "not single model");  }

        //普通方法返回消耗类
        Log.e("test", "---------simple method test-------------");
        MyPresenter pre = m.providerMyPresent_dfafd();
        if(pre == null){  Log.e("test", "can not return 0bject"); }
        else if(pre==myPresenter) Log.e("test", "single model");
        else Log.e("test", "not single model");
        new ClientClass(m);
    }
}
说明:
  • @Inject MyPresenter myPresenter;
  • @Inject MyData myDatabase1;
  • @Inject MyData myDatabase2;
  • 上面三个域通过 MyComponent m = DaggerMyComponent.builder().myModule(new MyModule(this)).secondModule(new SecondModule()).build();; m.gblihui(this); 注射进来
  • MyPresenter pre = m.providerMyPresent_dfafd();; 只是一个普通的方法调用,底层实现也是调用Module中的相关方法;
  • 注意使用@Inject进行标注的域,必须是类变量不能是局部变量;而且变量类型必然是能够在Component或者其底层Component声明的Module中能够找到对应拥有该返回值能力的方法;

消耗依赖类调试信息:
presenter is null
---------dagger test-------------
Dagger success
---------single test-------------
not single model
---------simple method test-------------
not single model




当 Dagger2 应用在 MVP 框架中

关于 Dagger2 自己一只想搞明白,但是从去年开始到现在,说真的,看过不少介绍 Dagger2 的文章,但自己一只云里雾里,最近打算把 Dagger2 应用到 MVP 框架中去,所以就重新翻看相关技术文章,重新学习下,下面算是自己这两天学习 Dagger2 后对 Dagger2 的认识,不一定都正确,如果 错误,欢迎指正,只要从代码角度出发,认识 Dagger2。

Note:如果你对 MVP 和 Retrofit 都不熟悉,这篇文章可能不适合你阅读。

学习初衷

前段时间按照 MVP 架构重构过一个妹子客户端 GankDaily ,关于 MVP 具体在 GankDaily 中如何使用,自己也专门写过一篇文章介绍过, MVP 模式在 GankDaily 中的应用 ,如果看过 GankDaily 的代码,其实不仅仅是 GankDaily 这个项目,所有 使用 MVP 架构的项目,都会存在一个问题,那就是每个 Activity 中一定会有一个 Presenter 实例对象,如下所示

 private MainActivityPresenter mPresenter;

它是 Activity 的控制器,所有的数据请求、界面更新都直接或间接的被它控制。然后每个Activity 都需要在 onCreate 中 去实例化它,如下所示

 mPresenter = new MainActivityPresenter(this, user);

上面看上去没什么不妥,但是如果你听过依赖注入(关于依赖注入,这里一哥们写的文章介绍的不错, 依赖注入原理 ),就知道这种使用 new 关键字去实例化一个对象有诸多不好的地方, 这种 new 实例的方式 其实是一种硬编码。为什么?

试想一下,如果以后 MainActivityPresenter 的构造方法中的参数发生变化,那么此时,不仅仅在 MainActivityPresenter 类中改动了 MainActivityPresenter 的构造器相关的代码,你还需要在所有使用到 MainActivityPresenter 的地方修改代码,这不就是硬编码吗?

那你肯定想问? “你说说除了这样的方法,还有什么方法能避免你上面说的这种问题呢?”

答案就是今天的主题 – Dagger2

试想一下,如果把 MainActivityPresenter 实例的获取,事先通过一个辅助类约定好,那么具体使用的时候,通过辅助类 去生成 MainActivityPresenter 的对象,那么是不是会好点,其实这个辅助类,在我理解而来就是 Dagger2 中的 Module 概念。

当然 Dagger2 除了可以解决硬编码的问题,还有其他很多好处,如方便测试等等。

以上算是自己学习的初衷,总结一下,我使用 Dagger2 的愿景或者最终的预期的结果就是 在Activity 中看不到 new Presenter() 这样的代码,直接在 定义 Presenter 实例时,用 @Inject 注解一下,Presenter 的实例就生成。

最终结果

下面自己将会使用 Dagger2 + MVP架构模式 + Retrofit 演示一个简单的 demo 。

这个demo 只有一个 MainActivity界面,最终的效果是在 MainActivity 通过 MainActivityPresenter 获取到一个 字符串显示在 MainActivity 。

这里关于 Dagger2 和 MVP 你应该已经了解他们为什么存在,但是对于 Retrofit 可能有点疑惑,关于 Retrofit:

Retrofit:用来模拟一般的网络数据获取,这个目前大家用的比较多,也确实是一个很方便的库,加上支持 RxJava ,它用于获取数据真是极方便的。 既然要用 Retrofit ,就应该知道整个应用中,最好只存在一个 Retrofit 实例。

环境配置

根目录的 build 文件加入 android-apt 支持

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.1.0'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

在 app 目录的 build 文件中加入 apt plugin 的支持

 apply plugin: 'com.neenbedankt.android-apt'

加入 Dagger2 retrofit OkHttp 依赖

//dagger2
compile 'com.google.dagger:dagger:2.0'
apt 'com.google.dagger:dagger-compiler:2.0'
provided 'org.glassfish:javax.annotation:10.0-b28'

//Square
compile 'com.jakewharton:butterknife:6.1.0'
compile 'com.squareup.retrofit:retrofit:1.9.0'
compile 'com.squareup.okhttp:okhttp:2.3.0'

在 Application 中实践 Dagger2

现在依赖都已经加入,开始动手使用 Dagger2 ,第一步考虑我们的 Application。

先创建 AppApplication,并在 AndroidMainfest.xml 注册好。

public class AppApplication extends Application{

        @Override
        public void onCreate() {
            super.onCreate();
        }
<application
       android:name=".AppApplication"
       android:allowBackup="true"
       android:icon="@mipmap/ic_launcher"
       android:label="@string/app_name"
       android:theme="@style/AppTheme" >

</application>

这里想想,在 Application 中应该做点什么呢?Application中一般可以做一些全局性相关的事。

对于 Retrofit ,前面也说了应该在全局中只存在一个实例,所以 Application 中可以有一个全局性的 Retrofit。

说到这里,感觉说的有点费劲,主要是自己理解的也还不够深入,先这样吧,反正是写给自己以后看的,如果对你,正在读文章的你造成了 困惑,还请见谅。如果读文章有点费劲,建议直接看已经存在于Github上最终的 源代码

基于上面的分析, Application 可能需要一个依赖,依赖于 ApiServiceModule(提供 Retrofit 实例), 这里我们看看 ApiServiceModule 的代码实现

@Module
public class ApiServiceModule {
    private static final String ENDPOINT="";


    @Provides
    @Singleton
    OkHttpClient provideOkHttpClient() {
        OkHttpClient okHttpClient = new OkHttpClient();
        okHttpClient.setConnectTimeout(60 * 1000, TimeUnit.MILLISECONDS);
        okHttpClient.setReadTimeout(60 * 1000, TimeUnit.MILLISECONDS);
        return okHttpClient;
    }

    @Provides
    @Singleton
    RestAdapter provideRestAdapter(Application application, OkHttpClient okHttpClient) {
        RestAdapter.Builder builder = new RestAdapter.Builder();
        builder.setClient(new OkClient(okHttpClient))
                .setEndpoint(ENDPOINT);
        return builder.build();
    }

    @Provides
    @Singleton
    ApiService provideApiService(RestAdapter restAdapter) {
        return restAdapter.create(ApiService.class);
    }
}

任何 Module 类的开头都以 @Module 进行注解。

然后,最终 ApiServiceModule 需要提供给 AppApplication ,这里不能直接将 ApiServiceModule 给 AppApplication , 现在需要一个组件对他进行整合 这个组件我们对他命名为 AppComponent下面是它的实现。

@Singleton
// 如果这里有多个 可以用逗号隔开,继续追加
@Component(modules = {ApiServiceModule.class})

public interface AppComponent {

    ApiService getService();
    
}

这里你会看到他是一个接口,然后在类申明的开始的位置,用 @Component 指定了他具体的依赖的类。

这个接口只有一个方法,返回了之前设想的一个全局实例 ApiService。

既然是接口,那么一定有类去实现他,设想一下,如果有一个类实现了上面的接口, 我们只要在 AppApplication 中得到 这个实现的对象,那这个对象不就是 AppComponent,然后我们不就 通过 AppComponent 获取到实例对象了吗?恩~ 就应该是这样。

现在定义好 AppComponent 接口,然后经过编译,当你在编辑器 打出 Dagger 编辑器就会自动提示 DaggerAppComponent 这个类。

恩?这是怎么回事?

这其实是 Dagger2 帮你自动生成的。

现在在 AppApplication 中直接使用 DaggerAppComponent,用它来得到 AppComponent 对象,然后有了 AppComponent 那么 ApiService 不就随之而来了吗?

AppApplication 中代码现在如下所示

public class AppApplication  extends Application{

    public static AppApplication get(Context context){
       return (AppApplication)context.getApplicationContext();
    }
    
    private AppComponent appComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        appComponent=DaggerAppComponent.builder()
                .apiServiceModule(new ApiServiceModule())
                .build();
   }

    public AppComponent getAppComponent() {
        return appComponent;
    }
}

那么现在只要在任何地方想要获取 ApiService 的实例,只要得到了 AppApplication 的实例(AppApplication.get(), 就可以通过 AppAppLication 的 getAppComponent() 方法得到 AppComponent ,然后通过 AppComponent 就可以间接得到 ApiService 的实例了。

上面大概说了下在 Application 中应用 Dagger2 去实例化 AppComponent,其实在 Activity 使用也是同样的道理。现在暂时没时间 写在 Activity 中怎么使用 Dagger2 了,具体可以从代码中查看。

代码都已经在 Github ,欢迎 查阅



MVP+Dagger2+Retrofit实现更清晰的架构

这个架构已经有不少文章介绍了,今天打算自己实践下。

MVP概念不多说了 相关介绍已经很多了

Dagger2:依赖注入框架,用来解决依赖 除了基本依赖 mvp的V–>P–>M的之间依赖也轻松解决 方便不少

Retrofit:用来解决M的RestApi数据获取, 天然支持Rxjava 不过这里我没用到Rxjava 其自带的Callback已经足够用了

估计这个架构的难点在于Dagger2 理解它的工作方式需要方式需要点时间,我收集了一些资料,可以查看我博客的相关文章。

ok,开始写demo,简单看下项目结构

Dagger2在AndroidStudio中的应用以及MVP_第1张图片

  • data:数据来源 也就是MVP的M
  • model:实体bean,本来也应该放在data中,为了方便查找就抽出了,毕竟很多地方用到
  • ui:视图,VP都在里面,View层是最难组织的,有人喜欢以功能划分 eg:main,login 有人喜欢以组件划分

来简单看一下dagger2提供的依赖结构

Dagger2在AndroidStudio中的应用以及MVP_第2张图片

AppComponent通常提供全局的对象以便于其他的组件依赖使用,比如context,rest api接口等,这些都是全局的单例对象

MainActivityComponent特意针对MainActivity,所以它只提供其对应的MainActivityPresenter,因为其依赖AppComponent,所以它可以使用AppComponent提供的对象

AppComponent 
可以看到其向外公布了Application,ApiService,User三个对象,供其他component都可以使用
@Singleton
@Component(modules = {AppModule.class, ApiServiceModule.class, AppServiceModule.class})
public interface AppComponent {


    Application getApplication();

    ApiService getService();

    User getUser();
}

MainActivityComponent 
它依赖了AppComponent,本身只提供了MainActivityPresenter,并且只针对MainActivity
@ActivityScope
@Component(modules = MainActivityModule.class,dependencies = AppComponent.class)
public interface MainActivityComponent {
    MainActivity inject(MainActivity mainActivity);

    MainActivityPresenter presenter();
}
最后看一下Activity怎么使用
public class MainActivity extends BaseActivity {

    @InjectView(R.id.tv)
    TextView textView;

    @Inject
    MainActivityPresenter presenter;//注入其所需要的presenter


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.inject(this);
        presenter.showUserName();
    }

    @Override
    protected void setupActivityComponent(AppComponent appComponent) {
        DaggerMainActivityComponent.builder()
                .appComponent(appComponent)
                .mainActivityModule(new MainActivityModule(this))
                .build()
                .inject(this);
    }

//只做界面逻辑
    public void setTextView(String username){
        textView.setText(username);
    }
}

代码就不全贴了 后面会放到github上面去 
显而易见,整个项目和结构会非常舒服,各个模块各司其事,有了dagger在扩展和测试会非常方便,分别提供DebugComponent和ReleaseComponent随意切换

附上github地址:点我哦













你可能感兴趣的:(Dagger2在AndroidStudio中的应用以及MVP)