MVP + Dagger2 + Retrofit2 + RxAndroidの構成で通信を試してみる


基本的には、以前書いたものにMVPの構成を当てはめただけです。
http://qiita.com/MuuKojima/items/8088b43876dc8d3d1745

今回の内容としてはUSDからJPYに変換した為替レートを受け取りTextViewに表示するだけです。
使うAPI: http://fixer.io/ なかなか便利そうですね。

実際のリクエストです。http://api.fixer.io/latest?base=USD&symbols=JPY
下記のように1件JPYのレートが入っています。

```Json:今回の例
{
base: "USD",
date: "2016-11-14",
rates: {
		JPY: 107.89
	}
}
```

パッケージの構成です。
MVPのModelに当たる部分は`data`として切り分け、
ViewとPresenterは`exchange`というパッケージの中に一緒に入れてみました。

```
├data├ApiModule
     ├AppComponent
     ├AppModule
     ├CountryCode
     ├ExchangeApi
     ├ExchangeRateResponse
├exchange├ExchangeRateContract
         ├ExchangeRateComponent
         ├ExchangeRateModule
         ├ExchangeRatePresenter
         ├MainActivity
├AppApplicatiinn
├PerActivity
```

それでは、まずrootのbuild.gradleの設定から

```gradle:build.gradle
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
   		classpath 'com.android.tools.build:gradle:2.2.0'

       // 追加
       classpath 'com.uphyca.gradle:gradle-android-apt-plugin:0.9.4'
    }
}
```

appのbuild.gradle

```gradle:build.gradle
apply plugin: 'com.android.application'
// 追加 ↑の下に入れる事
apply plugin: 'android-apt'

dependencies {	

    //////// 追加 ////////

    // RxAndroid
    compile 'io.reactivex:rxjava:1.1.0'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
    compile 'io.reactivex:rxandroid:1.1.0'
    // Dagger
    compile 'com.google.dagger:dagger:2.2'
    apt 'com.google.dagger:dagger-compiler:2.2'
    // Retrofit
    compile 'com.squareup.retrofit2:retrofit:2.0.2'
    // OkHttp
    compile 'com.squareup.okhttp3:okhttp:3.2.0'
    compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'
    // Gson
    compile 'com.google.code.gson:gson:2.6.2'
    compile 'com.squareup.retrofit2:converter-gson:2.0.2'
}

```

AndroidManifest.xmlにインターネットパーミッションを1行追加

```xml:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="kojimation.com.retrofitsample">
    
    <!-- 追加 -->
    <uses-permission android:name="android.permission.INTERNET" />
    
</manifest>
```

今回使う、為替レートを取得するAPIの作成
rxじゃないObservableをimportしないように注意

```java:ExchangeRateApi.java

public interface ExchangeRateApi {
    String URL = "/latest";

    @GET(URL)
    Observable<ExchangeRateResponse> getExchangeRate(@Query("base") String base,
                                                     @Query("symbols") String symbols);
}
```

APIから返って来るレスポンスのオブジェクトを作成

```java:ExchangeRateResponse.java

public class ExchangeRateResponse {
    private String base;
    private String date;
    private CountryCode rates;

    public String getBase() {
        return base;
    }

    public String getDate() {
        return date;
    }

    public CountryCode getRates() {
        return rates;
    }
}
```

```java:CountryCode.java

public class CountryCode {
    private float JPY;

    public float getJPY() {
        return JPY;
    }
}
```

AppModuleを作成(今回のサンプルでは使わないですが、作っておいた方が良いです)

```java:AppModule.java
@Module
public class AppModule {

    Application mApplication;

    public AppModule(Application mApplication) {
        this.mApplication = mApplication;
    }

    @Provides
    @Singleton
    Application provideApplication() {
        return mApplication;
    }
}
```

ApiModuleを作成

```java:ApiModule.java
@Module
public class ApiModule {

    @Provides
    @Singleton
    Gson provideGson() {
        GsonBuilder gsonBuilder = new GsonBuilder();
        return gsonBuilder.create();
    }

    @Provides
    @Singleton
    OkHttpClient provideOkhttpClient() {
        OkHttpClient.Builder client = new OkHttpClient.Builder();
        client.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
        return client.build();
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
        return new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                // ベースのURLの設定
                .baseUrl("http://api.fixer.io")
                .client(okHttpClient)
                .build();
    }
}
```

AppComponentを作成

```java:AppComponent.java
@Singleton
@Component(modules = {AppModule.class, ApiModule.class})
public interface AppComponent {
    Retrofit retrofit();
}
```

