LoginSignup
29
27

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-11-15

基本的には、以前書いたものに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のレートが入っています。

今回の例
{
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の設定から

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

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行追加

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しないように注意

ExchangeRateApi.java

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

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

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

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;
    }
}
CountryCode.java

public class CountryCode {
    private float JPY;

    public float getJPY() {
        return JPY;
    }
}

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

AppModule.java
@Module
public class AppModule {

    Application mApplication;

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

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

ApiModuleを作成

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を作成

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

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

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" を一行追記

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を作成

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

    interface Presenter {
        void getExchangeRate();
    }
}

ExchangeRateContract.PresenterをimplementsしたExchangeRatePresenterを作成

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を作成

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等を推奨していたので、その通りに作成

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

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

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

MainActivityのxmlにTextViewを1つ追加

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を下記のように修正して、実行してみる。

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

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

29
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
27