-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Retrofit2 + Moshi + RxJava(RxKotlin, RxAndroid)を利用したAPI通信処理 #8
- Loading branch information
over
authored and
over
committed
Jul 17, 2021
1 parent
0242c06
commit 8f13bc7
Showing
22 changed files
with
640 additions
and
55 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 0 additions & 11 deletions
11
app/src/main/java/tokyo/oversoftware/bookexplorer/SearchActivity.kt
This file was deleted.
Oops, something went wrong.
42 changes: 42 additions & 0 deletions
42
app/src/main/java/tokyo/oversoftware/bookexplorer/di/DependencyInjection.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package tokyo.oversoftware.bookexplorer.di | ||
|
||
import androidx.lifecycle.ViewModelProvider | ||
import tokyo.oversoftware.bookexplorer.model.datastore.remote.ApiClient | ||
import tokyo.oversoftware.bookexplorer.model.datastore.remote.BooksRemoteDataSource | ||
import tokyo.oversoftware.bookexplorer.model.datastore.remote.GoogleBooksDataSource | ||
import tokyo.oversoftware.bookexplorer.model.repository.BooksRepository | ||
import tokyo.oversoftware.bookexplorer.model.repository.GoogleBooksRepository | ||
import tokyo.oversoftware.bookexplorer.viewmodel.SearchViewModelFactory | ||
|
||
/** | ||
* アプリで使う実装クラスの依存関係をすべてここで解決する | ||
*/ | ||
object DependencyInjection { | ||
|
||
private val remoteDataSource: BooksRemoteDataSource = GoogleBooksDataSource( | ||
apiService = ApiClient.build() | ||
) | ||
|
||
private val remoteRepository: BooksRepository = GoogleBooksRepository( | ||
remoteDataSource = remoteDataSource | ||
) | ||
|
||
private val searchViewModelFactory: ViewModelProvider.Factory = SearchViewModelFactory( | ||
repository = remoteRepository | ||
) | ||
|
||
/** | ||
* 書籍取得用Repositoryの取得 | ||
*/ | ||
fun remoteRepository(): BooksRepository { | ||
return remoteRepository | ||
} | ||
|
||
/** | ||
* 検索用ViewModelの取得 | ||
*/ | ||
fun searchViewModelFactory(): ViewModelProvider.Factory { | ||
return searchViewModelFactory | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
app/src/main/java/tokyo/oversoftware/bookexplorer/entity/DisplayableBook.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package tokyo.oversoftware.bookexplorer.entity | ||
|
||
/** | ||
* 画面表示用に余計な情報を削ぎ落とした書籍データ | ||
*/ | ||
class DisplayableBook(private val book: Book) { | ||
|
||
// 書籍のタイトル | ||
val title: String = book.volumeInfo?.title ?: "" | ||
|
||
// 書籍のサブタイトル | ||
val subTitle: String = book.volumeInfo?.subtitle ?: "" | ||
|
||
// 著者 | ||
val authors: List<String> = book.volumeInfo?.authors ?: emptyList() | ||
|
||
// サムネイル | ||
val thumbnailUrl: String? = book.volumeInfo?.imageLinks?.thumbnail | ||
|
||
// リンク | ||
fun link(): String { | ||
// ISBNコードがあるか | ||
val isbn10 = book.volumeInfo?.industryIdentifiers?.find { it.type == "ISBN_10" }?.identifier | ||
if (isbn10 != null) { | ||
return "https://www.amazon.co.jp/dp/isbn10" | ||
} | ||
|
||
return if (book.volumeInfo?.industryIdentifiers?.size == 0) { | ||
// 出版社がない場合はタイトルで検索する | ||
"https://www.google.com/search?q=" + book.volumeInfo.title | ||
} else { | ||
// ISBNコードがない場合は検索する | ||
"https://www.google.com/search?q=" + book.volumeInfo?.industryIdentifiers?.get(0)?.identifier | ||
} | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
app/src/main/java/tokyo/oversoftware/bookexplorer/model/datastore/remote/ApiClient.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package tokyo.oversoftware.bookexplorer.model.datastore.remote | ||
|
||
import com.squareup.moshi.Moshi | ||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory | ||
import io.reactivex.rxjava3.core.Observable | ||
import io.reactivex.rxjava3.core.Single | ||
import okhttp3.OkHttpClient | ||
import okhttp3.logging.HttpLoggingInterceptor | ||
import retrofit2.Retrofit | ||
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory | ||
import retrofit2.converter.moshi.MoshiConverterFactory | ||
import retrofit2.http.GET | ||
import retrofit2.http.Query | ||
import tokyo.oversoftware.bookexplorer.BuildConfig | ||
import tokyo.oversoftware.bookexplorer.entity.Books | ||
|
||
/** | ||
* Retrofitのインタフェースを定義する | ||
*/ | ||
object ApiClient { | ||
|
||
// APIの既定URL | ||
private const val API_BASE_URL = "https://www.googleapis.com/books/v1/" | ||
|
||
/** | ||
* Retrofitのインタフェースを作成する | ||
*/ | ||
fun build(): ApiService { | ||
val moshi = Moshi.Builder() | ||
.add(KotlinJsonAdapterFactory()) | ||
.build() | ||
|
||
// Releaseビルド時は実行パフォーマンスの妨げになるのでログを出力しない | ||
val httpClient: OkHttpClient = if (BuildConfig.DEBUG) { | ||
OkHttpClient.Builder().addInterceptor( | ||
interceptor() | ||
).build() | ||
} else { | ||
OkHttpClient.Builder().build() | ||
} | ||
|
||
val builder = Retrofit.Builder() | ||
.baseUrl(API_BASE_URL) | ||
.addConverterFactory(MoshiConverterFactory.create(moshi)) | ||
.addCallAdapterFactory(RxJava3CallAdapterFactory.create()) | ||
|
||
val retrofit: Retrofit = builder.client(httpClient).build() | ||
|
||
return retrofit.create(ApiService::class.java) | ||
} | ||
|
||
private fun interceptor(): HttpLoggingInterceptor { | ||
val httpLoggingInterceptor = HttpLoggingInterceptor() | ||
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY | ||
return httpLoggingInterceptor | ||
} | ||
|
||
// API定義 | ||
interface ApiService { | ||
@GET("volumes") | ||
fun volumes( | ||
@Query("q") name: String, @Query("startIndex") startIndex: Int = 0, | ||
@Query("maxResults") limit: Int = 20 | ||
): Single<Books> | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
...main/java/tokyo/oversoftware/bookexplorer/model/datastore/remote/BooksRemoteDataSource.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package tokyo.oversoftware.bookexplorer.model.datastore.remote | ||
|
||
import io.reactivex.rxjava3.core.Maybe | ||
import io.reactivex.rxjava3.core.Observable | ||
import io.reactivex.rxjava3.core.Single | ||
import tokyo.oversoftware.bookexplorer.entity.Books | ||
|
||
/** | ||
* 書籍データをリモートから取得するデータソース | ||
*/ | ||
interface BooksRemoteDataSource { | ||
/** | ||
* 書籍データを取得する | ||
* @param searchKeyword 検索キーワード | ||
*/ | ||
fun fetchBooks(searchKeyword: String): Single<Books> | ||
} | ||
|
||
/** | ||
* Google Books Apiを利用した書籍データソース | ||
*/ | ||
class GoogleBooksDataSource(// Retrofitのインタフェース | ||
private val apiService: ApiClient.ApiService | ||
) : BooksRemoteDataSource { | ||
|
||
override fun fetchBooks(searchKeyword: String): Single<Books> { | ||
return apiService.volumes(name = searchKeyword) | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
app/src/main/java/tokyo/oversoftware/bookexplorer/model/repository/BooksRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package tokyo.oversoftware.bookexplorer.model.repository | ||
|
||
import android.util.Log | ||
import io.reactivex.rxjava3.core.Single | ||
import tokyo.oversoftware.bookexplorer.entity.Books | ||
import tokyo.oversoftware.bookexplorer.model.datastore.remote.BooksRemoteDataSource | ||
|
||
/** | ||
* 書籍情報を取得するRepository | ||
* 依存元からデータをどこから取得しているかをカプセル化するために利用する | ||
*/ | ||
interface BooksRepository { | ||
/** | ||
* 書籍情報を取得する | ||
* @param searchKeyword: 検索キーワード | ||
*/ | ||
fun findBooks(searchKeyword: String): Single<Books> | ||
} | ||
|
||
/** | ||
* Googleから書籍情報を取得するデータソース | ||
* MEMO: 現在は`RemoteDataSource`しかコンストラクタで注入していないが、 | ||
* 規模の大きなアプリであればCacheDataSourceなども注入する | ||
*/ | ||
class GoogleBooksRepository(private val remoteDataSource: BooksRemoteDataSource) : BooksRepository { | ||
|
||
// キャッシュデータ | ||
// 検索ワードと書籍データのペア。画面回転時のキャッシュに利用する | ||
var cache: Pair<String, Books>? = null | ||
|
||
/** | ||
* 書籍情報を取得する | ||
* @param searchKeyword: 検索キーワード | ||
*/ | ||
override fun findBooks(searchKeyword: String): Single<Books> { | ||
// キャッシュがあればそれを返す | ||
if (searchKeyword == cache?.first) { | ||
return Single.just(cache?.second) | ||
} | ||
|
||
return remoteDataSource.fetchBooks(searchKeyword = searchKeyword) | ||
.doOnSuccess { | ||
// 初回か別のキーワードであればメモリに入れておく | ||
val differentSearchWord: Boolean = searchKeyword == cache?.first | ||
if (cache == null || differentSearchWord) { | ||
cache = Pair(searchKeyword, it) | ||
} | ||
} | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
app/src/main/java/tokyo/oversoftware/bookexplorer/view/activities/SearchActivity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package tokyo.oversoftware.bookexplorer.view.activities | ||
|
||
import androidx.appcompat.app.AppCompatActivity | ||
import android.os.Bundle | ||
import androidx.lifecycle.Observer | ||
import androidx.lifecycle.ViewModelProvider | ||
import tokyo.oversoftware.bookexplorer.databinding.ActivitySearchBinding | ||
import tokyo.oversoftware.bookexplorer.di.DependencyInjection | ||
import tokyo.oversoftware.bookexplorer.entity.DisplayableBook | ||
import tokyo.oversoftware.bookexplorer.viewmodel.SearchViewModel | ||
|
||
class SearchActivity : AppCompatActivity() { | ||
|
||
private lateinit var binding: ActivitySearchBinding | ||
private lateinit var viewModel: SearchViewModel | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
binding = ActivitySearchBinding.inflate(layoutInflater) | ||
val view = binding.root | ||
setContentView(view) | ||
setupViewModel() | ||
|
||
viewModel.onSearch("ドラゴンボール") | ||
} | ||
|
||
private fun setupViewModel() { | ||
viewModel = ViewModelProvider( | ||
this, | ||
DependencyInjection.searchViewModelFactory() | ||
).get(SearchViewModel::class.java) | ||
|
||
viewModel.items.observe(this, renderResponse) | ||
} | ||
|
||
private val renderResponse = Observer<List<DisplayableBook>> { | ||
if (it.isNotEmpty()) { | ||
binding.apiResult.text = it[0].title | ||
} | ||
} | ||
} |
Oops, something went wrong.