Android API Level

AndroidバージョンとAPIレベル一覧です。

2025年4月14日現在

NoバージョンAPIレベルバージョンコード備考
1Android 1636BAKLAVA
2Android 1535VANILLA_ICE_CREAM
3Android 1434UPSIDE_DOWN_CAKE
4Android 1333TIRAMISU
5Android 1232S_V2
631S
7Android 1130R
8Android 1029Q
9Android 928P
10Android 8.127O_MR1
11Android 8.026O
12Android 7.1.1
Android 7.1
25N_MR1
13Android 7.024N
14Android 6.023M
15Android 5.122LOLLIPOP_MR1
16Android 5.021LOLLIPOP
17Android 4.4W20KITKAT_WATCHウェアラブル向け
18Android 4.419KITKAT
19Android 4.318JELLY_BEAN_MR2
20Android 4.2.2
Android 4.2
17JELLY_BEAN_MR1
21Android 4.1.1
Android 4.1
16JELLY_BEAN
22Android 4.0.4
Android 4.0.3
15ICE_CREAM_SANDWITCH_MR1
23Android 4.0.2
Android 4.0.1
Android 4.0
14ICE_CREAM_SANDWITCH
24Android 3.213HONEYCOMB_MR2
25Android 3.1.x12HONEYCOMB_MR1
26Android 3.0.x11HONEYCOMB
27Android 2.3.4
Android 2.3.3
10GINGERBREAD_MR1
28Android 2.3.2
Android 2.3.1
Android 2.3
9GINGERBREAD
29Android 2.2.x8FROYO
30Android 2.1.x7ECLAIR_MR1
31Android 2.0.16ECLAIR_0_1
32Android 2.05ECLAIR
33Android 1.64DONUT
34Android 1.53CUPCAKE
35Android 1.12BASE_1_1
36Android 1.01BASE
Android api level

インストール済アプリがピックアップできないAndroid 12

2022年7月19日

開発環境
OS:Windows 10
SDK:Android Studio Chipmunk | 2021.2.1

概要

Android 12におけるセキュリティ強化。
それに伴い、権限なしにインストール済アプリをピックアップすることができなくなってました、という話です。

スマホの機種変更

androidアプリ開発をしている以上もちろんAndroid 12での動作検証も行います。

とは言っても検証はもっぱら仮想デバイスでした。

それが最近、スマホを買い替え、ようやくAndroid 12の実機が我が手元に。

生でAndroid 12に触れる機会ができたので早速、アプリを色々と動かしてみたのですが。。。

早々と想定通りに動作していない機能を発見、慌てて改修した次第です。

仮想デバイスでは検証しにくい機能なんです。。。

Android12のセキュリティ強化

想定通りに動作しなかった機能とは何か?

それは、インストール済アプリのピックアップ機能です。

コードで表すと Packagemanager の getInstalledPackages です。

Android 12のセキュリティ強化に伴い、権限、もしくは、<queries>定義なしに

インストール済アプリをピックアップできなくなった、とのこと。

この機能がダメになるとランチャー系のアプリは全滅です。

そこで2パターンの方法で改修を行いました。

パターン1)どうしてもインストール済アプリを全て知りたい

ランチャー系アプリの場合、インストール済アプリを全てピックアップしたいので権限追加で対応します。

AndroidManifest.xmlに以下のパーミションを追加します。

<uses-permission android:name=”android.permission.QUERY_ALL_PACKAGES”/>

対応は以上です。

あとはビルドしてリリースするだけなのですが、なにせ強力な権限です。

Google Play Consoleの「アプリのコンテンツ」において権限の用途を説明する必要があります。

Google Play Console「アプリのコンテンツ」

まずは目的の説明を記載し、そして、用途の選択を行います。

機密情報にかかわる権限とAPI(1)

ランチャーアプリの場合、

目的は「ランチャーアプリ。アプリリストを表示するため」、

用途は「アプリの機能」、と明確です。

そして、最後に求められるのが動画です。

機密情報にかかわる権限とAPI(2)

ざっくりと作ってYouTubeにアップしてみました。

