- 投稿日:2020-08-05T17:06:52+09:00
Android11の変更点ピックアップ
初投稿です。
Android11の登場に向けて、優先的に対応しておいた方が良さそうだと思う変更点をピックアップします。
既存アプリへの対応という観点で見ているので、新機能にはあまり触れていません。
(今後もドキュメントの変更に応じて加筆修正するかもしれません)主な情報引用元
Android11上でアプリを動かす際に適応される項目。
https://developer.android.com/preview/behavior-changes-all?hl=jatargetSdkVersion=30とした時に適応される項目。
https://developer.android.com/preview/behavior-changes-11?hl=jaプライバシー関連。
https://developer.android.com/preview/privacy?hl=ja機能全般。
https://developer.android.com/preview/features?hl=jaちなみに、上記ドキュメントは更新頻度がそれなりに高いため、定期的にチェックした方が良いです。
一ヶ月前に確認した時と比較して、内容が結構変わっていました。(個人的な意見)
…Android11に詰め込みすぎなんじゃなかろうか。
なぜ、今年はこんなに細かい変更が多いのか。Android11上の変更点
ファイル記述子サニタイザー(fdsan)
https://developer.android.com/preview/behavior-changes-all?hl=ja#fdsan
fdsan
が何者かよく理解していないのですが、エラーを検出すると中断するようになります。以前は、警告をログに記録して、続行していました。
上記の変更内容が嫌な予感してます。
アプリ自体をクラッシュさせるということであれば、大きな挙動変更点かと思います。
変なメモリ解放とかやっていなければ特に問題はなさげですが、注意ポイントです。マップ v1 共有ライブラリの削除
マップ ?!?!?!
私は初見時、HashMapか何かか? とか思ってしまいましたが、
これはGoogle Maps Android API v1
のことかと思います。Android10で機能しなくなり、11で完全に消えるとのことです。
GCPのMaps SDK for Android
へ移行してくれと注意喚起されています。対応する
<uses-liblary>
をマニフェストから外さないと、
GooglePlayでユーザーにアプリが表示されない場合があるとのことなので、ちゃんと外しておきましょう。targetSdkVersion=30の変更点
カスタム トーストビューのブロック
デザインをカスタマイズした
toast
を、
バックグラウンドから動かすことができなくなったようです。
代わりにSnackBar
が推奨されています。
※ 画面下から にゅるっ と出てくるやつその他、
text toast
にも変更点があり、
getView()
等の一部のメソッドが機能しなくなっています。
詳しくは以下を確認してください。(丸投げ)
https://developer.android.com/preview/features/toasts?hl=jaAPK 署名スキーム v2 が必要
APKをビルドする際、Scheme v1だけでなくv2も署名してくださいとのことです。
Android Studioで署名付きビルドをする際、チェックボックスで選択可能ですね。Android Studio 2.2(gradle plugin 2.2)以降では、
デフォルトの挙動がv1 + v2
となっているので、
明示的にスキーム設定を無効にしていなければ、特に心配する必要はありません。
https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.SigningConfig.html
https://developers-jp.googleblog.com/2016/11/understanding-apk-packaging-in-android-studio-2-2.htmlメディア インテントのアクションにはシステムのデフォルト カメラが必要
以下のintentに対応できるのが、プリインストールのカメラアプリだけになるそうです。
- android.media.action.VIDEO_CAPTURE
- android.media.action.IMAGE_CAPTURE
- android.media.action.IMAGE_CAPTURE_SECURE
カメラアプリを作っている方には結構な痛手な気がします。
デバイス間のファイル転送
allowBackup
の挙動が変更される模様です。
アプリのセーブデータ等をバックアップ不可にするとのことで、
FCMの仕様上の都合(registration_idの記録封じ)等でこのフラグをfalse
にしている方はそれなりにいらっしゃると思います。ただ、
デバイス間
と書かれているので、
同一のOS同士のファイル転送だけが無効化できなくなったのだと思います。
クラウドへのバックアップは問題なく封じることが出来るはずです。プライバシー関連
直近一ヶ月の間にも続々と内容が更新され、かつ、例年と比較してボリュームが膨大なので、
特に注視しておいた方が良い項目です。対象範囲別ストレージの適用
targetSdkVersion=30の時、
requestLegacyExternalStorage
フラグが無視されます。
つまり、対象範囲別ストレージが強制的に反映されることになります。
Android10以前のデバイスではフラグは有効なのですが、この点が地味に厄介で、
Android10以前と11以降で、処理を分けなければいけない場面が出てくるかと思います。最も影響を受けるのはメディアファイルを扱っている場合ですが、ユースケース毎の対応が用意されています。
https://developer.android.com/training/data-storage/use-cases?hl=ja他のアプリのプライベート ディレクトリへのアクセス
表題の通り、他アプリの外部ストレージディレクトリにはアクセスできなくなります。
権限
アプリが Android 11 をターゲットとしている場合、WRITE_EXTERNAL_STORAGE 権限と WRITE_MEDIA_STORAGE 特権の両方で、追加のアクセス権を提供しなくなりました。
徹底的にファイル書き込みを封じに来てますね。
代替っぽい権限として、後述のMANAGE_EXTERNAL_STORAGE
が追加されていますが。すべてのファイルへのアクセス
MANAGE_EXTERNAL_STORAGE
権限が追加。
Added in API level 30
なので、Android11 OSで利用可能。付与すると、
他のアプリの専用ディレクトリ以外
であれば、
大抵のディレクトリへの読み書きが出来るようになる模様です。ただし、プライバシーポリシーに利用制限が存在するので注意が必要です。
これを守らないとAPKファイルのアップロードが出来ない可能性があります。
https://support.google.com/googleplay/android-developer/answer/9956427?hl=ja機能全般
パッケージの公開設定
targetSdkVersion=30から、他アプリとの連携に
queries
を利用する必要があります。
この項目、そこそこ大きい変更点かと思うのですが、
私の理解が追いついて無く、【そもそもどういった場面で必要になる】等が、ユースケースを読んでもよく分かっていません。
https://developer.android.com/preview/privacy/package-visibility-use-cases?hl=jaCustom URL Scheme(DeepLink)等を利用している場合、
少なからず関係があるとは思うのですが。ただ、問題は他にもあり、
- Android Studio 3.6.1以上
- Android Gradle プラグインの最新リリース(最新ってどの時点のですかね)
この2つがビルド条件に含まれている事の方が大きな影響があります。
特に、Gradle Pluginが一気に新しくなると、
使用できなくなるライブラリがあってもおかしくありません。
今までこまめにアップデートしていれば、問題にならなそうですが。OpenGL ES に ANGLE を使用する
OpenGL ES利用時、ベンダーのNativeドライバかVulkanかを選択出来るようなります。
Android 11 beta2.5時点では、初期ドライバがNativeとの事なので、
今のところは影響はないと思われますが、将来的にこちらがデフォルトになる可能性もありそうです。
- 投稿日:2020-08-05T15:31:41+09:00
【Kotlin】ViewPagerでスワイプする画面を作る
はじめに
以前にもViewPagerで画面をスワイプさせるアプリを作ったが(その時はJavaで)、どういう処理だったのか全然思い出せなかった。。。
参考にさせていただいた記事と似たり寄ったりですが、忘れないよう自分なりに記事にします。
フラグメント
ViewPagerで表示するFragment。
onCreateViewで表示するレイアウトを作成する。
※SampleFragment1のみ載せます。同じようにFragmentクラスを作成すればOK!MainActivity.ktclass SampleFragment1 : Fragment() { val TAG = "BlankFragment" override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { //レイアウトの取得 val layout = inflater.inflate('表示するレイアウトを設定', container, false) //アダプターで作成したBundleからページ番号取得 val bundle : Bundle? = arguments pageNum = bundle?.get("pageNum") as Int //レイアウトに何か処理をする場合はここでやる //レイアウトをリターン return layout } }アダプター
表示するフラグメントを返す。
FragmentStatePagerAdapterを継承して作成する。CustomPagerAdapter.ktclass CustomPagerAdapter(_fm: FragmentManager, private val fragmentList: List<Fragment>) : FragmentStatePagerAdapter(_fm) { // 表示するフラグメントを制御する override fun getItem(position: Int): Fragment { //表示するページ番号を渡す必要があったのでargumentsにbundleを詰め込んでる val bundle : Bundle = Bundle() bundle.putInt("pageNum", position) fragmentList[position].arguments = bundle //fragmentリストから表示するfragmentをリターン return fragmentList[position] } // viewPagerにセットするコンテンツサイズ override fun getCount(): Int { return fragmentList.size } }レイアウト
ViewPagerのみの単純なレイアウト。
共通して表示したいレイアウトがある場合、追加する。viewpager_main.xml<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.viewpager.widget.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent"/> </androidx.constraintlayout.widget.ConstraintLayout>メイン
ViewPagerで表示するFragmentのリストを作成し、アダプターにセットする。そしてviewpagerレイアウトのアダプターにセットする。
MainActivity.ktclass MainActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.viewpager_main) //fragmentリストを作成 val fragmentList : MutableList<Fragment> = arrayListOf() fragmentList.add(SampleFragment1()) fragmentList.add(SampleFragment2()) //adapterのインスタンス生成 val adapter = CustomPagerAdapter(supportFragmentManager, fragmentList) viewPager.adapter = adapter } }以上!!!
あと、ページスワイプ時に処理がしたい場合があったので、それも載っけておきます。
詳細は参考にさせていただいたページを参照ください。MainActivity.ktclass MainActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.viewpager_main) //fragmentリストを作成 val fragmentList : MutableList<Fragment> = arrayListOf() fragmentList.add(SampleFragment1()) fragmentList.add(SampleFragment2()) //adapterのインスタンス生成 val adapter = CustomPagerAdapter(supportFragmentManager, fragmentList) viewPager.adapter = adapter /* 下記が追加部分 */ viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { override fun onPageSelected(position: Int) {} override fun onPageScrollStateChanged(state: Int) {} override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} }) } }参考
[kotlin] ViewPagerでフラグメントを切り替える
[kotlin] ViewPagerをボタンで切り替える
ViewPagerの基本最後に
基本は理解できた!
ただ生成されているフラグメントの更新ができず(フラグメントは3枚まで保持している)、別の方法にシフトしました。
ViewPager2なら簡単にできるのかな~調べてみます。また、間違っている部分やアドバイスありましたらお願いします。
- 投稿日:2020-08-05T13:34:42+09:00
scrcpy で Android を PC から操作
概要
PC から Android 端末を操作するのに scrcpy がいいと教えてもらったので、ざっくり触った感じとその備忘録。
前提条件
- macOS
- Homebrew 導入済み
- adb 導入済み
- Android OS 5.0 以上
scrcpy とは
This application provides display and control of Android devices connected on USB (or over TCP/IP). It does not require any root access. It works on GNU/Linux, Windows and macOS.
このアプリは USB(または TCP/IP)で接続された Android 端末の表示と制御を提供する。Root 化不要。GNU/Linux、Windows、macOS で動作する。
導入
brew install scrcpy
Homebrew と adb が導入済みであればこれだけ。インストールが完了するまで少し時間がかかる。
起動
端末を PC に USB で接続し
scrcpy
コマンドで起動。端末側では予め開発者モードを有効にし、USB デバッグを許可しておくこと。ショートカット
アクション ショートカット 全画面に切り替え ⌘ + f 画面を左に回転 ⌘ + ← 画面を右に回転 ⌘ + → ホームボタン ⌃ + h 戻るボタン ⌘ + b アプリ切り替え ⌘ + s メニュー表示 ⌃ + m 実機の画面 OFF ⌘ + o 取り敢えずよく使いそうなものだけ抜粋。
その他機能
- 画面のキャプチャ(静止画/動画)
- ワイヤレス接続
- apk インストール
- ファイル共有
他にもまだいっぱいある。
所感
- 導入/設定が楽
- 機能が豊富
- 動作が軽い
- ラグが少ない
- 画質がいい
参考
- 投稿日:2020-08-05T04:30:52+09:00
Androidアプリ開発探求記(その8)
概要
前回は、buildSrc を導入しつつ、build.gradle を build.gradle.kts に書き換えてみました。
今回は、buildSrc 側で依存関係を一元管理する方法を模索してみようと思います。
前回の懸念点
前回は、以下のような懸念点を上げました。
◆ extension による可読性の低下
extension を使うと可読性が下がるように思えます。
plugins { id("com.android.application") // TODO: この extension って逆にわかりづらい気がする。 kotlin("android") id("kotlin-android-extensions") }plugins { id("com.android.application") // TODO: こっちのほうが分かりやすいように思える。 id("org.jetbrains.kotlin.android") id("kotlin-android-extensions") }◆ extension のメリット消失
extension のメリットとしては、コードの短縮や typo の発生確率の減少などがありますが、deps を導入して IDE のコード補完が利用可能になると、extension のメリットがほぼなくなるのではないでしょうか?
buildSrc/src/main/java/Dependencies.kt(前回コメントしておいたdeps導入検討コメント)object versions { const val kotlin = "1.3.72" } // TODO: 要検討 //object deps { //}ネット上を物色してみる
Dependencies.kt でググって、検索されたものを無条件で先頭から物色し、目についた点をメモしてみます。1
◆ kotlinpoet
- object をネスト
object versions { object kotlin { const val plugin = "1.4-M2" const val libs = "1.3.72" } const val spotless = "3.27.0" const val ktlint = "0.36.0" }◆ passiondroid
- minSdk 等も一元管理している。
object AndroidSdk { const val min = 15 const val compile = 28 const val target = compile }◆ chrisbanes/tivi
- Libs のネスト内部に version を含めている。
object Kotlin { private const val version = "1.4.0-rc" const val stdlib = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$version" const val gradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$version" const val extensions = "org.jetbrains.kotlin:kotlin-android-extensions:$version" }◆ adrielcafe/kaptain
- version を internal にしている
internal object Version { const val GRADLE_ANDROID = "3.6.1" const val GRADLE_KTLINT = "9.2.1" }◆ nrohmen
- BuildPlugins とか TestLibs とか分けている。
◆ BuiltinPluginIdExtensions.kt
プラグインに含まれている。org.gradle.kotlin.dsl.BuiltinPluginIdExtensions.kt
// 下記のように定義されていて、 inline val org.gradle.plugin.use.PluginDependenciesSpec.`java-gradle-plugin`: org.gradle.plugin.use.PluginDependencySpec get() = id("org.gradle.java-gradle-plugin") // このように id を用いずに、 id("org.gradle.java-gradle-plugin") // 下記のように書ける。しかし、IDEの補完が効かないのが致命的に使いづらい。 `java-gradle-plugin`一人ブレスト
- 今回は、依存関係以外(minSdkVersion等)は扱わず、依存関係だけ扱う。
- classpath, plugin の id, dependencyNotation に分けて考える。
- object として、classpath は Classpath, plugin の id は Plugin, dependencyNotation は Deps にまとめてみよう。
- 今回はIDEの補完が最も重要になるので、現状で補完の効かないバッククウォーテーション方式でハイフンを入れたりするのは却下。
- object のネストはツリー構造でさも簡潔に管理できそうに見えるが、同種のまとまりがツリー構造に収まる保証はどこにもない。つまり、保守が破綻する可能性がある。
- object のネストは、かならずドットでネストするのがよいとは限らないし、セミコロンでネストするのがよいとも限らない。ライブラリの追加や統廃合でネスト箇所が変わる可能性もある。また、ドットとセミコロンで必ずネストさせるといったルールでもあればよいが、少なくともそのようなケースをネット上で見方ことは無く、個人の裁量で適宜名称の簡略化をしている。そのため、保守にリスクが付きまとう。
- object のネストが深いと、ネストのたびに補完が必要になってしまうので無駄な手間が増える。
- 命名に関して、物色してきたソースでは、ほぼ全て、適度に省略した名称を自作していた。これは作った本人にとってはわかりやすいかもしれない。しかし、初見の保守者にとって知る由もなし。例えば、下記のように定義している場合、Libs はアプリの設計書に書かれているべき内容だし保守者は知っておくべきものなので問題無いが、その後に Kotlin や stdlib という文字列が続くかどうかなど、エスパーでもない限りわかるはずもない。しかも、stdlib に jdk8 が付与されていることなどエスパーでも見落とすかもしれない。
object Libs { object Kotlin { private const val version = "1.4.0-rc" const val stdlib = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$version" const val gradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$version" const val extensions = "org.jetbrains.kotlin:kotlin-android-extensions:$version" } }
- 結局、初見の(もしくはプロジェクトで独自に作成された命名が脳みそからスワップアウトした)保守者がどのように行動するかというと、org.jetbrains.kotlin:kotlin-stdlib という文字列を IDE 上で検索をかけ、Dependencies.kt に飛び、そこから、Libs.Kotlin.stdlib という参照を発見するわけだ。作業コストとしては、org.jetbrains.kotlin:kotlin-stdlib-jdk8 を直接書いたほうが圧倒的に早い。つまり、無駄な工数を強いているということになる。しかし、version が一元管理されているという点は大きなメリット。ただ、バージョンがツリー構造に合致する保証などどこにもない。つまり、Libs.Hoge 配下と Libs.Fuga 配下で version がクロスオーバーする可能性があるということ。なので、ツリー構造による保守は破綻する可能性がある。しかし、クロスオーバーしない場合に限りツリー構造上に配置するという折衷案はそれなりに機能しそうな気がする。特に、バージョンを単体で外に配置した場合、名前空間や命名にかなりの難があるので。でも、今回はバージョン情報を含めるような階層構造はなさそう。
- 前回からの懸念であった extension に関しても、plugin 側が extension をこっそり追加したところで、保守者は知らないので、まずは dependencyNotation の文字列で検索をかけたりググったりして extension を知る的な本末転倒なことになるので、使わないほうがいいと思われる。
- しかしながら、dependencyNotation の文字列をそのまま使えばいいかというと、typo のリスクがあるし、補完も効かないのでダメ。
- だったら、補完により typo のリスクを無くし、初見でも使え、version の一元管理もできるという方法があればいいんじゃね?
- 変数名を、dependencyNotation の文字列から単純かつ機械的なルールで命名するようにすれば初見でも変数名が分かるし、補完で typo リスクがなくなるし、変数に version も含まれていればバージョンの一元管理のメリットも得られるんじゃね?
実装
ということで、現行のコードをベースとして、それっぽく実装してみることにします。
diff◆ build.gradle.kt
☆ 変更前
buildscript { dependencies { classpath("com.android.tools.build:gradle:4.0.1") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}") } }☆ 変更後
buildscript { dependencies { classpath(Classpaths.com_android_tools_build__gradle) classpath(Classpaths.org_jetbrains_kotlin__kotlin_gradle_plugin) } }◆ app/build.gradle.kt
☆ 変更前
plugins { id("com.android.application") // TODO: この extension って逆にわかりづらい気がする。 kotlin("android") id("kotlin-android-extensions") } dependencies { coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.0.10") implementation("androidx.appcompat:appcompat:1.1.0") implementation("androidx.constraintlayout:constraintlayout:1.1.3") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}") testImplementation("junit:junit:4.12") androidTestImplementation("androidx.test.espresso:espresso-core:3.2.0") androidTestImplementation("androidx.test.ext:junit:1.1.1") }☆ 変更後
plugins { id(Plugins.com_android_application) id(Plugins.org_jetbrains_kotlin_android) id(Plugins.kotlin_android_extensions) } dependencies { coreLibraryDesugaring(Deps.com_android_tools__desugar_jdk_libs) implementation(Deps.androidx_appcompat__appcompat) implementation(Deps.androidx_constraintlayout__constraintlayout) implementation(Deps.org_jetbrains_kotlin__kotlin_stdlib_jdk8) testImplementation(Deps.junit__junit) androidTestImplementation(Deps.androidx_test_espresso__espresso_core) androidTestImplementation(Deps.androidx_test_ext__junit)◆ Dependencies.kt
/** * 複数個所で利用されるバージョン情報 */ private object Versions { const val kotlin = "1.3.72" } /** * DependencyHandler.add(CLASSPATH_CONFIGURATION, dependencyNotation) の dependencyNotation として * 扱われる文字列です。 * * 変数の命名手順: * - <groupId>:<artifactId>:<version> のうち、:<version> を削除する。 * - colon を 2連続の underscore に変換し、それ以外の編巣名に利用不可能な文字を underscore に変換する。 * * 利用例: * classpath(Classpaths.com_android_tools_build__gradle) */ object Classpaths { const val com_android_tools_build__gradle = "com.android.tools.build:gradle:4.0.1" const val org_jetbrains_kotlin__kotlin_gradle_plugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}" } /** * PluginDependenciesSpecScope.id(id) の id として扱われる文字列です。 * * 変数の命名手順: * - colon を 2連続の underscore に変換し、それ以外の変数名に利用不可能な文字を underscore に変換する。 * * 利用例: * id(Plugins.com_android_application) */ object Plugins { const val com_android_application = "com.android.application" const val kotlin_android_extensions = "kotlin-android-extensions" const val org_jetbrains_kotlin_android = "org.jetbrains.kotlin.android" } /** * dependencyNotation として扱われる文字列です。 * * 変数の命名手順: * - <groupId>:<artifactId>:<version> のうち、:<version> を削除する。 * - colon を 2連続の underscore に変換し、それ以外の変数名に利用不可能な文字を underscore に変換する。 * * 利用例: * coreLibraryDesugaring(Deps.com_android_tools__desugar_jdk_libs) * implementation(Deps.androidx_appcompat__appcompat) * testImplementation(Deps.junit__junit) * androidTestImplementation(Deps.androidx_test_espresso__espresso_core) */ object Deps { const val androidx_appcompat__appcompat = "androidx.appcompat:appcompat:1.1.0" const val androidx_constraintlayout__constraintlayout = "androidx.constraintlayout:constraintlayout:1.1.3" const val androidx_test_espresso__espresso_core = "androidx.test.espresso:espresso-core:3.2.0" const val androidx_test_ext__junit = "androidx.test.ext:junit:1.1.1" const val com_android_tools__desugar_jdk_libs = "com.android.tools:desugar_jdk_libs:1.0.10" const val junit__junit = "junit:junit:4.12" const val org_jetbrains_kotlin__kotlin_stdlib_jdk8 = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${Versions.kotlin}" }まとめ
今回は、buildSrc の Dependencies.kt にて、依存関係の一元管理をしてみました。
次回は、ライブラリのバージョン管理に関して模索してみようと思います。
思った以上に多くの老舗のプロジェクトは kts 化されておらず、buildSrc も使われてないですね、、、。老舗だからこそむやみやたらに kts 化とかしないんだろうな、、、。 ↩