Android アプリの Drawable リソースのエイリアスの作成について
当たり前の内容だけど日本語ドキュメントが間違っててバグを埋め込んでしまった (リリースはしてない) ので共有。
Drawable リソースのエイリアス作成
res/values/drawables.xml ファイルみたいなファイルを作って、そこに <drawable name="alias_name">@drawable/target_drawable</drawable>
みたいなタグを書けばエイリアスを作れる。
To create an alias to an existing drawable, use the
element. For example: <?xml version="1.0" encoding="utf-8"?> <resources> <drawable name="icon">@drawable/icon_ca</drawable> </resources>If you save this file as drawables.xml (in an alternative resource directory, such as res/values-en-rCA/), it is compiled into a resource that you can reference as R.drawable.icon, but is actually an alias for the R.drawable.icon_ca resource (which is saved in res/drawable/).
App resources overview | Android Developers
日本語ドキュメントが間違ってる件
日本語ドキュメントだと、
既存のドローアブルのエイリアスを作成するには、
<bitmap>
要素を使用します。次に例を示します。
って書かれてる。 が、実際にこれをやって (Bitmap Drawable 以外の?) Drawable リソースのエイリアスを作成しようとすると実行時にエラーが発生する。 (API level 16、23、25 のエミュレータで確認。)
ドキュメントの誤りについては https://issuetracker.google.com/issues/78862550 に報告した。
`bitmap` 要素を使ったときのエラー例
<?xml version="1.0" encoding="utf-8"?> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" android:src="@drawable/simple_drawable" />
上のような XML ファイルを用意して、Drawable として読み込もうとすると以下のようなエラー。
java.lang.RuntimeException: Unable to start activity ComponentInfo{info.vividcode.sample.simple/info.vividcode.sample.simple.MainActivity}: android.view.InflateException: Binary XML file line #31: Binary XML file line #31: Error inflating class ImageView at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) (中略) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused by: android.view.InflateException: Binary XML file line #31: Binary XML file line #31: Error inflating class ImageView at android.view.LayoutInflater.inflate(LayoutInflater.java:539) at android.view.LayoutInflater.inflate(LayoutInflater.java:423) (中略) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused by: android.view.InflateException: Binary XML file line #31: Error inflating class ImageView at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:782) at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704) (中略) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused by: android.content.res.Resources$NotFoundException: File res/drawable/simple_drawable_3.xml from drawable resource ID #0x7f02005f at android.content.res.Resources.loadDrawableForCookie(Resources.java:2640) at android.content.res.Resources.loadDrawable(Resources.java:2540) at android.content.res.TypedArray.getDrawable(TypedArray.java:870) at android.widget.ImageView.<init>(ImageView.java:152) (中略) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #2: <bitmap> requires a valid 'src' attribute (以下略)
Android における View にまつわる状態とライフサイクルについての考慮のメモ
Activity が再生成される際の、View の onSaveInstanceState
メソッドと onRestoreInstanceState
メソッドについて個人用にまとめておく。
Activity の再生成
- Activity が再生成 (re-creation) される際には、以前の状態が
Activity#onCreate
とActivity#onRestoreInstanceState
に渡される。 Activity#onRestoreInstanceState
が呼ばれるのは、Activity#onStart
が呼ばれた後。- 『This method is called after
onStart()
when the activity is being re-initialized from a previously saved state, given here in savedInstanceState.』 (Activity#onRestoreInstanceState
より)
- 『This method is called after
Activity#onRestoreInstanceState
が呼ばれるのは、Activity 再生成後の最初のActivity#onStart
が呼ばれた後の 1 回だけ。 (Android 8.0 の端末で実際の挙動を確認。)
View 周り
- View にも
View#onRestoreInstanceState
メソッドやView#onSaveInstanceState
メソッドがあるが、これらはどこから呼ばれるのか? - →
Activity#onRestoreInstanceState
メソッドやActivity#onSaveInstanceState
メソッドのデフォルト実装による。- 『The default implementation of this method performs a restore of any view state that had previously been frozen by
onSaveInstanceState(Bundle)
.』
- 『The default implementation of this method performs a restore of any view state that had previously been frozen by
Window
あたりがルートビューのView#saveHierarchyState
やView#restoreHierarchyState
などを呼んで、そっからView#dispatchSaveInstanceState
やView#dispatchRestoreInstanceState
やらが呼ばれて、最後にView#onSaveInstanceState
やView#onRestoreInsntaceState
やらにたどり着く。android:saveEnabled
の考慮はView#dispatchSaveInstanceState
で行われる。- 子 View の
View#dispatchSaveInstanceState
を呼ぶのはViewGroup#dispatchSaveInstanceState
が行っている。 - ここら辺は実際のコードをみて確認した。 API Level 27。
View にまつわる状態とライフサイクルについての考慮 (?)
- View Model やら Presenter みたいな、いわゆる Presentation Model 的なものを作っていると、Activity の再生成時の View の状態 (
View#onRestoreInstanceState
で復元されるやつ) と、Presentation Model 側の状態の同期をどうとるかで悩むことになりがち。 - 道はいくつもある
- カスタム View を作って、その内部に状態を持たせる。 状態の保存はカスタム View の
View#onSaveInstanceState
に任せる。 - View の状態管理は完全にアプリケーション側の責務ということにして、Android フレームワークの力には頼らない。 View には
android:saveEnabled="false"
を設定する。 - Activity 再生成時の状態復元は View だけでやって、Presentation Model 側では View 側の状態復元をフックにして状態を変える。
- 具体的に言うと、例えば
EditText
の入力内容に対応して Presentation Model 側で状態を保持する設計を考える。 Activity 再生成時の初期状態ではEditText
も Presentation Model も入力内容がない場合の状態になってて、onRestoreInstanceState
でEditText
の状態が復元される際にTextWatcher
でイベントを捕捉して Presentation Model 側の状態も変える、みたいな。
- 具体的に言うと、例えば
- カスタム View を作って、その内部に状態を持たせる。 状態の保存はカスタム View の
- Android フレームワークの挙動をちゃんと理解しておけばなんとでもなるのだけど、ちゃんとりかいしておかないと変なところでハマる。
- 個人的には Activity と View を疎に保ちたいのだけど、Android フレームワーク的には結構べったりくっついてるので辛い。
読んだ : さわって学ぶクラウドインフラ Amazon Web Services 基礎からのネットワーク & サーバー構築
Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版
- 作者: 玉川憲,片山暁雄,今井雄太,大澤文孝
- 出版社/メーカー: 日経BP社
- 発売日: 2017/04/13
- メディア: 単行本
- この商品を含むブログを見る
インフラ周りちゃんと学ぶぞー! と言いつつ時間ばかり経ってしまっているので、いい加減ちゃんとやろうと思って本書を読んだ。
易しめの内容でわかりやすく書かれているので、「インフラ周りをちゃんと学びたいと思いつつも苦手意識を持っている」 という人 *1 や、「SSH コマンドなどもろくに使ったことがないけどインフラ周りを学んでみたい」 という人におすすめの書籍だった。
感想
- 「AWS で WordPress を動かす」 ために必要な内容を順番に説明していくという、わかりやすい内容だった。
- AWS だけでなくネットワーク周り (TCP / IP とか) についても初心者向けの内容になっており、インフラ周りについてほぼ何も知らない状態でもとっつきやすい。
- AWS についても リージョンやアベイラビリティゾーン (AZ) といったことから説明がある。
- そして、Amazon Virtual Private Cloud (VPC) や Amazon Elastic Compute Cloud (EC2) といった、AWS 上で web サービスを運用する場合によく使われるであろうサービスについての知識が得られる。
- わかっている人にとっては簡単すぎる内容だと思うが、初心者にとってはこれぐらい簡単な方がとっつきやすくてありがたい。
- お金のことを考えるとなかなか個人で AWS を触ろうという気にならなかったりするのだけど、本書の内容は AWS のアカウント作成後に付与される無料枠の中でできることばかりなので、気軽に試していけるのも良い。
本書の内容
9 章構成になっている。
- 1 章 : ネットワークの基礎的なところから AWS の基礎的な部分まで解説される。 また、本書で構成するシステムの全体像も描かれる。
- 一般的な web アプリケーションに必要なアプリケーションサーバーと DB サーバーが、それぞれパブリックサブネットとプライベートサブネットに配置されているようなネットワーク。
- 2 章 : ネットワークを作る。
- VPC を用意し、パブリックサブネットを作る。
- 3 章 : サーバー構築。 EC2 インスタンス起動。
- 4 章 : Web サーバーソフトをインストール。 Apache。
- 5 章 : HTTP の動きの確認。
- 6 章 : プライベートサブネットを作る。
- 7 章 : NAT を構築。
- 8 章 : DB を用いたブログシステムの作成。
- DB サーバーを用意して、アプリケーションサーバーに WordPress をインストール。
- 9 章 : TCP/IP による通信の説明。
上記のような感じで、手順としても SSH コマンドなどについても丁寧に説明しているので、本当に初心者にもわかりやすいと思う。
さらに学ぶために
上で何度か言ったように、本書は初心者にもわかりやすいような内容になっている。 逆に、自動化などの応用的な話はあまりないので、さらに学びたい人は本書の内容を Terraform で書きながら読み進める、というようなことをしてもいいかもしれない。
私は本書を読み終わった後に Terraform の Getting Started を読んでいく、という感じで学んでいった。
*1:私です
Java (JVM 言語) におけるコードカバレッジの計測方法
Java と Kotlin で書かれたアプリケーションのコードテストのカバレッジを取りたいなーと思って、コードカバレッジ計測ツールについて調べてみてる。
JVM 言語におけるコードカバレッジ計測の方法
JaCoCo のドキュメントにわかりやすくまとまっている。
Coverage information has to be collected at runtime. For this purpose JaCoCo creates instrumented versions of the original class definitions. The instrumentation process happens on-the-fly during class loading using so called Java agents.
There are several different approaches to collect coverage information. For each approach different implementation techniques are known. The following diagram gives an overview with the techniques used by JaCoCo highlighted:
JaCoCo - Implementation Design
カバレッジ情報は実行時に収集されるものなので、情報収集のための仕組みが必要。
大きく分けると Runtime Profiling と Instrumentation の 2 つの仕組みがある。 前者は JVM の仕組み (JVMTI や JVMPI) を使うもの。 後者は実行対象のプログラムの方に収集用の仕組みを搭載する (これを instrumentation というらしい?) もの。
Runtime Profiling
JVM TI は Java Virtual Machine Tool Interface の略。 Java SE 5 で導入された。
JVM TI は JVM 上で動くアプリケーションの状態を検査したり、実行を制御したりするためのプログラミングインターフェイスらしい。
JVMPI は JVM TI よりも古くからある同じようなインターフェイスらしい。 Java SE 5 で (JVM TI ができたことで) 非推奨になり、Java SE 6 で廃止された模様。
これらを使ったコードカバレッジは、アプリケーション側に何も手を入れなくて良いことが利点だと思われる。 が、詳細はわからない。
Instrumentation
上でも説明したように、計測対象のアプリケーションコードに情報収集のための仕組みを搭載する方法が、この Instrumentation である。 Runtime Profiling と異なり、Android アプリの実行環境のような非 JVM 環境でも使用できるという利点がありそう *1。
上の図を見るとわかるように、様々な方法がある。
JaCoCo は、Java Agent を用いてオンザフライでバイトコードを変更する方式である。 *2
Java Agent について
Java Agent については java.lang.instrument
パッケージの Javadoc に書かれている。
-javaagent:
というコマンドラインオプションで指定して使用できるものである。 次のページも参考になる。
エージェントの背景にある基本概念は、「JVM がクラスをロードする場合、エージェントはそのクラスのバイトコードを修正できる」 という考え方です。
様々なコードカバレッジツール
JVM 言語用の様々なコードカバレッジツールについて、Clover のブログで比較紹介してくれている。 (最終更新が 2017 年春なので情報はちょっと古いかもしれない。)
有名どころとしては JaCoCo、OpenClover (Atlassian Clover がオープンソース化されたもの)、JCov といったところだと思う。
Instrumentation 方式に着目すると、JaCoCo と JCov はオフラインおよびオンザフライのバイトコード instrumentation に対応しており、Clover はソースファイル instrumentation に対応している。 なので JaCoCo や JCov は (JVM 言語なら何でも対応できるので) Kotlin にも対応するが、Clover は (対応言語に入っていない) Kotlin には対応しない。
Clover と JaCoCo を軽く使ってみたところ、設定の簡単さはどちらも同じ。 出力される HTML を見ると JaCoCo は単純な内容で、Clover の方はプロジェクトリスクの高いものを表示したり、視覚的だったりと、結果表示については高機能さを感じた。
今回は Kotlin でのカバレッジも取りたいので、(対応していない) Clover は選外で、JaCoCo か JCov のどっちかを使うことになりそう。
Ktor の自動再読み込み (Automatic Reloading) 機能
JetBrains 製の Kotlin 用 web アプリケーションフレームワークの Ktor の自動再読み込み機能について。 開発時にサーバー全体を再起動しなくても、ソースコードを変更してビルドした後のクラスファイルの再読み込みをしてくれる機能である。
- ドキュメント : Autoreload - Servers - Ktor
設定方法
上記ドキュメントにあるように、下記のように設定ファイルの ktor.deployment.watch
に監視対象のモジュールを指定してやればよい。
ktor { deployment { port = 8080 watch = [ module1, module2 ] } … }
モジュールの値としてどんな文字列を指定してやればよいのか?
ドキュメントには、以下のように書かれている。
For now watch keys are just strings that are matched with contains against classpath entries of the loaded application, such as a jar name or a project directory name. These classes are then loaded with special ClassLoader that is recycled when change is detected.
Autoreload - Servers - Ktor
すなわち、JAR 名やプロジェクトのディレクトリ名などの、クラスパスのエントリに含まれるような文字列を書けばよい。
例えば Gradle を使っているプロジェクトを IntelliJ IDEA 上で実行する場合は、クラスパスに 「file:/C:/Users/nobuoka/Documents/projects/ktor-sample/app/out/production/classes/」 というようなファイル URL が含まれる。 この文字列の一部分にマッチするような文字列を指定してやる。 IntelliJ IDEA 用での実行だけを考えるなら、ぶっちゃけ 「/out/」 あたりを指定しておいてやればいい気がする。 それだけですべてのサブプロジェクトに自動再読み込みが適用される。
自動再読み込みの仕組みを追う
ドキュメントだけを読んでもあんまり理解できないので内部を追いかけたい人用。 (自分もドキュメントだけではわからなくて Ktor のソースコードを見ながらデバッグした。)
ソースコードを見ると、自動再読み込みのためのクラスローダの生成が ApplicationEngineEnvironmentReloading
クラスで行われていた。 メソッドとしては createClassLoader
メソッドである。 IntelliJ IDEA などの IDE を使っているならデバッグしやすいので、このクラスを IDE 上で開いてブレークポイントをはってデバッグ実行すると処理の流れを追える。
『No ktor.deployment.watch patterns specified, automatic reload is not active』 とか 『No ktor.deployment.watch patterns match classpath entries, automatic reload is not active』 といったメッセージもこのクラスで出力されている。
参考になれば。