はじめに
前回の記事ではTwitter依存解消のため、 Python + Tkinter を使ってTwitterに投稿できるシンプルなデスクトップアプリを作成しました。今回はその延長として、Androidアプリ をKotlinで実装しました。
タイムラインを閲覧せず、投稿だけをモバイルで完結させたいというニーズに対応するものです。
対象読者
- Android Studio が動く環境を持っている方
- Twitterに投稿するシンプルな機能だけを使いたい方
ゴール・動作概要
- アプリ上でテキストを入力し、「ツイートする」ボタンを押すと投稿できる
- Twitter API v2 の投稿に対応するため、jp.takke.twitter4j-v2 という非公式のラッパーライブラリを導入しています(Twitter4J の拡張版)。公式APIとは異なり一部制約や仕様差があるため、実運用では事前検証をおすすめします
必要な準備
1. Twitter API キーの取得
Twitter Developer Portalにて、以下の情報を取得してください。
- Consumer Key
- Consumer Secret
- Access Token
- Access Secret
取得方法は「Twitter API認証情報の取得方法」に書いてあります
※本稿ではコード中に直接記載していますが、実運用では
BuildConfig
やlocal.properties
を使って外部管理することを推奨します。
2. 開発環境
項目 | 内容 |
---|---|
Android Studio | Meerkat 2024.3.1 Patch 1 |
Kotlin | 2.0.21 |
AGP (Android Gradle Plugin) | 8.9.1 |
minSdk / targetSdk | 24 / 35 |
プロジェクト構成
app/
├─ manifests/
│ └─ AndroidManifest.xml
├─ java/com/example/simpletweetpost/
│ ├─ MainActivity.kt
│ └─ test/ExampleUnitTest.kt
│ └─ androidTest/ExampleInstrumentedTest.kt
├─ res/
│ ├─ layout/activity_main.xml
│ ├─ values/
│ │ ├─ strings.xml
│ │ ├─ colors.xml
│ │ └─ themes.xml, themes.xml (night)
│ ├─ drawable/
│ └─ mipmap/
│ └─ ic_launcher などアイコン類
├─ build.gradle.kts
├─ libs.versions.toml
test / androidTest フォルダには自動生成されたテストファイルが含まれます。本アプリでは利用していませんが、ユニットテストやUIテストを導入する際のベースになります。
実装コード
MainActivity.kt
package com.example.simpletweetpost
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import twitter4j.TwitterFactory
import twitter4j.conf.ConfigurationBuilder
import twitter4j.v2 // 重要:拡張プロパティのインポート
class MainActivity : AppCompatActivity() {
// Twitter API 認証情報
private val CONSUMER_KEY = "YOUR_CONSUMER_KEY"
private val CONSUMER_SECRET = "YOUR_CONSUMER_SECRET"
private val ACCESS_TOKEN = "YOUR_ACCESS_TOKEN"
private val ACCESS_SECRET = "YOUR_ACCESS_SECRET"
private lateinit var tweetEditText: EditText
private lateinit var tweetButton: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// UIコンポーネントの取得
tweetEditText = findViewById(R.id.tweetEditText)
tweetButton = findViewById(R.id.tweetButton)
// ツイートボタンのクリックリスナー
tweetButton.setOnClickListener {
val tweetText = tweetEditText.text.toString().trim()
if (tweetText.isEmpty()) {
showAlertDialog("警告", "ツイート内容が空です。入力してください。")
return@setOnClickListener
}
postTweet(tweetText)
}
}
private fun postTweet(message: String) {
// UIスレッドをブロックしないようにコルーチンを使用
CoroutineScope(Dispatchers.IO).launch {
try {
// Twitter認証設定
val cb = ConfigurationBuilder()
cb.setOAuthConsumerKey(CONSUMER_KEY)
cb.setOAuthConsumerSecret(CONSUMER_SECRET)
cb.setOAuthAccessToken(ACCESS_TOKEN)
cb.setOAuthAccessTokenSecret(ACCESS_SECRET)
cb.setJSONStoreEnabled(true) // JSONサポートを有効に
val twitter = TwitterFactory(cb.build()).instance
// Twitter API v2を使ってツイートを投稿
val response = twitter.v2.createTweet(text = message)
// UIスレッドに戻ってダイアログを表示
withContext(Dispatchers.Main) {
showAlertDialog("成功", "ツイートが投稿されました!\nツイートID: ${response.id}")
tweetEditText.text.clear()
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
showAlertDialog("エラー", "ツイートの投稿中にエラーが発生しました: ${e.message}")
}
}
}
}
private fun showAlertDialog(title: String, message: String) {
AlertDialog.Builder(this)
.setTitle(title)
.setMessage(message)
.setPositiveButton("OK", null)
.show()
}
}
(投稿成功後のダイアログを表示させない場合は showAlert() 呼び出しを削除)
activity_main.xml
<?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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<TextView
android:id="@+id/labelTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/title"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/tweetEditText"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="8dp"
android:background="@android:drawable/editbox_background"
android:gravity="top|start"
android:hint="@string/input_hint"
android:inputType="textMultiLine"
android:padding="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/labelTextView" />
<Button
android:id="@+id/tweetButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/tweet_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tweetEditText" />
</androidx.constraintlayout.widget.ConstraintLayout>
strings.xml
<resources>
<string name="app_name">SimpleTweetPost</string>
<string name="input_hint">ここにツイート内容を入力</string>
<string name="title">ツイート内容を入力してください:</string>
<string name="tweet_button">ツイートする</string>
</resources>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SimpleTweetPost"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
build.gradle.kts(抜粋)
dependencies {
implementation("org.twitter4j:twitter4j-core:4.0.7")
implementation("io.github.takke:jp.takke.twitter4j-v2:1.4.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
// その他の依存は libs.versions.toml に記載
}
実装のポイント
GUI: ConstraintLayout + EditText + Button の最小構成
認証処理: Twitter4J に API キーを設定して OAuth 認証
非同期処理: Coroutine で非同期処理を実装、UIスレッドをブロックしない
結果表示: 投稿完了/失敗を AlertDialog で通知
ビルドと実行手順
必要に応じて API キーを環境に応じて埋め込む
Android Studio で実行
入力 → 投稿ボタン → 結果表示
まとめ
Kotlin + Twitter4J でシンプルなTwitter投稿アプリが構築可能
投稿専用アプリなら、最小限の構成で十分動作する
実運用時はAPIキーの管理と投稿制限(Rate Limit)に注意すること
注意
無料プランでは、API経由での投稿上限が 1日あたり17件 に制限されています(2025/04/21時点)。上限を超える可能性がある場合は、有料プランの導入や投稿タイミングの調整を検討してください。