20191112のAndroidに関する記事は8件です。

Android フォアグラウンドからバックグラウンド、その逆のイベントとる

スクリーンショット 2019-11-12 23.09.54.png

ハマりすぎたのでメモ2
参照とかnugetからパッケージ落としてくるとこは前記事を参照

仕組みはarch.lifecycle extentionsって言うのでなんでもライフサイクルが管理できる様になる
何も入れないとActivityはonPauseとonResumeとかあるのでこれはいらないが、
これをアプリケーション自体に適用するとアプリケーションがフォアグラウンドになった時とかバックグラウンドになった時のイベントをハンドリングできる様になる

画像を見れば以下のことが分かる
・アプリケーション自体のクラスの実装の仕方
・アプリがバックグラウンド、フォアグラウンドに行った時のイベントハンドリングのメソッド
・nugetから落とすパッケージ
・参照に入れるもの(参照を追加で検索>Extentionでヒットするやつを一つ追加)

ちなみにアプリのバージョンは8.1
arch.lifecycle extentionsとかはlatestじゃなくて1.1.1(これは前の記事参照

このProcessLifeCycleObserverを使うサンプルはJavaのやつとかはインターネットにたくさん落ちてるがC#のやつはまじで落ちてない。
のでここに書いておく

追記

ProcessLifecycleObserver.Get()..
のくだりはクリーンと言うかプロジェクトを別のPCに移してvs立ち上げ直したらエラーが取れた

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

後輩養成のすすめ。~あの辛い徹夜ラッシュをもうしないために~

1.はじめに

1.1 経緯

年代によって大きく技術力は変わります。僕は学生ですので会社では年代は関係ないのかもしれませんが、プログラミングを必要としない世界(例えば非IT企業、研究室、中高学校)では大きく変わってしまうのです。もちろん一定数そうゆう人は入ってきます。学校ではパソコン関連の同好会みたいな部署が学園祭のアプリや動画を作っていました。僕の学校で問題だったのは人が足りないということ、そして学年によって差が多いところでした。

1.2 なにが問題?

まずは人が足りないということです。学校でやってるアプリとかっていうのは企業の真似事ですからアプリはデザインの人が仕様書作ってみんなで共同作業を...って感じですし、動画は絵コンテから映像化...ここからここまでは○○で...って感じでやるわけですよ。でも人が足りなければできることも少ない、でもやりたい、だから一人でやる。っていう感じに一人に背負わされるわけです。学生なのに労働基準法に抵触(学生だから適法だけども)してるわけですね(笑)

そして二つ目は代によって差があるということです。学校なので留年しない限り進級して卒業します。生憎、これでも進学校ですので高校3年生は学園祭には参加しないのです。そして学園祭は4月の終わりにやるので入ってきてすぐの中学1年生はどちらかというとお客さん側です。
つまりは中2、中3、高1、高2で作り上げます。もっと言うと学園祭は4月の終わりということで準備は前年度になるわけです。今は2019年11月ですから、次は2020年4月です。準備は今年度なんですね。準備は今の中1、中2、中3、高1でやるってことですね。中1はまだ一年を経験していないので見習いです。この状態でもやばいわけです。

lRtTSQg.gif

えー。取り乱しました。つまりは実質、全校の技術者の半分で行うわけです。そしてその差が開くとどうなるかといいますと...。

A期技術者a「よっしゃ、めっちゃ技術力あるで!すごいの作ったろ!」
その年の学校「すげぇー!」
ーーーA期引退ーーー
その年の学校「今年もすごいの作るんやろ?」
B期技術者b「よし、がんばるぞ...まずは前の年のコードは...わからん」
B期技術者c「わからん」
B期技術者d「わからん」
B期技術者bcd「やーめた」
A期技術者a「俺たちの努力が...一代だけ...」
その年の学校「あーあ」

ということになりかねません。というかなりました。(入学する前の学園祭アプリがたびたび途切れてます)そして、今まさにこの状況でこのままいけば僕たちがA期、一個下がB期を演じることになります。(幸い、僕の代は技術力が高いです)

1.3 解決策

講座を開講して技術者ではない人も取り込んで全体的に技術力をアップさせます。ここで気を付けたところ、失敗したところ、成功したところをまとめます。

2.技術者養成講座の歩み

2.1 2019年4月

そのときは全校が学園祭ムードに包まれていました。僕は中3になってすぐの時でした。前年度の体育祭のときに先輩にTwitterを特定され、Androidができると知られた僕はAndroidのアプリを一人で作っていました。正直、技術力は劣らないと思っていましたし、もちろん成功すると思っていました。しかし、いつまでたってもホーム画面が完成しないそうで「Activityは順番に作っていく」僕にとっては一個目の画面が来ないのでなにもできませんでした。先に他の画面をつくればそれぞれへIntentをつなぐ処理を書き忘れそうで怖かったですし、一度書いたコードは自分が書いたコードであっても解読に時間がかかっていましたのでできるだけ順番につくっていました。結果から言うと1週間の睡眠時間は10時間に達していませんでした。このとき、モンスター(魔材)とお友達どころか親友になりました。アプリは学園祭前一週間にはリリースしていました。ですが、画像を多く使うという理由で一部のくそスぺ端末ではOut of memory連発。また、徹夜しました。

2.2 2019年5月

ゴールデンウイークは毎日15時間は寝てました。このさなか、アプリを担当する部署の長になりました。ありがとうございます。同時に「技術者養成講座」(以下、養成講座)の話が同期の動画部署の長からありました。そのひとは僕以上の社畜で、いろんな仕事を掛け持ちしてました。もう、辛い思いをするのはやめようということで他の部署もまとめてこの講座をすることになりました。

2.3 2019年7月

この月に開催しようと思いましたが全然準備できてなくて延期になりました。

2.4 2019年8月

中国にいました。https://qiita.com/Cyber_Hacnosuke/items/e67df2b02cdd7b3e98c1

2.5 2019年9月

技術者ではない人も人員を増やすことにしようと決めました。ここで問題になったのはパソコンの貸し出しです。非技術者のなかにがパソコンを持っていない人が大半でしょう。また、技術者のなかでもデスクトップのみの人もいました。学校にパソコン室はあるのですが、Win8でexe等の実行ファイルは一切実行できない、ダウンロードしたファイルはシャットダウンすると削除...となかなかに難しかったです。

2.6 2019年10月

学校に政府からの補助金でかったマウスのパソコンがあるのが判明。すぐさま交渉にいきました。用途と合致している(研究者を育成するという政府の目的と合致?)ので使用可になりました。22台ほどありました。この確認が済んだ後、安心して中間テストに臨めたのはよかったです。中間テスト後には選択制の外部演習がありました。それが終わった後そのレポートとその演習の発表プレゼン、養成講座、学生研究で押しつぶされそうになりましたが、魔材の力で乗り切りました。幸い、養成講座の準備は8月からしていたので間に合いました。講座の募集が終わりました。全部で39人の応募があり、貸し出し台数はぴったり22台でした。なにかの集計間違えだと思ったのをいまでも覚えています。こんなにひとが集まるとは思いませんでした。このうち、アプリの講座を取ったのは11人で動画部署の12人に続いて2番目の多さでした。

2.7 2019年11月2日

土曜授業の終了後高2フロアにいき準備をしました。そして開講。結果はまた後で。

3. 講座の成功と失敗

3.1 内容

講座内容は以下です。

  • 挨拶
  • 自己紹介
  • OSってなに
  • byteと文字
  • 拡張子ってなに
  • プログラミングに必要なもの エディタ 開発環境
  • プログラミングサイクル
  • カウントアプリを作ろう
  • 変数
  • メソッド
  • イメージピッカーを作ろう

終わらないのを前提でもし理解が早かったらまずいので長めに設定しました。

3.2 失敗

あえて失敗から言いますと、

  • 時間が足りねぇ

のみです。カウントアプリを作り終えるところまでは行きたかったのですが途中までで、プログラミングは数行書いただけ...

3.3 成功

それでも楽しく授業はできました。かなり、わかりやすかったと反響はいただいています。プログラミングもっとしたかった?と聞いてみるとこれでもいいと言ってくれました…ありがとう!文字の解説では「1byte文字と2byte文字を今説明したけど、なにか気づいた?そう!全角と半角だよ!」といったときにみんなが驚いて納得したようでよかったです。途中、Fateのセイバー派か凛派かで生徒と戦争(?)になりましたが、アニメやラノベ、小説のネタを入れて面白く解説できました。プログラミングの続きをしたい人はぜひ同好会へ!と宣伝することでいまでは37人もの人が所属しています。ちなみに僕はアルトリア・ペンドラゴンちゃんが大好きです。とくにメイドオルタ。

4.なにをすればいいか

まず、激務を自分のせいだと思って抱え込まないのが大切です。予算が足りない、人が足りない、時間が足りない...そういったことを上に訴えることによって僕たちは(学校で)講座を開くことができました。では授業内容にはどのような配慮が必要でしょうか?

4.1 授業内容の配慮

初心者も交じっていればそのレベルに合わせるのが必要でしょう。最低レベルに合わせるということです。でも、上級者や経験者はつまらないと思ってしまいます。個人的な感情ですが、自分が知ってることを先生が自慢げに話すと、「先生ってそんな感じなんだ...」という感じになってしまいます。なので、みんなをアッと驚かせるコラムをいれることで「え!そうゆうことだったの?」と興味を持ってもらうことができます。また、このような内容は身近なものかつあまり知られていないことにすべきでしょう。僕の場合これが「1byte文字が半角、2byte文字が全角」というものでした。そしてこれは特に初心者向けにやったほうがいいことですが、プログラミングの先入観や固定概念を捨ててもらう必要があります。したような結果も出ています。(自社調べ)

○○中高の陽キャラに聞いた!プログラミングのイメージ(自社調べ)
3位「パソコンメガネ」
「陰キャラ?(笑)」「ウザい」「アニオタ」
2位「0と1の羅列」
「2進法?」「指が全部で2本しかない人の末路」
1位「ハッキング」
「危ない」「社会の敵」「暗い部屋」「緑色の文字」「Matrix」「一瞬でのっとり」「○ね」

こういった考えをなくし、プログラミングのプロセスを説明するというのが必要になってきます。そしてよく上級者が初心者がやってしまうのは「文法教えて課題」「ソースコード書かせて解説の繰り返し」です。これは時には中級者にも反感をかいます。理由としては言い方を考慮せずに言えば「初心者は思っているよりバカ」ということです。そもそも教える側にたてばそれだけで一般レベルから大きく外れています。僕たちなら別の言語に結びつけたりしてすぐ覚えますが初心者は頼るもの、すなわちプールサイドがない状態で泳いでるのです。説明しましょう、1からいや0から!そしてソースコードをガンガン書くのも同様の理由でよくないです。僕たちは単語帳のようにソースコードのフレーズを覚えられますが、それができない人は非常におおいです。書ききったとしてもそれはあなたの模範解答を映してる可能性が高いです。

  1. まとめ ということで、徹夜ラッシュ回避のため講座を開講したという話でした。https://twitter.com/Cyber_Hacnosuke
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Kotlin] Android Studioで簡単に非同期処理