まさか動画を求められるとは。。。

すると、まあ、なんということでしょう。

リリースしてから1時間もしないうちに審査が通り、インストールできるようになりました。

パターン2)必要なアプリだけインストールされていることを知りたい

アプリの連携のため、連携対象のアプリがインストールされているか知りたい。

そのような場合、

AndroidManifest.xmlに以下の定義を追加します。

(アプリ”com.sample.application”がインストールされているか知りたい場合)

<manifest ・・・>

    <queries>
        <package android:name="com.sample.application" />
    </queries>

    <application ・・・

    </application>

</manifest>

これで Packagemanager の getInstalledPackagesまわりは改修しなくても対象アプリをピックアップしてくれるようになります。

あとはビルドしてリリースするだけです。

こちらのパターンの場合、Google Play Consoleで「アプリのコンテンツ」を記載する必要もありません。

Android 12のセキュリティ強化に伴う対応は以上です。

Android Studioを更新したら起動しなくなった話

2022年6月13日

開発環境
OS:Windows 10
SDK:Android Studio Bumblebee | 2021.1.1

概要

全ては日本語化パッチに通ずる。

Android Studioを更新する

さて、アプリの改修でも行おう。と、Android Studioを起動するとたまに出る右下のポップアップ。

kotlinのバージョンアップ通知、emulatorのバージョンアップ通知、などなど。

バージョンアップを促してくるわけですが、基本的には見かけたらすぐにバージョンアップしています。

ですが、ただ1つ、Android Studioのバージョンアップだけは慎重になります。

なぜならだいたいバージョンアップをすると問題が発生するからです。

なので、Android Studioのバージョンアップだけは時間的に余裕のあるときに実施するわけです。

そして、今日。

ハチからリスにバージョンアップした結果がこれです。

やはりハマってしまいました。

バージョンアップ時に発生する問題はたいてい日本語化対応による弊害。

対処法は2つ。

①日本語化パッチを当てなおす。

②再インストールする

①を実施、それでも駄目なら②を実施。

今までもこれで乗り切ってきたわけです。

それが今回、①、②、どちらを実行してもエラーが解除されない事態に。。。

そこで “Could not find main class com/intellij/idea/Main” で検索しみると。

stackoverflowなどでも検索結果がヒットするものの、解決策を見ると環境変数JAVA_HOMEの設定が~。

どうやら的外れ。

そこで基本に立ち戻り、日本語化を解除してみました。

C:\Users\~\AppData\Roaming\Google\AndroidStudio2021.2\studio64.exe.vmoptions

上記ファイルの最下段、”-Xverity”と”-javaagent:~plaiades.jar”をコメントアウト。

すると、あっさり起動しました。

日本語化パッチ、またやりおったか。

Googleマップが表示されない事象の発生から解決まで

概要

2022年3月9日早朝、自作アプリ“みてみて”を起動したところ、Googleマップが表示されないことに気付く。

慌ててGoogleマップを使用している自作アプリを片っ端から起動した結果、全滅でした。

なぜGoogleマップが表示されなくなったのか?

事象の発生から解決までの道のりを記事にしました。

Noアプリ名事象発生当時の最終リリース日
みてみて2022年2月24日
伊勢125社巡り2022年2月5日
伊勢テイクアウトなび2020年6月4日
みまもってね2022年1月15日
Googleマップが表示されなくなったアプリ一覧
伊勢125社巡り
マップ表示(改修前)
みてみて
マップ表示(改修前)

Googleマップの実装

最初に自作アプリにGoogleマップをどう実装しているかをざっくり説明します。

まず始めにデフォルトのMapsActivityを追加します。

ただし、そのままだとフラグメントのみの構成のため、ConstraintLayoutに組み込むようレイアウトを変更します。

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/constraint_map">
        <fragment
            android:id="@+id/map"
            android:name="com.google.android.gms.maps.SupportMapFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>

Activity側のベースロジックは変更しません。

あとはGoogle Cloud Platformでプロジェクトを作成し、APIの有効化でMaps SDK for Androidを選択。

APIキーを入手後、AndroidManifest.xmlのメタデータにAPIキーを定義します。

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="MAP_API_KEY" />

