Android Gradle Pluginを4.0.0から4.0.1へアップデート ( Android11 の Package visibility )

Android Gradle Pluginのバージョン4.0.0から4.0.1へアップデート。何が変わったのでしょうか? https://developer.android.com/studio/releases/gradle-plugin#4-0-0

This minor update supports compatibility with new default settings and features for package visibility in Android 11.

Package visibilityの管理ができるようになりました!

なんだそれは?調べてみると、他のアプリとの連携を細かく制御できるようです。 developer.android.com

アプリのtargetがAndroid 11 (API Level 30)以上で、他のアプリと連携したい場合、manifestファイルに <queries> という要素を書かないといけない場合ができたようです。


例えば、PackageManagerで情報を取得するケースを試してみます。

targetSdkVersion 30 のアプリA ( info.bati11.example.a )とアプリB ( info.bati11.example.b ) を用意する。アプリAからPackageManagerを使ってアプリBの情報を取得してみることにする。

package info.bati11.example.a

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById<Button>(R.id.button).setOnClickListener {
            val packageName = "info.bati11.example.b"
            Log.i(
                "test",
                packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES).toString()
            )
        }
    }
}

API Level29のエミュレータで試すと問題なし。

2020-09-14 23:50:27.850 5962-5962/info.bati11.example.a I/test: PackageInfo{19d4329 info.bati11.example.b}

API Level30のエミュレータで試すと以下のExceptionが発生!

2020-09-14 23:59:17.151 5734-5734/info.bati11.example.a E/AndroidRuntime: FATAL EXCEPTION: main
    Process: info.bati11.example.a, PID: 5734
    java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 
     Caused by: android.content.pm.PackageManager$NameNotFoundException: info.bati11.example.b
        at android.app.ApplicationPackageManager.getPackageInfoAsUser(ApplicationPackageManager.java:206)
        at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:178)
        at info.bati11.example.a.MainActivity$onCreate$1.onClick(MainActivity.kt:20)
        at android.view.View.performClick(View.java:7448)
        at android.view.View.performClickInternal(View.java:7425)
        at android.view.View.access$3600(View.java:810)
        at android.view.View$PerformClick.run(View.java:28305)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 

アプリAのAndroidManifest.xmlに <queries> を記述してみる。

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="info.bati11.example.a">
    <queries>
        <package android:name="info.bati11.example.b" />
    </queries>

    ...

これでAPI Level30のエミュレータでもOK!

2020-09-15 00:13:56.601 2388-2388/info.bati11.example.a I/test: PackageInfo{7ea0aab info.bati11.example.b}


ちなみに、アプリAの targetSdkVersion30 から 29 に変えれば例外は発生しませんでした。

また、 startActivity でアプリAからアプリBを起動する場合に <query> は必要なかったです。

公式ドキュメントにも記載されてますね。

https://developer.android.com/training/basics/intents/package-visibility#automatic

Note: If your app targets Android 10 (API level 29) or lower, all apps are visible to your app automatically.

...

In addition, you can start another app's activity using either an implicit or explicit intent, regardless of whether that other app is visible to your app.

シェルソートの動きを見てみる

Aizu Online Judge の Shell Sort の問題 をやってて、なんか分かった気がしないなぁと思ってました。

Aizu Online Judgeの一部の問題は、本だと解説されてたりします。

計算量もどうしたらいいんだろと調べたところ、単純ではないようです。

qiita.com

まずは雰囲気だけでもサクッと...と思ってJavaScriptで挿入ソートとシェルソートを並べてみました。うむ、確かにシェルソートの方が速くソートできてる。

See the Pen Insertion Sort & Shell Sort by kariyayo (@kariyayo) on CodePen.

記念日を忘れないためのAndroidアプリをつくった

以前、記念日を忘れないためのWebアプリを作りました。

bati11blog.hatenablog.com

しかし、致命的な弱点がありました。それはオンラインじゃないと動かないということです。つまり、飛行機で上空にいるタイミングで「もう少しで結婚記念日だわね〜」みたいな会話になった時に、何年目だっけ!?というのをすぐに確認できません!!!

というわけでAndroidアプリとしてオフラインで動くように作りました。あと通知でリマインドする機能も追加。これでばっちりです。

github.com

登録画面 登録画面 通知


開発メモ

という理由に加えて、2020年になってから久しぶりに仕事でもガッツリとAndroidアプリ開発に携わることにもなったので、練習がてら作り初めてみたのです。

久しぶりのAndroidアプリ開発は、以前と比べてとても快適になっていました。これまでにもいろいろなアーキテクチャコンポーネントがありましたが、2020年時点では Jetpack というラベル付けで提供されるものを使うと良さそうです。

快適とはいえ学ぶことは多かったので、どんなことを調べたっけなというのをメモ。

Kotlin

プログラミング言語JavaではなくKotlinになってる。Kotlin Bootcamp Courseというcodelabをやりつつ公式ドキュメントを参照するというのをやった。Kotlin イン・アクション という本も読んだけどKotlinのバージョンが古いので、こちらも公式ドキュメントを参照しながら。特にCoroutine周りは公式ドキュメントが読みながらコード書いてみるのが良い。

Jetpack

Kotlinを使ったAndroidアプリ開発のcodelabをやった。Jetpackコンポーネントも使いつつなので良い。

Android KTXを使うといろいろ便利。以下をよく使う。

  • Fragment KTXby viewModels
  • Lifecycle KTXviewLifeCycleOwner

codelabが終わったらすぐに以下の記事を読んでおけばよかった。

Dagger

DIコンテナ。Android DevelopersにもDaggerのページがあるし、codelabも用意されているのでデファクトスタンダード感。これらのドキュメントのViewModelはJetpackのViewModelではないのでちょっと混乱した...

Dagger単体ならまだしもAndroidと合わせて使うと難しい。この本読みながらいろいろ試行錯誤した。

DaggerのAndroid拡張はドキュメントや本を読んでもスッと入ってこない。SubComponentを良い感じに取り扱う方法は結局よく分からず。

テスト

テストはユニットテストをちょっとしか書けていない。ViewModelとLiveDataを使ったテストは難しい。。いろいろ読み漁った。

しかもJUnit5を普通に使うだけだとうまくいかないのを解決できずJUnit4を使ってる。Androidコンポーネントに依存しないテストはJUnit5で書くなどして、バージョン4と5が混在しても大丈夫。あんまり覚えてないけど、この記事ぱらぱらと読んだ気がする...。

モックライブラリのMockKは使いやすいと思った。

一覧画面

codelabには出てこないけどやりたかったこと。

スクロールするとUIにないデータを取りにいくという処理はpagingを使う。

スクロールに応じてToolbarやFloating Action Buttonを動かすにはCoodinateLayoutを使う。

UIの細かいところ

角丸にするのがよく分からなかったり...

リップル対応したり...

アイコン

こちらを使って W の1文字で...

ライセンス表記

使用しているライブラリのライセンス表示はこちらを使って。

開発中のデバッグ

このスライドがとても参考になった。

おしまい

Androidは自分のためのアプリをつくって日常で使えて良いですね!