はじめに

AndroidアプリでHTTPリクエストをしようとするとメインスレッドでやるなと言われるので、手っ取り早く解決する方法を一応メモ。

環境

  • Android Studio
    • 3.5.1
  • Kotlin
    • 1.3.50

HTTPリクエストするならインターネット通信のパーミッションを取るのを忘れずに!

AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"></uses-permission>

コード

メインスレッドでやらなければいいので、Threadクラスで別スレッドを作成すればOK。
今回はとりあえずHTTPリクエストをしてみる。

sample.kt
Thread {
    // ここは非同期処理
    val url = URL("https://www.google.com")
    val httpCone = url.openConnection() as HttpURLConnection
    httpCone.requestMethod = "GET"
    httpCone.connect()
    if (httpCone.responseCode in 200 .. 299) {
        val resIS = httpCone.inputStream
        val resReader = BufferedInputStream(resIS).bufferedReader()
        Log.d("LOG", resReader.readLine())
    }

}.start()

まとめ

とりあえず非同期処理を行いたい場合は、Threadを使えばいいと思う。
本格的に使うのであれば、AsyncTaskAsyncTaskLoaderを使うべきなのかな?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VisualStudioでAntを使ってNativeなAPKを作ってみる

※2019/11/13 Gradle版の記事もアップいたしました。詳しくは こちら をご御覧ください。