最後にMapsActivityのタイトルを非表示にするため、Activityにスタイルを設定して完成です。

マップ表示は基本的に”com.google.android.gms.maps.SupportMapFragment”まかせの処理としています。

突然Googleマップが表示されなくなった

自作アプリは随時、改修を行っています。

今回問題が発生したアプリも2022年2月24日に改修してリリースしたものがありました。

もちろんリリース前に動作確認しており、そのときにはマップ表示に問題がなかったのです。

それから約2週間後、突然Googleマップが表示されなくなりました。

つまりアプリに手を加えていないタイミングでマップが表示されなくなったということです。

そうなるとまずはアプリ内部の処理ではなく外部を疑いたくなるものです。

解決までの道のり

まず疑ったのはマップデータが受け取れてないのでは?ということです。

GoogleマップAPIが使えなくなっていないか?

Google Clound Platformでプロジェクトの設定やAPIキーの制限を確認しますが問題ありませんでした。

次にGoogleマップの表示だけを行うサンプルアプリを作成し、APIキーを使ってみます。

するとGoogleマップが表示されました。

GoogleマップAPIは正常に機能していたわけです。

確かにGoogle Clound PlatformでAPIのトラフィックを確認するとアプリではマップが表示されてないにも関わらず、データ通信は発生していました。

伊勢125社巡りのAPI(Map SDK for Android)のトラフィック

マップデータは受け取れている。となると次はアプリ内部の処理を疑わざるおえません。

アプリをデバッグモードで起動し、試しにデフォルトのMapsActivityを追加してみます。

そして、アプリを動かしてみると。。。

デフォルトのMapsActivityにGoogleマップが表示されました。

そうなるとデフォルトのMapsActivityと作り込んだMapsActivityの違いを調べるだけです。

デフォルトのMapsActivityにコードを徐々に追加し、マップが表示されなくなるか確認。

なんと最後までGoogleマップは表示されました。

つまり、MapsActivityのコード内容には問題がなかったわけです。

あと違いがあるのは何か?

ここでスタイル設定に気が付きます。

デフォルトのMapsActivityにタイトル非表示用に定義したスタイルを設定してみると。。。

Googleマップが表示されなくなりました。

そう、原因はスタイル定義だったんです。

ちなみに問題となったスタイルが以下です。

    <style name="AppTheme.NoTitleStatus" parent="@style/Theme.AppCompat.NoActionBar">
        <item name="android:background">@color/colorBackground</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowFullscreen">false</item>
        <item name="android:colorBackgroundCacheHint">@null</item>
        <item name="android:windowIsTranslucent">true</item>
    </style>

このなかで問題を引き起こしたのがバックグランド設定でした。

事象から察するにマップフラグメントがバックグランドの裏に隠れるようになったのではないでしょうか。

今回の改修案としてはバッググランド設定を透明に変更したスタイルを新たに用意。

それをMaspActivityに設定することで解決しました。

それにしてもまさかアプリ内部処理が要因だったとは・・・

伊勢125社巡り
マップ表示(改修後)
みてみて
マップ表示(改修後)

後書き

今回の件で一番驚いたのはアプリ内のライブラリのバージョンを上げたり、コードを変更したりすることなく、Googleマップが表示されなくなったことです。

アプリの更新を行わずにアプリの挙動が変わるとは思っていなかったため、最初は外部要因(マップAPIのIF依存)だとばかり思っていました。

結局、今回の事象の真の要因は何かは分からないですが、アプリ自体を更新しなくてもアプリの挙動が変わってしまうことがあるというのは勉強になりました。

日付のソートが失敗していた話

概要

型変換の認識不足により日付のソートに失敗しておりました。

Long型とInt型、型変換する際は気を付けねば、という話です。

2022年の投稿がソートで正しく表示されないバグ

投稿アプリ「みてみて」には全ての投稿をリスト表示する画面があります。

このリストでは新着順、いいね順、距離順の3パターンでソートできるようにしているのですが。。。

2022年の最新の投稿を新着順でソートした際に先頭に表示されないバグが発覚。

