Closed15

Kotlin/Androidアプリ作りながらメモ

akeomeakeome

下準備

ライブラリの導入系

build.gradle(Project)
    id 'org.google.dagger.hilt.android' version '2.43.2' apply false
  • dagger... Google謹製のDIライブラリ
build.gradle(Module)
    // 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)に記載する)
akeomeakeome
KotlinMVVMTutorial.kt
@HiltAndroidApp
class KotlinMVVMTutorial: Application()
AndroidManifest.xml
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:name=".app.KotlinMVVMTutorial"
  • インターネット許可
  • アプリ名の設定?
akeomeakeome

JSON To Kotlin Calss便利すぎ

  • Preferences > Pluguins > JSON To Kotlin Class インストール
  • New > Kotlin data class File from JSON > JSONをペーストしてGenerate

JSONからdata classが自動生成される便利すぎ

akeomeakeome

data class とは

  • データを保持するクラスに使う
  • equals() とか toString() とかのメソッドが自動的に生える
akeomeakeome

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)
    }
}
akeomeakeome

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()関数を使用して表示されます。

akeomeakeome

?: はてなころんの名前は「エルビス演算子」

val result = nullableな変数 ?: "nullだったらこれ"

便利。Swiftでいう ?? (nil coalescing operator)

akeomeakeome

Packageの表示の変更

  • Package右上の歯車 > Tree Appearance > Compact Middle Packages

チェックありだと空のPackageが省略表示になる

akeomeakeome

operator fun invoke() とは

  • Kotlinで定義されている関数呼び出し演算子をオーバーロードするための特別な構文
  • クラスにメソッドを定義するまでもなく、インスタンスに () をつけて呼び出す処理を追加できる

https://codelabsjp.net/kotlin-practice-invoke/

class HelloWorld {
    operator fun invoke() {
        print("Hello World!")
    }
}
val helloWorld = HelloWorld()
helloWorld()              //"Hello World!"が出力されます。

akeomeakeome

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) {

}
akeomeakeome

StateFlowMutableStateFlow

  • Coroutinesライブラリで提供される、状態管理のためのもの
  • StateFlow は、一度設定された状態を保持し、変更不可能な値を提供する。状態を更新するには、 MutableStateFlow を使用する

実例

  • MutableStateFlow である _games はprivateで保持
  • _games.asStateFlow()StateFlow に変換した games をPublicに
  • 画面側で gamescollectAsState() して表示する
    • collectAsState()StateFlow から値を取得して状態を更新するための関数
HomeViewModel.kt
@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) {}
        }
    }
}
Home.kt
@Composable
fun HomeScreen() {
    val homeViewModel = viewModel(modelClass = HomeViewModel::class.java)
    val games by homeViewModel.games.collectAsState()

    LazyColumn {
        items(games) {game: GameItem ->
            GameCard(game = game)
        }
    }
}
  • MutableStateFlowStateFlow に変換する理由は、変更不可能な値を提供することで状態管理において安全性を確保するため
    • MutableStateFlow は値の変更が可能であるため、コードのどこからでも値を変更できる可能性がある。一方 StateFlow は変更不可能な値を提供するため、コードの外部から値を変更することができない
このスクラップは2023/02/19にクローズされました