はじめに

CEDEC2019 の「玄人向けAndroid/iOS対応エンジン 開発レポート」というセッションにて VisualStudioでもGradleを使ったNativeアプリが作れるという話を聞きました。
かなり前から私がやりたかったことでしたので早速試してみました。

ブレイクポイントが止まらない

VisualStudioとGradleで色々試しNativeアプリは作れましたが、どうやってもブレイクポイントが止まりませんでした。
これではVisualStudioの旨味がかなり減ってしまいます。
ということで違う方法を模索しました。

VisualStudioとAnt

NativeActivityも試してみましたが、Android向けの広告などがJavaのライブラリの物が多くC++オンリーでの開発はかなり不向きな環境でした。
複数のプラットフォームに対応する上でVisualStudioで開発できることが私の環境では望ましかったため、VisualStudio上のAntで開発する方向で落ち着きました。

開発環境

  • OS : Windows 10
  • Visual Studio 2017 Professional

プロジェクトの準備

1. Antプロジェクトの作成

Android」という名前でプロジェクトを作っています。
00.gif

2. Sharedライブラリ用のプロジェクトの作成

Shared」という名前でプロジェクトを作っています。
01.gif

3. Sharedライブラリをantプロジェクトに紐付ける

02.gif

この段階でソリューションのビルドを行い、ビルドが通れば設定完了です。

JNIで繫いで動作確認

ここからはJavaとNativeを繫いでブレイクポイントが止まるか確認してみます。
2つのファイルに下記コードを追記します。

Android.java
public class Android extends Activity
{
    // 追記
    static {
        System.loadLibrary("SharedProject");
    }
    native void NativeTest(int value);

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        /* Create a TextView and set its text to "Hello world" */
        TextView  tv = new TextView(this);
        tv.setText("Hello World!");
        setContentView(tv);

        // 追記
        NativeTest(123);
    }
}
Shared.cpp
extern "C" {
    // 追記
    JNIEXPORT void JNICALL Java_com_Android_Android_NativeTest(JNIEnv* env, jobject, jint value)
    {
        LOGI("test [%d]", value);
    }
}

動作確認

上記を追記したらAndroid端末とPCを繋ぎ、実行してみます。
03.gif

ブレイクポイントを貼ったところで止まり、変数の中身が見えていることが確認できました。

まとめ

VisualStudio & Gradle で開発ができるのが理想ですが、どうしても難しい場合は VisualStudio & Ant でも行けそうです。
VisualStudio & Gradle でもブレイクポイントがうまく止まったらまた記事にしてあげようと思います。
VisualStudioでAndroidのNative開発している文献があまりにも少ないのでもう少し世の中に増えていくといいなと思いました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Unity]オーディオクリップ セッティング メモー

Unityマニュアル一通り

Force To Mono

これを有効にすると、マルチチャンネルオーディオはパックする前にミックスダウンされモノラルトラックにされます。
結論:いつ(雑音で?)使われるんでしょう!!! 使ったことありません!(以降もなさそう!x1)

Normalize

これを有効にすると、Force To Mono ミックスダウン処理中にオーディオは ノーマライズ されます。
結論:いつ(雑音で?)使われるんでしょう!!! 使ったことありません!(以降もなさそう!x2)

Load In Background

これを有効にすると、メインスレッドを妨げることなく、クリップの読み込みは別のスレッドで遅延させて行われます。
結論:CPU負荷分担したい時に使えそうです!

Ambisonic

アンビソニックオーディオソースは、リスナーの向きに基づいて回転する音場を表す形式でオーディオを格納します。全天球型のビデオや VR や AR アプリケーションに便利です。オーディオファイルにアンビソニックでエンコードされたオーディオが含まれている場合は、このオプションを有効にします。
結論:立体音声の時に使えそうです!

Preload Audio Data

オンになっている場合、シーンの読み込み時にオーディオクリップもあらかじめ読み込まれます。Unity の標準挙動ではシーンが再生される前にすべての AudioClip が完全に読み込まれるため、通常はオンになっています。このフラグが設定されていない場合、オーディオデータは、最初の AudioSource.Play()かAudioSource.PlayOneShot() か AudioSource.LoadAudioData() で読み込まれて AudioSource.UnloadAudioData() で再び破棄されます。
結論:マニュアル過ち修正 AudioSourceでなく、Audioclip.LoadAudioData()、Audioclip.UnloadAudioData()のはずです。
オン:端末が起動されシーンに入った時読み込まれますが、大量の音声データがある時、起動が遅くなるのが現象です。
オフ:起動負荷はないですが、音声Play()時読み込み解凍でcpu負荷がかかるのが現象でフレームが落ちます。なのでフレーム保持したいアプリとかは、事前に(初期化タイミング?)LoadAudioData()を呼び出しする必要があります。

LoadType

Decompress On Load