ロジックを見直しても一見、おかしなところも見当たらず。

そこでソートロジックを切り取り、テストアプリを作成してログを確認。

ようやく理由が判明した、というわけです。

日付をソートするテストアプリで動作確認

まずは適当に日付をセットしたリストを用意し、表示するだけのアプリを作成。

これにソート用のボタンを追加し、ボタンをタップするとソートしてリスト表示を反映させるロジックを追加。

        val btnUp = findViewById<Button>(R.id.btnUp)
        btnUp.setOnClickListener {
            Collections.sort(list, object : Comparator<List>{
                override fun compare(p0: List?, p1: List?): Int {
                    val tmpdiff = p1!!.timestamp - p0!!.timestamp
                    return tmpdiff.toInt()
                }
            })
            adapter.setList(list)
            adapter.notifyDataSetChanged()
        }

上記はアプリに組み込んだロジックを切り取ったものですが間違いに気づきましたでしょうか?

日付はLong型(1970年1月1日00:00を起点とした経過時間)で扱っています。

ソートはCollections.sortのcompareメソッドを使用。

compareは大小比較さえできればよいだろうということでLong型の差分をリターンすればよいと判断。

ただし、それだとInt型で受けれないのでLong型からInt型に型変換を追加。

そして、このロジックでソートした結果が以下となります。

ソートされていません。

ここでログを仕込み、引数1、引数2、引数2と引数1の差分(Long型)、差分の型変換(Int型)の順で出力してみると。

Long型では負なのに、Int型では正になっていることが発覚。

Long型は8バイト、Int型は4バイト。

上記で1例を見るとLong型の値、-2678400000は16進数で0xFFFF FFFF 605A DC00。

これをInt型に変換すると先頭4バイトが落ち、16進数で0x605A DC00。

つまり、1616567296。正の値になりました。

Int型への型変換で4バイトの情報だけを参照した結果、正の領域が負の領域に切り替わってしまったというわけです。

正しいロジックとは

修正するとすれば2パターンでしょうか。

パターン1)Date型に変換し比較する。

        val btnUp = findViewById<Button>(R.id.btnUp)
        btnUp.setOnClickListener {
            Collections.sort(list, object : Comparator<List>{
                override fun compare(p0: List?, p1: List?): Int {
                    val datea = Date(p0!!.timestamp)
                    val dateb = Date(p1!!.timestamp)
                    return dateb.compareTo(datea)
                }
            })
            adapter.setDateList(list)
            adapter.notifyDataSetChanged()
        }

パターン2)Long型の差分結果をもとにInt型で正負に置き換える

        val btnUp = findViewById<Button>(R.id.btnUp)
        btnUp.setOnClickListener {
            Collections.sort(list, object : Comparator<List>{
                override fun compare(p0: List?, p1: List?): Int {
                    val tmpdiff = p1!!.timestamp - p0!!.timestamp
                    var rtnval = 0
                    if (tmpdiff < 0L) {
                        rtnval = -1
                    } else if (tmpdiff == 0L){
                        rtnval = 0
                    } else {
                        rtnval = 1
                    }
                    return rtnval
                }
            })
            adapter.setDateList(list)
            adapter.notifyDataSetChanged()
        }

どちらのロジックにしろ、正しくソートされました。

早速、修正したいと思います。

APIキー定義をプロジェクト外部で管理する

概要

少し前にandroidアプリをリリースしようとしたところ、

Google Play Consoleで警告を出されたので記事にしておきます。

APIキーの定義は慎重に

アプリに外部サービス(YouTubeやGoogle Mapなど)を組み込む場合、必ずAPIキーが必要になると思います。

APIキー(文字列)をどのように管理しますか?

私はstring.xmlに定義していました。

この管理方法に対し警告が出されたわけです。

外部サービスは基本的に従量課金制です。

一定量まで無料、それ以上はサービス使用量に応じて課金されます。

外部サービスにアクセスする際に使用されるAPIキーは言わばユーザーアカウント。

APIキーに紐づくユーザーにサービス使用量が加算されるわけです。

