Kotlin/Androidアプリ作りながらメモ
Androidアプリ作りながら何これと思ったことをめも
教材
下準備
ライブラリの導入系
id 'org.google.dagger.hilt.android' version '2.43.2' apply false
- dagger... Google謹製のDIライブラリ
// Retrofit2
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// Coil
implementation("io.coil-kt:coil-compose:1.4.0")
// ViewModel
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1"
// Dagger Hilt
implementation "com.google.dagger:hilt-android:2.43.2"
kapt "com.google.dagger:hilt-android-compiler:2.43.2"
- Retrofit2...HTTP通信ライブラリ
- Coil...画像埋め込みライブラリ
- ViewModel...ViewModelという名前でViewModelの仕組みが提供されてる
- Dagger Hilt...DI
build.gradle(Project)、build.gradle(Module) の違い
- Project全体(=全てのModuleに適用する設定はbuild.gradle(Project)に記載する)
フォルダ階層作るにはPackage
@HiltAndroidApp
class KotlinMVVMTutorial: Application()
- Application()継承
- 全Activityからアクセスできるクラスになる
- データの共有とかできる
- @HiltAndroidApp
- Hilt(DIライブラリ)を使うというおまじない(https://qiita.com/uhooi/items/2a1ccb3fab9afd539203)
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:name=".app.KotlinMVVMTutorial"
- インターネット許可
- アプリ名の設定?
companion object
- クラス内に定義するSingletonのこと
val
vs const val
- 呼び出し側からの書き方とかは同じだけど、
const val
の方が不要なメソッドがなくなるからよさげ
JSON To Kotlin Calss便利すぎ
- Preferences > Pluguins >
JSON To Kotlin Class
インストール - New > Kotlin data class File from JSON > JSONをペーストしてGenerate
JSONからdata classが自動生成される便利すぎ
data class
とは
- データを保持するクラスに使う
-
equals()
とかtoString()
とかのメソッドが自動的に生える
Retrofitの書き方っぽい
interface GameApi {
@GET(GAMES_ENDPOINT)
suspend fun getGames(): Response<List<GameModel>>
}
@Module
@InstallIn(SingletonComponent::class)
object RetrofitModule {
@Singleton
@Provides
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Singleton
@Provides
fun provideGameApi(retrofit: Retrofit): GameApi {
return retrofit.create(GameApi::class.java)
}
}
ChatGPTにRetrofitのこと教えてもらったら普通に実用的でビビる、、
ぼく「KotlinでHTTP通信するコードを教えてください」「Retrofit2を使った例を教えてくれますか?」
はい、Retrofit2を使ったHTTP通信の例を示します。
まず、以下のようにbuild.gradleファイルにRetrofit2の依存関係を追加してください。
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
次に、Retrofit2のRetrofitインスタンスを作成し、APIインターフェースを定義します。
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
interface MyApi {
@GET("api/data")
suspend fun getData(): MyData
}
val retrofit = Retrofit.Builder()
.baseUrl("https://example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val myApi = retrofit.create(MyApi::class.java)
上記の例では、Retrofit.Builder()を使用してRetrofit2のRetrofitインスタンスを作成し、ベースURLをbaseUrl()メソッドで指定しています。addConverterFactory()メソッドでJSONのシリアライズ/デシリアライズにGsonを使用するように設定しています。
次に、APIインターフェースMyApiを定義しています。この例では、@GETアノテーションを使用してapi/dataエンドポイントに対するHTTP GETリクエストを定義しています。レスポンスのJSONデータはMyDataクラスにデシリアライズされます。また、この例ではsuspendキーワードを使用してコルーチンを使用していることを示しています。
最後に、retrofit.create()メソッドを使用して、Retrofit2がAPIインターフェースMyApiの実装クラスを生成し、myApi変数に代入しています。これで、myApi.getData()メソッドを呼び出すことで、HTTP GETリクエストを送信し、MyDataオブジェクトを取得することができます。
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
val data = myApi.getData()
println(data)
}
上記の例では、コルーチンを使用してmyApi.getData()メソッドを非同期に呼び出しています。取得したMyDataオブジェクトは、println()関数を使用して表示されます。
?:
はてなころんの名前は「エルビス演算子」
val result = nullableな変数 ?: "nullだったらこれ"
便利。Swiftでいう ??
(nil coalescing operator)
Packageの表示の変更
- Package右上の歯車 > Tree Appearance > Compact Middle Packages
チェックありだと空のPackageが省略表示になる
operator fun invoke()
とは
- Kotlinで定義されている関数呼び出し演算子をオーバーロードするための特別な構文
- クラスにメソッドを定義するまでもなく、インスタンスに
()
をつけて呼び出す処理を追加できる
class HelloWorld {
operator fun invoke() {
print("Hello World!")
}
}
val helloWorld = HelloWorld()
helloWorld() //"Hello World!"が出力されます。
ViewとViewModelの紐付け
@Composable
fun HomeScreen() {
val homeViewModel = viewModel(modelClass = HomeViewModel::class.java)
val games by homeViewModel.games.collectAsState()
LazyColumn {
items(games) {game: GameItem ->
GameCard(game = game)
}
}
}
@Composable
fun GameCard(game: GameItem) {
}
StateFlow
と MutableStateFlow
- Coroutinesライブラリで提供される、状態管理のためのもの
-
StateFlow
は、一度設定された状態を保持し、変更不可能な値を提供する。状態を更新するには、MutableStateFlow
を使用する
実例
-
MutableStateFlow
である_games
はprivateで保持 -
_games.asStateFlow()
でStateFlow
に変換したgames
をPublicに - 画面側で
games
をcollectAsState()
して表示する-
collectAsState()
はStateFlow
から値を取得して状態を更新するための関数
-
@HiltViewModel
class HomeViewModel @Inject constructor(private val getGamesUseCase: GetGamesUseCase): ViewModel() {
private val _games = MutableStateFlow(emptyList<GameItem>())
val games = _games.asStateFlow()
init {
getGames()
}
private fun getGames() {
viewModelScope.launch {
try {
val games = getGamesUseCase()
_games.value = games
} catch (_: Exception) {}
}
}
}
@Composable
fun HomeScreen() {
val homeViewModel = viewModel(modelClass = HomeViewModel::class.java)
val games by homeViewModel.games.collectAsState()
LazyColumn {
items(games) {game: GameItem ->
GameCard(game = game)
}
}
}
-
MutableStateFlow
をStateFlow
に変換する理由は、変更不可能な値を提供することで状態管理において安全性を確保するため-
MutableStateFlow
は値の変更が可能であるため、コードのどこからでも値を変更できる可能性がある。一方StateFlow
は変更不可能な値を提供するため、コードの外部から値を変更することができない
-
謝辞
ありがとうJorge Sanaguarayさん、ChatGPTさん、Zennさん
YouTubeのvideoIDが不正です