オーディオファイルは読み込まれるとすぐに展開されます。サイズの小さい圧縮サウンドにこのオプションを使えば、その場での展開に伴うパフォーマンスのオーバーヘッドを防ぐことができます。ただし、Vorbis でエンコードされたサウンドを読み込み時に展開すると、圧縮されたままの場合の10倍程(ADPCM エンコーディングでは約3.5倍)のメモリーを使用するので、サイズの大きいファイルにはこのオプションを使用しないでください。
結論:大体この設定でいいかもですが、大きいサイズは使えません!(大きいサイズ?これは個人の心の中の基準でしょうか!)

Compressed In Memory

サウンドはメモリ中では圧縮されたままで、再生中に展開されます。このオプションは若干のパフォーマンスオーバーヘッドが発生します(特に Ogg/Vorbis 圧縮ファイルの場合)ので、メモリー使用量が大きすぎて読み込み時に展開できないファイルにのみに使用してください。展開はミキサースレッドで行われ、Profiler ウィンドウ内の Audio ペインにある “DSP CPU” のセクションでモニタリングできます。
結論:音声play()時のcpuの負荷は小さくなるが、パッケージ(apk/ipa)した時のサイズが大量に大きくなります!お勧めしません!

Streaming

サウンドをその場でデコードします。この方式では、圧縮データのバッファーのための使用メモリーが最小限に抑えられます。データはディスクから段階的に読み込まれて必要に応じてデコードされます。展開は別のストリーミングスレッドで行われますが、その CPU 使用量は Pofiler ウィンドウ内の Audio ペインにある “Streaming CPU” のセクションでモニタリングできます。
結論:パッケージ(apk/ipa)した時のサイズがほぼない、cup負荷は分担され Decompress On Loadより小さい

Compression Format

PCM

品質は高くなりますが、ファイルサイズが大きくなります。非常に短い音響効果に最適です。
結論:説明の通りですが、使ったことありません!(以降もなさそう!x3)

ADPCM

この形式は、足音や衝突音、武器などのノイズを多く含んでいて大量に再生される必要があるサウンドに便利です。圧縮率は PCM の3.5分の1ですが、CPU の使用量は MP3/Vorbis 形式に比べ格段に少なくなるため、上記のような種類の音には適しています。
結論:雑音でNormalizeと一緒に使えそうですが、使ったことありません!(以降もなさそう!x4)

Vorbis/MP3

圧縮ファイルは小さくなりますが PCM オーディオに比べると若干クオリティが落ちます。圧縮率は Quality スライダーの調整で可能です。この形式は中程度の長さの SE や音楽に適しています。
結論:参考からAndroidはVorbis、iOSはMP3が最適のようです

Quality

圧縮 クリップに適用される圧縮の度合いを決定します。PCM/ADPCM/HEVAG 形式には適用されません。ファイルサイズに関する情報はインスペクターで確認できます。この値を調整する方法としては、スライダーをドラッグし、ファイルサイズを小さく保ったまま「許容できるレベルの」再生クオリティになるポイントを見つけるのがよいでしょう。(注)Original Size は元ファイルのサイズなので、例えばこれが MP3 ファイルで Compression Format が PCM (非圧縮)に設定されている場合、Ratio は結果として 100% を超える値になります。これは、ファイルが非圧縮で保存されるために元々のソースの MP3 ファイルよりも大きなスペースを取ってしまうからです。
結論:Ratioが100%を超えた時に手動で100%以下に抑えることができるハンドルでしょうか!

汎用まとめ

  • 大体の音源データロードタイプ
    • LoadType=Decompress On Load
  • 大きいすぎて展開できない音源データのみ
    • LoadType=Compressed In Memory
  • 長い音源データロードタイプ
    • LoadType=Streaming
  • 遅延で行われてもよく、cup負荷かけたくない時
    • Load In Background
  • 起動時にロードさせたいが、起動時間長くしたくない時
    • Preload Audio Data
    • Load In Background
  • Compression Format
    • Android : Vorbis
    • iOS : MP3

音声データインポートした時自動設定エディタ拡張

using UnityEngine;
using UnityEditor;

public class UnityAssetImporter : AssetPostprocessor
{
    /// <summary>
    /// 音源データインポートした後呼ばれる
    /// </summary>
    private void OnPostprocessAudio(AudioClip clip)
    {
        var audioimporter = (AudioImporter)assetImporter;
        audioimporter.Setting("iOS", AudioCompressionFormat.MP3, clip.length);
        audioimporter.Setting("Android", AudioCompressionFormat.Vorbis, clip.length);
        audioimporter.preloadAudioData = false;
    }
}
public static class ImpoterUtility
{
    public static void Setting(this AudioImporter importer, string platform, AudioCompressionFormat acformat, float length)
    {
        var setting = importer.GetOverrideSampleSettings(platform);
        setting.compressionFormat = acformat;
        //適当に時間でロードタイプ決める
        if (length >= 5f)
            setting.loadType = AudioClipLoadType.Streaming;
        else
            setting.loadType = AudioClipLoadType.DecompressOnLoad;
        importer.SetOverrideSampleSettings(platform, setting);
    }
}

注意
手動で設定変更した時拡張ファイルも一緒に一回実行されますので注意しましょう!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【デジタル署名】PlayStoreとAppStoreでAndroid・iOSアプリを配信する時の署名まわり【証明書】

「どうすればいいのか」という操作手順は探すと結構見つかるんですけど、

「それはなんのためにやるのか」というところがピンときてないと、

何かトラブった時に自分で対処できなかったり、

そもそも何をやってるかわからずに作業するのが気持ち悪かったりするので

それをなんとかする記事です。

間違いがあったらコメントで教えていただけるとありがたいです。他力本願…!

また、私自身で疑問が解決していないところは「ここがわからない?」という旨を随時書いていきます(?この絵文字で)ので、コメントで教えていただけるとありがたいです?。他力本願…!!

また、「デジタル署名」「公開鍵」「秘密鍵」「証明書」「認証局」などの言葉については既知とします。

知らなかったら先に調べてきてもらえると助かります。

…サラッと言ってますが、サクッと調べて2分で理解できるような概念ではないです。