そのAPIキーをstring.xmlに定義するとどうなるか?

簡単なアプリ解析でバレてしまうようです。。。

APIキーが漏洩してしまうとそれを悪用してサービスのタダ乗りをされてしまうよ!

ということで警告を出されたのでした。

うかつでした。。。

APIキーを隠匿する2つの方法

APIキーをどのように管理すればいいのか。

APIキーをプロジェクト外部に定義し、プロジェクト起動時にメモリ(変数)に保持させるのがいいようです。

そして、その方法は2つ。

環境変数に定義する方法と外部ファイルに定義方法です。

外部管理方法その1.環境変数に定義する

(1)まずは環境変数にAPIキーをセットします。

コマンドライン操作を使った環境変数の設定方法はこちらを参照してください。

(2)次にbuild.gradle(app)に環境変数の参照処理を定義します。

    defaultConfig {
        minSdk 24
        targetSdk 31
        versionCode 1
        versionName "1.0"

        manifestPlaceholders = [KEY_VALUE: System.getenv("api_key")]
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

上記は環境変数”api_key”の値を変数”KEY_VALUE”に格納しています。

(3)次にAndroidManifest.xmlの定義です。

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"

        <meta-data
            android:name="API_KEY"
            android:value="${KEY_VALUE}" />

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

プロジェクト内で参照できるようメタデータに格納します。

(4)あとは参照するだけです。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val appInfo = getPackageManager().getApplicationInfo(packageName, PackageManager.GET_META_DATA)
        val apiKey = appInfo.metaData.getString("API_KEY")

        val txtSet = findViewById<TextView>(R.id.txtSet)
        txtSet.setText(apiKey)
    }
}

上記では取得した環境変数をテキストに表示させています。

外部管理方法その2.外部ファイルに定義する

(1)まずは定義ファイルを作成します。

ファイル名に任意ですがファイルの格納場所は以下です。

debugとreleaseの2か所に格納します。ファイルは全く同じもので問題ありません。

赤枠の場所にファイルを格納します。

ファイルの格納パス

C:\~\AndroidStudioProjects\プロジェクト名\app\src\release\res\values\

C:\ ~ \AndroidStudioProjects\ プロジェクト名 \app\src\debug\res\values\

定義ファイル

<resources>
    <string name="api_key" templateMergeStrategy="preserve" translatable="false">APIキー</string>
</resources>

上記の”APIキー”の箇所に実際のAPIキーを定義します。

(2) 次にAndroidManifest.xmlの定義です。

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"

        <meta-data
            android:name="API_KEY"
            android:value="@string/api_key" />

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

プロジェクト内で参照できるようメタデータに格納します。

(3)あとは参照するだけです。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val appInfo = getPackageManager().getApplicationInfo(packageName, PackageManager.GET_META_DATA)
        val apiKey = appInfo.metaData.getString("API_KEY")

        val txtSet = findViewById<TextView>(R.id.txtSet)
        txtSet.setText(apiKey)
    }
}

以上がAPIキーの外部管理方法です。

APIキーのstring.xml定義はくれぐれもご注意ください。

,

androidアプリを初めて作るなら【書籍紹介】

初めてのandroidアプリ開発

androidアプリを開発するには何が必要でしょうか?

アプリ開発用のパソコン、ネット環境、スマートフォン(android)。

以上です。

あとは統合開発環境 android studio をインストールしてアプリを制作。

スマートフォンで動作確認。

最後にGoogle PlayにリリースでOK!

1度でもアプリ開発を経験するとそんなものだと分かるんですが3年前の私は違いました。

アプリ開発用のPCを買った。ネットは使える。

あとandroidアプリを作るには何が必要なんだろうか。。。

そんなときに買った本がこちらです。

統合開発環境 android studio のインストール方法から 基本的な使い方、そして、簡単なアプリ制作の例までし っかりガイドしてくれています。

「1日で基本が身に付く!」 のうたい文句通り、ざっくり理解するのであれば 1日で充分なわかりやすさでした。

androidアプリを開発するには何をすべきか悩んだ人におすすめの1冊です。

改訂版もリリースされているようです。