一旦、実行もしくはrebuildする(DaggerAppComponentが生成される)
Applicationクラスを継承したAppApplicationを作成

```java:Application.java
public class AppApplication extends Application {

    private AppComponent mAppComponent;

    @Override
    public void onCreate() {
        super.onCreate();

        mAppComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this))
                .apiModule(new ApiModule())
                .build();
    }

    public AppComponent getAppComponent() {
        return mAppComponent;
    }
}
```

Applicationクラスを使うので、AndroidManifest.xmlに  `android:name=".AppApplication"` を一行追記

```xml:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="kojimation.com.daggerretrofitrxandorid">

    <!-- 追加 -->
    <uses-permission android:name="android.permission.INTERNET" />
	
	<!-- このタグの中に追加 -->
    <application
        android:name=".AppApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
```

ViewのinterfaceとPresenterのinterfaceまとめるExchangeRateContractを作成

```Java:ExchangeRateContract.java
public interface ExchangeRateContract {
    interface View {
        void bindExchangeRate(ExchangeRateResponse exchangeRateResponse);
    }

    interface Presenter {
        void getExchangeRate();
    }
}

```

ExchangeRateContract.PresenterをimplementsしたExchangeRatePresenterを作成

```Java:ExchangeRatePresenter.java
public class ExchangeRatePresenter implements ExchangeRateContract.Presenter {
    private Retrofit mRetrofit;
    private ExchangeRateContract.View mView;

    @Inject
    public ExchangeRatePresenter(Retrofit retrofit, ExchangeRateContract.View view) {
        this.mRetrofit = retrofit;
        this.mView = view;
    }

    public void getExchangeRate() {
        mRetrofit.create(ExchangeRateApi.class).getExchangeRate("USD", "JPY")
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<ExchangeRateResponse>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.d("通信 -> ", "失敗" + e.toString());
                    }

                    @Override
                    public void onNext(ExchangeRateResponse exchangeRateResponse) {
                        mView.bindExchangeRate(exchangeRateResponse);
                    }
                });
    }
}
```

ViewをModuleに登録するためのExchangeRateModuleを作成

```Java:ExchangeRateModule.java
@Module
public class ExchangeRateModule {
    private final ExchangeRateContract.View mView;

    public ExchangeRateModule(ExchangeRateContract.View mView) {
        this.mView = mView;
    }

    @Provides
    ExchangeRateContract.View provideExchangeRateView() {
        return mView;
    }
}
```

Injectする際に使用するカスタムアノテーションを作成
命名は特に決まっていないが、ググるとPerActivity等を推奨していたので、その通りに作成

```Java:PerActivity.java
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {}
```

Activity等でInjectする用のExchangeRateComponentを作成

```Java:ExchangeRateComponent.java
@PerActivity
@Component(dependencies = AppComponent.class, modules = ExchangeRateModule.class)
public interface ExchangeRateComponent {
    void inject(MainActivity activity);
}

```
MainActivityのxmlにTextViewを1つ追加

```xml:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="kojimation.com.retrofitsample.MainActivity">
	
    <!-- 追加 -->
    <TextView
        android:id="@+id/txt_jpy"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>
```

ここでもう一度、実行もしくはrebuildする
(DaggerExchangeRateComponentが生成される)
MainActivityを下記のように修正して、実行してみる。

```Java:MainActivity.java
public class MainActivity extends AppCompatActivity implements ExchangeRateContract.View {

    @Inject
    ExchangeRatePresenter mPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaggerExchangeRateComponent.builder()
                .appComponent(((AppApplication) getApplicationContext()).getAppComponent())
                .exchangeRateModule(new ExchangeRateModule(this))
                .build().inject(this);

        mPresenter.getExchangeRate();
    }

    @Override
    public void bindExchangeRate(ExchangeRateResponse exchangeRateResponse) {
        TextView textView = (TextView) findViewById(R.id.txt_jpy);
        textView.setText("JPY: " + String.valueOf(exchangeRateResponse.getRates().getJPY()));
    }
}
```

実行結果

![スクリーンショット 2016-11-15 午後1.01.17.png](https://qiita-image-store.s3.amazonaws.com/0/33553/edec915e-6423-6106-f667-a2fa4dea7d2a.png "スクリーンショット 2016-11-15 午後1.01.17.png")


Githubにサンプルを置いておきます
https://github.com/MuuKojima/MVPDaggerRetrofitRxAndoridSample
CleanArchitectureを適用したものを追記しておきます
http://qiita.com/MuuKojima/items/5d20a1b6b558cb432010