ここで説明するとめちゃくちゃ長くなるので他に任せます、ということです。

そもそも何がしたいのか

両OSで共通してやりたいことは

配布するアプリにデジタル署名を施す

ということです。

デジタル署名が施してあれば、アプリを受け取る人は、手元にある「公開鍵(検証鍵)」を使って

  • アプリがその公開鍵と対応する秘密鍵を持つ人によって作られたこと
  • その秘密鍵で署名された後でアプリが改竄されていないこと

を確認できます。

なお、ユーザーはその公開鍵をいつ入手するかというと、

最初にそのアプリをインストールした時に一緒についてくる証明書に入ってる

ということになります。

署名で防げること(Android)

Androidの場合、署名の主な目的は「アプリを不正にアップデートされることを防ぐ」ことです。

こちらから引用します。(閲覧・引用日 2019年11月12日)
https://support.google.com/googleplay/android-developer/answer/7384423?hl=ja

Android アプリは秘密鍵で署名されます。アプリのアップデートが信頼できるものであることを保証するために、秘密鍵はそれぞれ公開鍵証明書に関連付けられています。デバイスやサービスはその公開鍵証明書を使って、アプリが信頼できる提供元によるものであることを確認できます。デバイスがアプリの更新を許可するのは、すでにそのデバイスにインストールされているアプリとアップデートで署名が一致する場合のみです。

既にインストールしたアプリと同じ製作者によるアップデートしか受け付けない、ということです。

この署名で防げないこと

Androidアプリには製作者の署名と、公開鍵証明書(公開鍵自体もそこに入っている)が含まれていますが

その「証明」を行ったのはアプリ制作者自身です。証明書に入っている公開鍵に対応する秘密鍵を使って、その公開鍵に署名したものです。

これを自己署名証明書と言います。

ということは、その公開鍵が正当な製作者のものであるという「第三者に依る保証」がありません。

攻撃者が偽アプリを作って、攻撃者自身が作った秘密鍵で署名し、自己署名証明書を同梱しても、受け取る人は正当な製作者によるものと区別がつきません。

なので、アップデート時以外、つまり、アプリを最初にデバイスにインストールする時(一度消して再びインストールする場合も含む)に、アプリそのものを署名や証明書ごとすり替えられる可能性が残ります。

ここは私もよくわかっていないところなのですが、

Play Storeとの通信がHTTPSで保護されている以上、そんな攻撃は考えられない、ということですかね??

Play Store以外から入手する場合は自己責任でいいと思いますけども。

署名で防げること(iOS)

iOSアプリの場合は、自己署名証明書ではなく、Appleが発行した証明書が付きます。

こちらのページから引用します。(閲覧・引用日 2019年11月12日)
https://developer.apple.com/jp/support/code-signing/

Appにコード署名をすると、そのAppが特定の発行元から提供されており、最後に署名した時点から改変されていないことをユーザーに保証できるようになります。MacやiOS Appを、ストアサービスで利用、開発やテスト用にiOSデバイスにインストール、App Storeに登録するには、Appleが発行した証明書で署名する必要があります。

アップデート時に限らず、最初のインストール時から、正当な製作者によるアプリであることが保証される、ということですね。

開発者からすると、Appleに公開鍵証明書をもらうという一手間が増えます。

「証明書で署名」?

…ところで。

私はこの引用に出てくる「証明書で署名」という表現が気になります。

これは「『証明書に含まれる公開鍵に対応する秘密鍵』で署名する」という意味に捉えればいいんですかね?

証明書に秘密鍵は含まれていませんから、「証明書で署名」と言われると「署名する時に証明書は関係ないじゃん」と思ってしまうんですが…?

共通の基本的な手順

ということで、デジタル署名を施してアプリをアップロードする大まかな手順は次のようになります。

  1. 秘密鍵・公開鍵のペアを作成する
  2. (Android)公開鍵の自己署名証明書を作る/(iOS)公開鍵をAppleに登録し、証明書をもらう
  3. 秘密鍵でアプリに署名し、証明書を同梱する
  4. アプリをストアにアップロード
  5. 秘密鍵と証明書のセットをどこかに保管しておく

となります。4と5はどっちが先でもいいです。

これがピンと来ていると、実際の作業も見通しが良くなります。

でもこれはあくまで基本的な流れで、具体的な流れはもっと細かくいろいろあります。

Androidの手順

ではもう少し具体的な流れを見てみましょう。

「何のソフトを開いて、どこをクリックして、何のコマンドを実行して…」というところまでは具体化しません。あくまで「この操作はこういうことをやっている」という解説です。

1. 2. 秘密鍵・公開鍵のペアを作り、自己署名証明書を作る

(アプリ開発が2つ目以降で、前回と同じ鍵を使う場合は、このステップは飛ばせます)

keytool --genkey (あと略)

このコマンドで、自己署名証明書の作成まで一度に行われます。

-genkeypair
(中略)
鍵のペア(公開鍵および関連する非公開鍵)を生成します。公開鍵はX.509 v3自己署名証明書でラップされます。

