ミーハーのブログ

ミーハーだからを免罪符に適当なことを言う。

AndroidでAPIから情報をゲットしてみよう

この記事はあくあたん工房クリスマスアドベントカレンダー22日目の記事です.

adventar.org

はじめに

最近Androidアプリ開発の勉強をしているのですが,WebAPIを使ってみたくなったので今回はTwitterAPIで指定した単語を含むツイートを検索して遊んでみようと思います.

環境

Twitter APIへアクセスするための各種キーの取得

Twitter APIの使用申請

Twitter APIを使用するには使用のために申請をする必要があります. 詳しくは以下の記事等を参考にしてみてください.

2020年度版 Twitter API利用申請の例文からAPIキーの取得まで詳しく解説 | 新宿のホームページ制作会社 ITTI(イッティ)

申請の返事はだいたい1日か2日で返ってきたと思います.

Twitter APIの各種キーを保存

申請が通るとAPI keyAPI key secretBearer tokenというものがもらえます. API keyとsecretはあとから確認できるのですが,Bearer tokenは確認できないので(再生成はできる)しっかり保存しておきましょう.

ツイートやリツイートなどの機能以外はこのBearer tokenのみで行うことができます.

最初にやること

マニフェストファイルにインターネット使用のパーミッションを設定

これがないと通信してデータを持ってくるみたいなことができません. 以下の1文をapp/src/main/AndroidManifest.xmlに追記してください.

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

APIから情報を得るためのライブラリを導入

  • Retrofit:APIコールを簡単に行ためのツール
  • coroutine:実行スレッドを分けて並列処理するための仕組み

app/build.gradledependencesに以下を追記してください.

    implementation 'com.squareup.retrofit2:retrofit:2.7.1'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'

コードを書いていく

APIを使用するためのinterfaceを実装

今回使用するエンドポイントはhttps://api.twitter.com/2/tweets/search/recent?query=なので,そこをGETで叩くように作ります.

Twitter APIを叩くためには認証情報が必要なのでヘッダに指定してあげます. また,検索したい単語を指定するためにqueryを入れるようにしておきます.

package com.example.adventcalendar

import okhttp3.ResponseBody
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Query

interface TwitterApi {

    @GET("search/recent")
    suspend fun fetchTweets(
        @Header("Authorization") accessToken: String,
        @Query("query") searchWord: String? = null
    ):ResponseBody
}

これでfetchTweets()トークンとクエリを入れて実行してあげることでAPIからレスポンスが返ってきます.

画面を作る

今回は任意の単語をクエリとしてAPIのレスポンスを受け取るだけなので,単語を入力するEditTextButtonだけ追加します.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">


        <EditText
            android:id="@+id/edit"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Get API response!"/>

        <TextView
            android:id="@+id/text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAlignment="center"
            android:text="Response here" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

f:id:rideonyan:20201222001151p:plain

処理部分を作る

Retrofitを設定して作成したAPIインスタンス化する

今回は簡単に作るために認証トークンを直に書いていますが,もしGitHubなどで管理する場合はhoge.propertiesなどに書いてBuildConfigで参照するようにしましょう.

private val BEARER_TOKEN = "************"
private val BASE_URL = "https://api.twitter.com/2/tweets/"

private val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .build()
private val api = retrofit.create(TwitterApi::class.java)

Coroutineについて

ここで,一旦話が逸れるのですが,Coroutineについて説明します.

Androidアプリでは,Mainスレッドで画面の更新などの処理を行っているため,時間がかかる処理をMainスレッドで行ってしまうと,それが終わるまで画面が更新できません.

そのため,時間がかかる処理は別スレッドで行わなければいけないことになっており,ネットからデータを持ってくる処理は遅くなる可能性が高いので,別スレッドで処理を行う必要があります.

そこで利用されるのがCoroutineです. Coroutineを利用して別スレッドに処理させることで上記の問題を解決します.

使い方は簡単で,CoroutineScope()の引数として処理させたいスレッドを指定し,.launch内に処理を書きます. 今回はIOスレッドにAPIからデータを取得する処理をしてもらいましょう.

CoroutineScope(Dispatchers.IO).launch {
    //  APIからデータを取得する処理
 }

さて,APIから取得したデータをTextViewに反映したいのですが,ここで一つ問題が生じます.

CoroutineScope内で定義した値は同スコープ内でしか使えないため,同スコープ内にTextView の書き換え処理を書く必要があります. しかし,Mainスレッド以外で画面の更新をするとエラーになってしまいます.

このような,「あるスレッドのCoroutineScope内で別スレッドを動かしたい」となったときに使うのがwithContext()です.

CoroutineScope(Dispatchers.IO).launch {
                // APIからデータを取得する処理
                withContext(Dispatchers.Main) {
                    // 取得したデータで画面を更新する処理
                }
            }

ボタンを押したときの挙動を定義

話を実装の方に戻します. ボタンのListenerを以下のようにonCreate()内に定義します.

val button = findViewById<Button>(R.id.button)
val textBox = findViewById<EditText>(R.id.edit)
val textView = findViewById<TextView>(R.id.text)

button.setOnClickListener{
    val query = textBox.text.toString()
    CoroutineScope(Dispatchers.IO).launch {
        val response = api.fetchTweets(accessToken = "Bearer $BEARER_TOKEN", searchWord = query).string()
        withContext(Dispatchers.Main) {
            textView.text = response
        }
    }
}

完成

実行してみるとこんな感じ.

f:id:rideonyan:20201222013237g:plain

ちゃんとレスポンスが返ってきてTextViewが更新されていますね.

さいごに

今回はkotlinで書いたAndroidアプリでTwitter APIから情報を取得するまでの流れを紹介しました.

取得した情報を実際に使うときはレスポンスのjsonをパースしていい感じに表示するのですが,かなり長くなってしまうので今回は取得部分までとしました.気が向いたらパースしてList表示する部分を書くかもしれません.

Web APIが使えるようになると作れるものの幅がグッと広がるので,ぜひ皆さんも試してみてください!

参考にした書籍

gihyo.jp