cloud9_note

cloud9に限らないメモ

View on GitHub

Android アプリ開発

開発環境構築

(VSCodeは諦めて)Android Studioを導入する。

  1. ダウンロード
  2. 下記実行。
     # ファイル配置
     sudo cp ${ダウンロードしたファイル} /usr/local
    
     # 解凍
     cd /usr/local
     sudo zxvf ${ダウンロードしたファイル} 
    
     # 初回起動
     cd /usr/local/android-studio/bin
     bash studio.sh
    
    
  3. PATHに追加
    • ~/.bashrcに下記を追記
        # Android Studio
        export ANDROID_STUDIO_HOME="/usr/local/android-studio"
        export PATH="$PATH:$ANDROID_STUDIO_HOME/bin"
      

日本語化

  1. Android Studioのバージョンを確認
    1. 起動
    2. 左下のOptions Menu -> About
  2. JetBrains Language Pack for Android Studioから、一致するバージョンの言語パックをダウンロード
  3. 下記実行
     sudo cp ${ダウンロードしたファイル} $ANDROID_STUDIO_HOME/plugins
     cd $ANDROID_STUDIO_HOME/plugins
     sudo unzip ${ダウンロードしたファイル}
     sudo rm ${ダウンロードしたファイル}
    
  4. Android Studio -> Plugins -> 歯車アイコン -> Install Plugin from Disk
  5. /usr/local/android-studio/plugins/ja.${ダウンロードしたバージョン}/lib/ja.${ダウンロードしたバージョン}.jar
  6. Android Studio再起動
  7. Customize -> Language and Legionで日本語を選択
  8. Android Studio再起動

参考

起動

studio

adbコマンド

Ubuntuで実機を認識させるための設定

sudo apt install android-tools-adb
cd /etc/udev/rules.d/
sudo touch 51-android.rules
sudo chmod a+r 51-android.rules
SUBSYSTEM=="usb", ATTR{idVendor}=="VENDORID", MODE="0666", GROUP="plugdev"

(VENDORIDは、lsusbコマンドで確認できます。例:Googleデバイスは「18d1」)

sudo udevadm control --reload-rules
sudo udevadm trigger

実機での実行

KVM設定(エミュレータ高速化)

# インストール
sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils

sudo adduser $USER kvm
sudo adduser $USER libvirt

# ログアウト/ログインする
# インストールされているか確認
kvm-on

Android端末が接続されていることを確認する

Android端末を接続しているにもかかわらず、うまく認識していないときに参照する。

Android Studioを起動していると、ps aux | grep adbを実行すると、PIDが度々更新されるのが確認できる。
その場合はAndroid Studioを停止する。

USB

lsusb

adb確認

# デバイスの確認
adb devices

# 停止/開始
adb kill-server && adb start-server
# 実行すると、Android側で切断/接続を検出する。接続モードの問い合わせが行われるので、画面から操作する。

プロジェクトの基本構成

コマンド

gradle init --type java-application

ディレクトリ構成

my-android-app/
├── app/
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/
│   │   │   │   └── パッケージ名/
│   │   │   ├── res/
│   │   │   │   ├── layout/
│   │   │   │   ├── values/
│   │   │   │   └── drawable/
│   │   │   └── AndroidManifest.xml
│   │   └── test/
│   │       └── java/
│   │            └── パッケージ名/
│   └── build.gradle
├── build.gradle
├── settings.gradle
└── gradle.properties

build.gradle

buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:8.9.0'
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()  
    }

    tasks.withType(JavaCompile).configureEach {
        options.fork = true
        options.forkOptions.executable = '/usr/lib/jvm/jdk-17.0.6+10/bin/javac'
    }
}

settings.gradle

rootProject.name = 'android_helloworld'
include ':app'

gradle.properties

# Project-wide Gradle settings.
org.gradle.java.home=/usr/lib/jvm/jdk-17.0.6+10
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
android.useAndroidX=true
android.nonTransitiveRClass=true

gradle/wrapper/gradle-wrapper.properties

gradleのバージョンが合わない場合はここを修正する。

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

app/build.gradle

/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java application project to get you started.
 * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
 * User Manual available at https://docs.gradle.org/7.6/userguide/building_java_projects.html
 */

plugins {
    id 'com.android.application'
}

