0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【kotlin】超簡単なMVPを実装してみた、③RemoteRepository設定(APIのやり取り)

Last updated at Posted at 2022-01-16

はじめに

今回はMVP(アーキテクチャ)に沿って、RemoteRepository設定(API通信のやり取り)を実装していきたいと思います。

本記事は「【kotlin】超簡単なMVPを実装してみた、②LocalRepository設定(ローカルデータに保存、読み込み)」の内容を引き継いで実装しています。

これまでの内容は
【kotlin】超簡単なMVPを実装してみた、①Presenterの実装
【kotlin】超簡単なMVPを実装してみた、②LocalRepository設定(ローカルデータに保存、読み込み)
をご参照ください

実装内容

APIは「楽天ブックス雑誌検索API」を使用する。
EditTextに文字を入力してボタンを押したら、APIから本を検索し、タイトル、画像、値段を表示する。
Screenshot_1642310088.png

実装

[準備]
「楽天ブックス雑誌検索API」
から、アプリIDを獲得する。

[レイアウト]
①editText
②button
③textView
④bookTitleTextView
⑤bookImageView
⑥bookPriceTextView
Screenshot_1642310088.png

[コード]
・RemoteRepository設定
①androidmanifestにを追加する。

<uses-permission android:name="android.permission.INTERNET"/>

②Retrofit設定
※詳しい説明は割愛させていただきます。

BooksEntity
data class BooksEntity(
    var Items: ArrayList<ItemsData>?
)

data class ItemsData(
    var Item:ItemData?
)

data class ItemData(
    //商品タイトル
    var title: String ="",
    //値段
    var itemPrice: String ="",
    //画像URL
    var largeImageUrl: String ="",
)
app/build.gradle
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
ApiService
interface ApiService {
    @GET("20170404?applicationId=(獲得した楽天BooksアプリID)")
    fun getBooks(
        @Query("format") format:String,
        @Query("keyword") keyword:String,
        @Query("booksGenreId") booksGenreId:String,
        @Query("sort") sort:String,
    ): Call<BooksEntity>
}
ApiClientManager
class ApiClientManager {

    //Retrofitクラスのインスタンス化
    val retrofit = Retrofit.Builder()
        //ベースURLを入力
        .baseUrl("https://app.rakuten.co.jp/services/api/BooksTotal/Search/")
        //レスポンスを変換する設定を追加
        .addConverterFactory(GsonConverterFactory.create())
        //Httpクライアント結合
        .client(getOkHttpClient())
        //onResponseバックグラウンド処理
        .callbackExecutor(Executors.newSingleThreadExecutor())
        .build()

    // サービスクラスの実装オブジェクト取得(インターフェイスを利用する)
    val apiClient:ApiService = retrofit.create(ApiService::class.java)

    private fun getOkHttpClient(): OkHttpClient {
        val httpClient = OkHttpClient.Builder()
            .readTimeout(30,TimeUnit.SECONDS)
            .writeTimeout(30,TimeUnit.SECONDS)
            .build()
        return httpClient
    }

}

③RemoteRepositoryを作成
※RemoteRepositoryの役割はPresenterに橋渡し、Presenterで使用するメソッドを定義する。
④RemoteRepositoryで使用するメソッドを追加する。

RemoteRepository
interface RemoteRepository {
    //④本を検索しデータを持ってくる。
    fun getBooks(word: String, callback: Callback<BooksEntity>)
}

⑤ApiClientManager戻り値にRemoteRepositoryを追加する。
⑥RemoteRepositoryで設定したメソッドが出るので設定。

ApiClientManager
//⑤戻り値にRemoteRepositoryを追加する。
class ApiClientManager: RemoteRepository {

    val retrofit = Retrofit.Builder()
        .baseUrl("https://app.rakuten.co.jp/services/api/BooksTotal/Search/")
        .addConverterFactory(GsonConverterFactory.create())
        .client(getOkHttpClient())
        .callbackExecutor(Executors.newSingleThreadExecutor())
        .build()
    val apiClient:ApiService = retrofit.create(ApiService::class.java)

    private fun getOkHttpClient(): OkHttpClient {
        val httpClient = OkHttpClient.Builder()
            .readTimeout(30,TimeUnit.SECONDS)
            .writeTimeout(30,TimeUnit.SECONDS)
            .build()
        return httpClient
    }

    //⑥本を検索するメソッド
    override fun getBooks(word: String, callback: Callback<BooksEntity>) {
        apiClient.getBooks(format = "json", keyword = word, booksGenreId = "000", sort = "standard").enqueue(callback)
    }
}

・Presenter設定
①Presenterの引数にRemoteRepositoryを追加。

MainPresenter
//①Presenterの引数にRemoteRepositoryを追加。
class MainPresenter(private val view: MainContract.View, private val localRepository:  LocalRepository, private val remoteRepository: RemoteRepository): MainContract.Presenter {

    init {
        view.presenter = this
    }