(https://docs.oracle.com/javase/jp/8/docs/technotes/tools/windows/keytool.html より引用 2019年11月12日)

ちなみに多くの解説ではgenkeyとされているところは、現在(2019年11月12日)では

--genkeypair

を使うべきとされているようです。機能は同じ。

秘密鍵も、公開鍵を含んだ自己署名証明書もまとめて一つのkeystoreに保存されます。拡張子は.keystore.jksにすることが多いようです。

開発したアプリが2つ目以降の場合、前回と同じ鍵を使っても構いません。

3. アプリに署名し、証明書を同梱

いくつか方法があります。

  • zipalignapksigner
  • jarsignerzipalign
  • key.propertiesbuild.gradleを適切に書いてビルド時に自動で処理

と言ったところでしょうか。

zipalignは署名とは関係ない操作ですが、署名方法によってタイミングが変わってくるので書き足しておきました。

4. アップロード

Play Store Console からアプリの作成を行い、ビルドしたアプリ本体をアップロードします。

リリースするためには説明文を書いたり対象年齢を設定したりいろいろやることがありますがこの記事では割愛します。

5. 秘密鍵と証明書のセットを保管

先程作ったkeystoreファイルをどこかに保管すればOK。

「Google Play アプリ署名」を使う場合

GooglePlayアプリ署名を使う場合は、アプリ本体への署名をGoogleが代わりに行います。その際の鍵ペアと自己署名証明書はGoogleが勝手に作ります。

また、Play Storeにアップロードするものが、apkファイルではなくAndroid App Bundleになります。

(Android App Bundleではなくapkファイルをアップしつつ、「Google Play アプリ署名」を使用することもできるのかどうかはよくわかりません?)

ユーザーは、アプリの製作者が変わっていないことをGoogleが保証してくれるので、安心してアップデートできます。

あとは、製作者がアップデート版をアップロードする時に、Googleに対して「前と同じ製作者ですよ」ということを証明しないといけません。

なので結局、製作者も毎回署名をする必要があります。この時の署名に使う鍵は「アップロード鍵」と呼ばれます。

とはいえ署名手順はそこまで大きく変わりません。使った鍵がアップロード鍵と呼ばれるようになるだけです。

アップロード鍵も、複数のアプリで同じものを使用してもかまいません。

既にapkファイルをアップロードしたアプリを、 「Google Play アプリ署名」を使うように変更する場合

この場合、今後のアップデートでアプリに署名を施すのはGoogleになるわけですが、

ユーザー視点で考えると、アプリに施された署名はアップデート時も変わってはいけないので、

製作者は、これまで使っていたアプリ署名用の鍵をGoogleに渡し、使ってもらう必要があります。

つまりこれって秘密鍵を渡すことになります。

これは結構珍しい操作な気がしますがどうなんでしょう。

そして、今後自分が使うアップロード鍵を用意して、それもGoogleに伝えます。こちらは秘密鍵を渡してはいけません。公開鍵(を含んだ自己署名証明書)だけで十分ですね。

iOSの手順

続いてiOSです。こちらも「つまりどういうことをしているのか」の解説にとどめ、具体的な操作手順は解説しません。

実際の手順はこちらのサイトがわかりやすいです。
https://i-app-tec.com/ios/app-release.html

1. 秘密鍵・公開鍵のペアを作成する

今回は、いきなり「キーのペアを作る」という操作はしません。ではどうするのか。

この後のステップで、「Appleに証明書をお願いする」という部分があります。

一般的に、認証局に証明書をお願いする場合は「証明書署名要求(certificate signing request, CSR)」と呼ばれるものを作成し、それを認証局に送ります。

証明書署名要求(Wikipedia)

認証してもらえた場合、証明書が送り返されたり、ダウンロードできるようになる、という流れです。

iOSアプリを作る際は、お手元のMacに最初からインストールされているKeychainAccessというアプリケーションを使ってCSRを作成します。

Keychain Accessを使ってCSRを作成する際に、秘密鍵・公開鍵のペアも自動で作成されます。

事前に作っておく必要はないということです。

ステップが削減されるのは良いのですが、私ははじめ「鍵を作らずにCSRを作るってどういうことだ…」と混乱しました。

2. 公開鍵をAppleに登録し、証明書をもらう

Apple Developerにログインし、Certificatesの所から操作を行います。

その際に、先程作ったCSRを指定する行程があります。

これが完了すると、証明書がダウンロード可能になります。

実はダウンロードしなくてもXCodeからこの証明書を指定できるので、アプリをアップロードするためなら証明書はダウンロードしなくてかまいません。

が、後で秘密鍵とセットで保管するので、ダウンロードしておきましょう。その際、Keychain Accessに追加しておきます。

有効期限は1年

作った証明書の有効期限は1年ですから、切れる前に作り直す必要があります。

その場合はまたCSRの作成から行います。

署名に使う鍵も変更されるわけですね。この点はAndroidの場合と違いますね。アップデート時に製作者が変わっていないことを、「署名が変わっていないこと」ではなく、Appleの認証によって保証するわけですね。

作り直したら、Provisioning Profile(後述)を編集して、紐付ける証明書を切り替えておきましょう。

証明書はCSRを作ったMacに紐付いている

Macを買い替えたりした場合は紐付けをし直す必要があります。

やり方はここでは解説しないので調べてください?

3. 4. 秘密鍵でアプリに署名し、証明書を同梱し、アップロードする

Apple Developerでの作業

まずApple Developerのサイト上でApp IDの登録をします。

その後、Provisioning Profileというものを作ります。これもApple Developerのサイト上です。

Provisioning Profileは、次の3種類の情報の組み合わせを表したものです。

  • Certificate(証明書のこと)
  • App ID
  • テスト用端末のID

この情報があることで、Appleは「どのアプリとどの証明書をくっつけて配信すればいいのか」を把握できます。

Certificate(証明書)の有効期限が1年で切れますので、その前に新しい証明書を作って紐付けておきましょう。

ちなみに、Provisioning ProfileとApp IDの紐付けは一対一対応ではないそうです。どういうケースで一対一対応ではなくなるのかコレガワカラナイ?

XCodeでの作業

アプリのプログラムそのものは、XCodeを使ってアップロードします。

その際に、先程作ったProvisioning Profileを指定します。そこに証明書の情報も紐付いているので、先程作った証明書を使ってくれるようになります。

もろもろ調整してアップロードします。

余談ですが、この部分でかなりいろんなエラーに悩まされると思います。私は「アイコンに透明度が設定されている」というエラーが一番多かったです。その場合は透明度を抜いた画像を作り直して設定し直します。

App Store Connectでの作業

最後にApp Store Connectのサイト上で、新しいアプリを登録し、スクリーンショットや説明文など必要な情報を登録します。先程作ったApp IDとの紐付けも行います。

Androidで言うところの、Play Store Console上での作業に相当します。

5. 秘密鍵と証明書のセットを保管

Keychain Accessをまた開いて、先程ダウンロードした証明書と、それに対応した秘密鍵をセットにしたp12ファイルというものを作成できます。それを保管すればOK。

CategoryはMy Certificateを選んでおかないと、p12を選択できませんので注意。

Keychain AccessをまるごとiCloudで管理しておくという手もありますね。

おわり

この記事は以上です。

署名まわりの話はややこしいですが、この記事の内容を理解しておけば、何が何だかわからずに作業するよりもずっと見通しが良くなると思います。

もし間違いがありましたら教えていただけると幸いです。また、疑問点として残した所もご存知の方がおられましたら教えていただけると幸いです。他力本願…!!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AppiumでAndroidアプリを自動テストする用の環境作成

はじめに

まとまっているところがなかったので作成しました。
自分用備忘録兼ねてます。

使用技術

macでの開発を想定しています。
zsh使用してるので、bashの方は適宜読み替えてください。

Main(必須)

  • Xcode
  • Homebrew:性質的にはsubだけどないと始まらないのでmainに入れてます
  • Ruby
  • Node.js
  • Java
  • Android Studio
  • Appium
  • WebdriverIO

js fremework

  • mocha + chai

Sub

  • enyenv:env系管理
  • rbenv:ruby管理
  • nodenv:node管理
  • jenv:java管理

環境作成

-- First Step --

001 Xcode install

AppStoreから。App版のテストで必要なのと、Homebrew入れるのにどうせ必要なので入ってない方はこの瞬間入れてください。結構時間かかります。
入れ終えたら一度起動してXcode Command Line Toolsをinstallしておきます。

002 Homebrew install

最新のスクリプトを
https://brew.sh/index_ja
からコピーしてください。

下記は2019.11.11時点のもの。

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

ターミナルで実行。
出てきた文言に従いましょう。

-- Second Step --

各言語のversion固定で変更しないとかだと要らないStep。
アップデート追従がとても大変なので入れておいたほうがいいでしょう。

ここを飛ばす場合は、

  • ruby 2.5.0
  • node 12.10.0
  • java 1.8.0

を適宜入れてください。

003 anyenv install

$ brew install anyenv
$ anyenv install --init
$ echo 'eval "$(anyenv init -)"' >> ~/.zshrc
$ anyenv init

004 rbenv install

$ anyenv install rbenv
$ (exec $SHELL -l)
$ rbenv install -v 2.5.0
$ gem install bundler

005 nodenv install

$ anyenv install nodenv
$ (exec $SHELL -l)
$ nodenv install -v 12.10.0

006 jenv install

AppiumにはJava8が必要です。
コマンドでJavaをインストールするなら以下のコマンドになります。
ライセンスの絡みがあるので詳しくは調べてください。

brew cask install adoptopenjdk/openjdk/adoptopenjdk8

※Javaは色々プロジェクトあるみたいなんですけどどっちがどうとかはわからないです。。。

ライセンス的にjenv経由ではインストールができません。インストールされているJavaの管理のみになります。

$ anyenv install jenv
$ (exec $SHELL -l)
$ jenv add $(/usr/libexec/java_home -v 1.8)

-- Third Step --

007 Android Studio install

$ brew cask install android-studio

install後、起動して適当なプロジェクト作成してSDKとgradleを入れておきます。

008 Add PATH

.zshrcに追加します。
ANDROID_HOMEは各機の環境に差し替えてください。

# ANDROID_HOMEの設定
export ANDROID_HOME=/Users/User名/Library/Android/sdk
export PATH=${PATH}:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools

# JAVA_HOMEの設定
export JAVA_HOME=$(/usr/libexec/java_home)
export PATH=${PATH}:$JAVA_HOME/bin

### etc...
PATH=$PATH:/usr/sbin

009 Appium install

AppiumとAppiumのチェック用のappium-doctorをglobalにinstallします。

npm install -g appium appium-doctor

010 Add etc...

Appiumで必要なもの色々。
cmakeが初期では入ってないので入れてます。

brew install cmake

globalに各種入れていきます。

opencv4nodejsはインストールに時間がかかります。

※画像処理に関する様々なアルゴリズムが簡単に使えるオープンソースライブラリ

npm i -g opencv4nodejs

※動画や音声を加工するツール

brew install ffmpeg

※動画圧縮の形式

npm i -g mjpeg-consumer

wgetが初期ではないので追加します。

brew install wget

※Androidアプリバンドルを操作するためのツール

$ mkdir ~/bin/
$ cd ~/bin/
$ wget https://github.com/google/bundletool/releases/download/0.10.3/bundletool-all-0.10.3.jar
$ ln -s bundletool-all-0.10.3.jar bundletool.jar
$ chmod a+x bundletool-all-0.10.3.jar

PATH追加

# bundletoolの設定
export PATH="$PATH:$HOME/bin/"

011 check appium-doctor

appium-doctor --android

オールグリーン「Everything looks good, bye!」が出たらOK。
出なかったときは頑張って

-- Fourth Step --

このstepはなくても動きます。

012 download standalone appium

http://appium.io/
公式よりスタンドアロン型のAppiumも落としてきます。
なにかと便利なので。

Edit CongigurationsよりANDROID_HOMEとJAVA_HOMEの設定をお忘れなく。
スタンドアロンは自身の設定を見ます。

-- Fifth Step --

テストプロジェクトの作成をします。
前提として

  • 実機テスト
  • テスト対象アプリは実機にインストール済み
  • 実機は開発者モード + USBデバッグON

とします。

013 create project folder

好きな場所に作ったあとcdでそのフォルダに入ってください。

以下例。

mkdir /projects/test-sample/
cd /projects/test-sample/

014 webdriverio install

$ npm init
$ npm install --save-dev webdriverio
$ npm install @wdio/cli

015 create config

$ npx wdio config

環境作成おわり。

おわりに

気力が湧いてきたらプロジェクト設定やらテストやらも書きます。
つかれたよー。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android]Exifを理解して、取得して、表示する