android {
    namespace 'ittimfn.android.helloworld'
    compileSdk 35

    defaultConfig {
        applicationId "ittimfn.android.helloworld"
        minSdk 21
        targetSdk 35
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.7.0'
    implementation 'com.google.android.material:material:1.12.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.2.1'

    // JUnit5
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.12.1'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.12.1'
    testImplementation 'org.junit.platform:junit-platform-launcher:1.12.1'
    
    testImplementation 'org.hamcrest:hamcrest:3.0'
    androidTestImplementation 'androidx.test.ext:junit:1.2.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'    

}

// JUnit5のテストを実行するための設定
tasks.withType(Test) {
    useJUnitPlatform()
    testLogging {
        events "passed", "skipped", "failed"
    }
    
    // テストは常に実行されるようにする(ファイルに変更がなくても実行)
    outputs.upToDateWhen { false }
}

app/src/main/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="ittimfn.android.helloworld">

    <!-- アイコンを追加したい場合は下記を追加。-->
    <!-- android:icon="@mipmap/ic_launcher" -->
    <!-- android:roundIcon="@mipmap/ic_launcher" -->
    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.AppCompat">
        <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>

app/src/main/res/layout/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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

app/src/main/res/values/strings.xml

<resources>
    <string name="app_name">Hello World</string>
    <string name="hello_world">Hello World!</string>
</resources>

app/src/java/${package}/MainActivity.java

package ittimfn.android.helloworld;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Android StudioなしでコマンドラインからAndroidエミュレータを起動する

前提

  1. adbインストール済み
  2. Android Studioコマンドラインツールインストール済み

コマンド

# インストール可能なイメージ
sdkmanager --list

# イメージのインストール
sdkmanager "$image_name"

# 使用可能なイメージ
avdmanager list target

# デバイス表示
avdmanager list device

# エミュレータ作成
# 任意のインスタンス名
name=
# 「インストール可能なイメージ」
package=
# 機種名
device=
avdmanager create avd --name $name --package $package --device $device

# 使用可能なエミュレータを表示
emulator -list-avds

# エミュレータ起動
emulator -avd $エミュレータ名

起動時にSIMの設定をする

tel=09012345678

# ドコモ
mnc=10
emulator \
  -avd $name \
  -netspeed lte -netdelay none \
  -prop gsm.sim.operator.numeric=440$mnc \
  -prop gsm.sim.operator.iso-country=jp \
  -phone-number $tel

mnc

事業者 MCC MNC numeric 値
NTT ドコモ 440 10 44010
KDDI (au/UQ) 440 50 / 51 44050 / 44051
ソフトバンク 440 20 44020
楽天モバイル 440 11 44011

エミュレータにアプリをインストールする

# 拡張子 apkのファイルがAndroidアプリ。
# エミュレータが起動している状態で実行する。
adb install ./app/build/outputs/apk/debug/app-debug.apk
# app-debug.apkをビルドする
./gradlew assembleDebug
# app-debug.apkをインストールする
./gradlew installDebug

エミュレータからログを取得してホストに送る

  1. エミュレータメニューの一番下
  2. Bug report
  3. Bug report dataをチェック
  4. エラーになる操作を行う
  5. Save Reportボタンを押下

エミュレータから電話をかける

本物に対しても使える。

# 発信先
tel_no=123
adb shell am start -a android.intent.action.CALL -d tel:$tel_no

エミュレータに電話をかける

エミュレータにしか使えない。

# 発信元
tel_no=123
adb emu gsm call $tel_no

非通知でかける

# 直接指定するとかけられない。変数に代入する必要がある。
tel_no=#67080053
adb emu gsm call $tel_no

Android端末の開発者モードを有効化する

  1. 設定 -> デバイス情報 -> ビルド番号を7回タップ
  2. 設定 -> システム -> 開発者向けオプションを有効化

ログ取得

adb logcat > logfile.txt

# ActivityManagerとMyAppのログのみを取得
adb logcat ActivityManager:I MyApp:D *:S > filtered_log.txt

# おすすめ
adb logcat -c
adb logcat -v long *:E

SQLite + inflater + オリジナルViewのサンプル

Androidの通知の実装

Androidの通知の実装

チャネルを作成する

Android 8.0(API レベル 26)以降の機能。Android 8.0以降で、作成されていない場合はエラーになる。Android 8.0より前の場合も作成を試みるとエラーになる。

  1. 一意のチャネル ID、ユーザーが認識できる名前、重要度を指定して、NotificationChannel オブジェクトを作成します。
  2. 必要に応じて、システム設定内でユーザーに表示する説明を setDescription() で指定します。
  3. createNotificationChannel() に通知チャネルを渡して登録します。
import android.os.Build;
import android.util.Log;

import android.app.NotificationManager;
import android.app.NotificationChannel;

public static void createNotificationChannels(String channelId, String channelName, String description, NotificationManager notificationManager, String tag) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel channel = new NotificationChannel(
            channelId,
            channelName,
            NotificationManager.IMPORTANCE_HIGH
        );
        channel.setDescription(description);
        notificationManager.createNotificationChannel(channel);
        Log.d(tag, "NotificationChannelを作成: " + channelName);
    } else {
        Log.d(tag, "Android 7以前のため、NotificationChannelは不要");
    }
}
// 呼び出す側
import android.content.Context;
import android.app.NotificationManager;

NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {

    Utils.createNotificationChannels(
        notificationEnum.getChannelId(),
        notificationEnum.getTitle(),
        notificationEnum.getDescription(),
        notificationManager,
        getTag()
    );
}


参考