    override fun start() {
        val text =localRepository.loadText()
        view.showTextView(text)
    }

    override fun onClickButton(text: String) {
        localRepository.saveText(text)
        view.showTextView(text)
    }

}

・Contract設定
①UIが増えたのでview側で使用するメソッドを作成。

MainContract

interface MainContract {

    interface View: BaseView<Presenter> {
        fun showTextView (text: String)

        //①bookTitleTextViewに本のタイトルを表示させるメソッド
        fun showBookTitleTextView (text: String)
        //①bookPriceTextViewに値段を表示させるメソッド
        fun showBookPriceTextView (text: String)
        //①bookImageViewに画像を表示させるメソッド
        fun showBookImageView (bitmap: Bitmap)
    }

    interface Presenter: BasePresenter {
        fun onClickButton(text: String)
    }
}

・View設定
①追加したUIを獲得。
②Contractで追加したメソッドが出てくるので、設定。
③Presenterに引数にApiClientManager()を追加。

MainFragment

class MainFragment: Fragment(),MainContract.View {
    override lateinit var presenter: MainContract.Presenter
    lateinit var editText :EditText
    lateinit var button :Button
    lateinit var textView :TextView

    //①追加したUIを獲得。
    lateinit var bookTitleTextView :TextView
    lateinit var bookImageView :ImageView
    lateinit var bookPriceTextView :TextView

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_main, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        editText = view.findViewById<EditText>(R.id.editTextText)
        button = view.findViewById<Button>(R.id.button)
        textView = view.findViewById<TextView>(R.id.textView)

        //①追加したUIを獲得。
        bookTitleTextView = view.findViewById<TextView>(R.id.bookTitleTextView)
        bookImageView = view.findViewById<ImageView>(R.id.bookImageView)
        bookPriceTextView = view.findViewById<TextView>(R.id.bookPriceTextView)

        //③Presenterに引数にApiClientManager()を追加。
        initPresenter()
        presenter.start()
        button.setOnClickListener{
            presenter.onClickButton(editText.text.toString())
        }
    }
    override fun showTextView(text: String) {
        textView.text = text
    }

    //②bookTitleTextViewに本のタイトルを表示する。
    override fun showBookTitleTextView(text: String) {
        bookTitleTextView.text = text
    }
    //②bookPriceTextViewに本の値段を表示する。
    override fun showBookPriceTextView(text: String) {
        bookPriceTextView.text = text
    }
    //②bookImageViewに本の画像を表示させる。
    override fun showBookImageView(bitmap: Bitmap) {
        activity?.runOnUiThread {
            bookImageView.setImageBitmap(bitmap)
        }
    }
    fun initPresenter() {
        if (::presenter.isInitialized) {
            return
        }
        context?.let {
            presenter = MainPresenter(this, PreferenceManager(it),ApiClientManager())
        }
    }
}

・Presenter設定
①LocalRepositoryのメソッドを呼び出す。
②通信が成功した場合、view側に反映させる。

MainPresenter
class MainPresenter(private val view: MainContract.View, private val localRepository:  LocalRepository, private val remoteRepository: RemoteRepository): MainContract.Presenter {
    init {
        view.presenter = this
    }
    override fun start() {
        val text =localRepository.loadText()
        view.showTextView(text)
    }
    override fun onClickButton(text: String) {
        localRepository.saveText(text)
        view.showTextView(text)

        //①LocalRepositoryのgetBooksメソッドを呼び出す。
        remoteRepository.getBooks(text, object: Callback<BooksEntity>{
            override fun onResponse(call: Call<BooksEntity>, response: Response<BooksEntity>) {
                if(response.isSuccessful){
                    //②通信が成功した場合showBookTitleTextViewを使用し、View側に本のタイトルを反映させる。
                    response.body()?.Items?.get(0)?.Item?.title?.let {
                        view.showBookTitleTextView(it)
                    }
                    //②通信が成功した場合showBookImageViewを使用し、View側に画像を反映させる。
                    response.body()?.Items?.get(0)?.Item?.largeImageUrl?.let {
                        val url = URL(it)
                        val streem = url.openStream()
                        val bitmap = BitmapFactory.decodeStream(streem)
                        view.showBookImageView(bitmap)
                    }
                    //②通信が成功した場合showBookPriceTextViewを使用し、View側に値段を反映させる。
                    response.body()?.Items?.get(0)?.Item?.itemPrice?.let {
                        view.showBookPriceTextView(it+"円")
                    }
                } else {

                }
            }
            override fun onFailure(call: Call<BooksEntity>, t: Throwable) {
            }

        })
    }
}

以上、RemoteRepositoryの実装でした。

関連記事

【kotlin】超簡単なMVPを実装してみた、①Presenterの実装
【kotlin】超簡単なMVPを実装してみた、②LocalRepository設定(ローカルデータに保存、読み込み)

参考資料

Android アプリ設計パターン入門
テストが書けない人のAndroid MVP
todo-mvp

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?