はじめに

今回は Exif を理解して、取得して、表示して見ようと思います。
最終的には次のような Android アプリを作成して動作確認します。

Exif を理解する

私は写真の位置情報を取得するなら Exif で理解が止まっているので、
はじめに Exif について調べて理解を深めていこうかなと思います。

Exif ってなんなのか?

次のようなことが Wikipedia の Exif のページに書いていました。

Wikipediaから引用
Exchangeable image file format(エクスチェンジャブル・イメージ・ファイル・フォーマット)は、
富士フイルムが開発し、当時の日本電子工業振興協会 (JEIDA)で規格化された、写真用のメタデータを含む画像ファイルフォーマット。
デジタルカメラの画像の保存に使われる。略称はExifで「エグジフ」(もしくは「イグジフ」)。

また Exif の規格書には次のようなことが書かれています。

デジタルスチルカメラ用画像 ファイルフォーマット規格 Exif2.3 より引用
この規格は、ディジタルスチルカメラ、及びデジタルスキルカメラで記録される画像ファイル又は
音声ファイルを取り扱うシステムにおいて、画像、音声及びタグのフォーマットを規定する。

Exif は ”だだのメタデータ群” だと勘違いをしていましたが ”画像、音声及びタグのファイルフォーマット” なんですね。

Exif はJPEGじゃないのか?

