🐞

KotlinでAPI通信を行う

に公開

Kotlinを勉強し始めたばかりで間違い等ありましたら、御指摘して頂けますと幸いです。
APIを初めて叩くため、とりあえず表示させることだけを目的に書きました。
*ライブラリについては今回は記述しておりません。
*勉強のため非推奨のAPIを使っております。(後に改善予定)

PokeAPIを使う

今回はPokeAPIを使用してデータを取得します。
PokeAPI

レスポンスを受け取る型を定義

今回は『名前』『画像』を取得するための型を定義しました。
@SerializedName("front_default")はGsonなどのJSONライブラリを使って、JSONデータから Kotlin のオブジェクトに変換するときのマッピングのルールを定義しています。

data class PokemonResponse(
    val name: String,
    val sprites: Sprites
)

data class Sprites (
    @SerializedName("front_default")
    val frontDefault: String?
)

interfaceを定義

PokeAPIの『interface』を定義します。
@GET("pokemon/{id}")でHTTPのGETリクエストが使われ、『suspend』を使った関数はコルーチン内で非同期に呼び出されます。
@Path("id") id: Intで指定したポケモンのURLの{id}に差し込まれ、このURLで返ってきたレスポンスのJSONをパースし『PokemonResponse型』のオブジェクトとして返します。

interface PokeAPIService {
    @GET("pokemon/{id}")
    suspend fun getPokemon(@Path("id") id: Int): PokemonResponse
}

APIにリクエストを送って、レスポンスとしてデータを取得するインスタンスを作成する

『object』を使用してシングルトンのインスタンスを生成します。
上記で作成した『PokeAPIService』のinterfaceに基づくAPIクライアントの実装を生成します。
val api: PokeAPIService by lazy { ... }このプロパティは初めてアクセスされたときに、初期化されます。『by lazy』を使用することで、不要なタイミングでインスタンスが生成されず、リソースの無駄を防いでいます。
Retrofit・・・HTTP通信を簡単に行うためのライブラリ。

object RetrofitInstance {
    val api: PokeAPIService by lazy {
        Retrofit.Builder()
            .baseUrl("https://pokeapi.co/api/v2/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(PokeAPIService::class.java)
    }
}

現段階では『MainActivity』の『onCreate』内でシンプルにAPI通信を実行しています。

lifecycleScope.launch {
            for (id in 1..10) {
                try {
  //◆下記で1~10のIDのポケモンのデータを取得しpokemonListの配列に追加しています。
                    val pokemon = RetrofitInstance.api.getPokemon(id)
                    pokemonList.add(pokemon)
                    adapter.notifyItemInserted(pokemonList.size - 1)
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        }

全体

class MainActivity : AppCompatActivity() {

    private lateinit var recyclerView: RecyclerView
    private lateinit var adapter: PokeAPIListAdapter
    private val pokemonList = mutableListOf<PokemonResponse>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        WindowCompat.setDecorFitsSystemWindows(window,true)
        setContentView(R.layout.activity_main)
        recyclerView = findViewById(R.id.pokeRecyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this)
        adapter = PokeAPIListAdapter(pokemonList)
        recyclerView.adapter = adapter

        lifecycleScope.launch {
            for (id in 1..10) {
                try {
                    val pokemon = RetrofitInstance.api.getPokemon(id)
                    pokemonList.add(pokemon)
                    adapter.notifyItemInserted(pokemonList.size - 1)
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        }
    }
}

class PokeAPIListAdapter(private val pokemonList: MutableList<PokemonResponse>): RecyclerView.Adapter<PokeAPIListAdapter.ViewHolder>() {
    class ViewHolder(view: View): RecyclerView.ViewHolder(view) {
        val pokeName: TextView = view.findViewById(R.id.textView)
        val pokemonImage: ImageView = view.findViewById(R.id.imageView)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val pokeText = LayoutInflater.from(parent.context)
            .inflate(R.layout.pokemon_row, parent, false)
        return ViewHolder(pokeText)
    }

    override fun getItemCount(): Int = pokemonList.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val pokemon = pokemonList[position]
        holder.pokeName.text = pokemon.name

        Glide.with(holder.itemView.context)
            .load(pokemon.sprites.frontDefault)
            .into(holder.pokemonImage)
    }

}

data class PokemonResponse(
    val name: String,
    val sprites: Sprites
)

data class Sprites (
    @SerializedName("front_default")
    val frontDefault: String?
)

interface PokeAPIService {
    @GET("pokemon/{id}")
    suspend fun getPokemon(@Path("id") id: Int): PokemonResponse
}

object RetrofitInstance {
    val api: PokeAPIService by lazy {
        Retrofit.Builder()
            .baseUrl("https://pokeapi.co/api/v2/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(PokeAPIService::class.java)
    }
}

Discussion