Exif はファイルフォーマットだと理解できましたが、 Exif という拡張子は見たことないし結局どんな画像なのかって思いますよね。
もう少し Exif の規格書を読み進めていくと次のようにその疑問の答えが書いていました。

デジタルスチルカメラ用画像 ファイルフォーマット規格 Exif2.3 より引用
ファイルの記録形式は、既存のフォーマットをベースとする。圧縮ファイルはJPEG(ISO/IEC 10918-1)に
アプリケーション・マーカーセグメント(APP1, APP2)を挿入記録し、非圧縮ファイルはTIFF Rev.6.0で記録する。

つまりは Exif 画像と言えば JPEG 画像 + メタデータ情報 のことらしいです。
なので JPEG 画像であるのは間違いありませんがそこにメタデータが含まれるかどうかで Exif 画像であるか決まるという感じですね。

Exif にはどんな情報が含められている?

Exif ですがここに書ききれない種類の情報(タグ)が含められています。
例えば情報(タグ)の一覧は例えばこことかでまとめてくれています。
なので Exif から情報を取得するときにはタグ一覧から欲しい情報があるか確認するのがよいと思います。

Exif を取得して表示する

ある程度ですが Exif への理解も深められたところで、
実際に Android で Exif を取得して表示してみます。

1. Exif 情報を読み取る画像を格納する。

今回は Assets フォルダにある画像の Exif 情報を読み取りたいと考えています。
ですので次の手順で Assets フォルダを作成して画像を格納しておきます。

(1) 「appを右クリック」→「New」→「Folder」→「Assets Folder」で Assets フォルダを作成する
(2) 作成した Assets フォルダを開き、Exif 情報を読み取る画像をコピーする

image.png

2. Exif 情報を表示する View を作成する。

Exif情報を読み取った画像を表示するための ImageView と
読み取った Exif 情報を表示するための ListView を MainActivity に定義します。

image.png

main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:background="@android:color/black"
        android:scaleType="centerCrop"/>

    <ListView
        android:id="@+id/exif_data_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

3. Exif 情報を読み取る。

画像から Exif 情報を読取るには ExifInterface を利用します。
次のように ExifInterface を作成し getAttribute でタグを指定することで Exif のタグ情報を取得できます。

Exifタグを取得する
{
        // getAttribute に指定するタグの定義は Exit Interface ページに記載されてるのでそこを参照する。
        val inputStream = assets.open("sample.jpg")
        val exifInterface = ExifInterface(inputStream)
        exifInterface.getAttribute(TAG_NAME)
}

今回の ListView に複数の Exif タグ情報を表示したいので、
次のように Map にセットしたタグの情報を取得して表示するように処理を記述します。

MainActivity.kt
    private val exifMap = mapOf(
        "Datetime" to ExifInterface.TAG_DATETIME,
        "Latitude" to ExifInterface.TAG_GPS_DEST_LATITUDE,
        "Latitude Ref" to ExifInterface.TAG_GPS_DEST_LATITUDE_REF,
        "Longitude" to ExifInterface.TAG_GPS_DEST_LONGITUDE,
        "Longitude Ref" to ExifInterface.TAG_GPS_DEST_LONGITUDE_REF
        )

    private fun createDrawableFromAssets(assetsName: String) : Drawable {
        val inputStream = assets.open(assetsName)
        return Drawable.createFromStream(inputStream, null)
    }

    private fun createExifArrayFromAssetsFile(assetsName : String) : ArrayList<String> {
        val inputStream = assets.open(assetsName)
        val exifInterface = ExifInterface(inputStream)
        return arrayListOf<String>().also { array ->
            exifMap.forEach { item ->
                array.add("${item.key} : ${exifInterface.getAttribute(item.value)}")
            }
        }
    }

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

        val image = findViewById<ImageView>(R.id.image_view)
        val drawable =  createDrawableFromAssets("sample.jpg")
        image.setImageDrawable(drawable)

        val list = findViewById<ListView>(R.id.exif_data_list)
        val array = createExifArrayFromAssetsFile("sample.jpg")
        list.adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, array)
    }

4. 動作を確認する

アプリを起動して動作を確認すると次のような感じで Exif 情報が表示されます。

おわりに

  • Exif とは画像・音声又はタグのファイルフォーマット
  • Exif 画像は JPEG に複数のメタデータ(タグ)を含めたもの
  • Android で Exif 情報は ExifInterface を利用すれば取得できる

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む