月別: 2016年5月

暗号化について勉強中

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

Androidでユーザデータを暗号化して保護するにはどうしたらいいのでしょうか。

これまで「暗号化のやり方がよくわからないから、機密情報を保存しないようにしよう」と避けてきたのですが、それにも限界があるのかなぁと思って、手を広げてみる気になったのです。

まあ本当の出発点は、ユーザデータの保護ではなく、アプリで扱うデータを秘匿するにはどうしたらいいのだろうだったんですけれど・・・。

例えば、Twitterクライアントを作るとして、そのAPIキーをどうやって秘匿するのかということです。

Androidでこの手のAPIキーを扱う場合、サンプルではJavaのコードに直接書いてありますが、実際に自分でリリースするアプリでもそのままでいいのかという話です。

AndroidではAPKを簡単に抜き出して、しかも簡単にソースコードを確認することもできます。ProGuardで難読化をしても、Javaのコードに埋め込んだ文字列はそのままです。

String api_key = "hogehoge";のapi_keyの部分はProGuardによってaとか意味のない文字に書き換わりますが、”hogehoge”の部分は書き換わりません。(書き換わったらプログラムの動作が変わってしまうので当たり前です)

つまり、やろうと思えばAPIキーは見放題ということになります。

だからここの部分、どうするのがセオリーなんだろうってのから始まって、暗号化すれば秘匿できるのかなと思ってみたのです。

多分、ユーザに見せたくないデータはアプリで保持しないのが正解であって、暗号化は関係ないと思いますが、それでも今まで放置していた暗号化に関して勉強するいい機会ではあるのでそのまま続けております。

どう勉強してるのかって話ですが、https://developer.android.com/training/articles/keystore.htmlを見たり、Android Studioのimport Sampleで取り込めるGoogleのサンプルコードを使ったりしてます。

実際に手を動かしてみて初めて知ったのですが、同じ文字列を同じ暗号化鍵で暗号化しても、得られるバイト列が毎回異なることに驚きました。”abc”を同じ暗号化鍵を使って暗号化したら、毎回123になるというイメージを持っていたものですから、なんで毎回異なるバイト配列になるのか不思議です。しかも異なるバイト配列になるのに、復号化したらちゃんと”abc”に戻るのですからなお不思議です。

これは一体どういう仕組なのか。そもそも暗号化の仕組みを理解せずに使うのは逆に危険なんじゃないのかと思い始め、今度はこんな本を読み始めました。

まあまだ読んでいる最中なんですけど面白いです。

本書には

弱い暗号は暗号化しないより危険である

という考え方が序盤で紹介されています。仕組みを理解せずに暗号化するのもまた良くないと言えるのではないでしょうか。

安定の結城先生なので非常に分かりやすく、楽しく読めそうです。

それにしても、初めはAPIキーを秘匿することがきっかけだったのに、我ながらよく脱線してきたものだなぁと思います。

Contextの使い分けについて私が辿った変遷

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

Application Contextを渡してはいけないという記事を読みました。

http://ytrino.hatenablog.com/entry/2016/05/26/033936

この記事を読んで改めて自分のContextに対する認識があまいことを実感したので、思ったことをまとめてみようと思います。

趣旨は「僕はContextに対してこういうふうに思ってるんだけど、違う・違わないが自信持ててないから誰か突っ込んでくれ」です。たぶんそんなに間違ってないと思うんですけど、相談できるような相手がいないんだ、察しておくれ。

「私もそういう認識です」と言ってもらえれば自信になるし、違う部分があれば指摘をもらって学びの機会になり私のためになります。また、こういう変遷を経て学んできたという情報が初学者の役に立てばなぁなんて思ってもいます。

私のAndroidレベルは初学者というわけでもないですが、Contextにまつわる話を聞くと理解が曖昧でモヤモヤするレベルです。上記記事に倣えばAndroid2級でしょうか。

私は最近でこそライフサイクル考えてContextを使い分けるようになりましたが、ちょっと前まで「困ったらApplication Context渡しとけば問題ない」という考えの人でした。前までというか、今もそういう部分があるんですけどね。だからこそ「Application Contextを渡してはいけない」というのを見て不安になってしまいました。

渡してはいけないの意味

Application Contextを渡してはいけないというのは、どういう意味なのでしょうか。

  1. アプリがクラッシュするから渡してはいけない
  2. 表示がおかしくなるから渡してはいけない
  3. 渡しても動作上は問題ないが、コードの見た目上・意味的に美しくないので渡してはいけない

実はApplication Contextをなるべく使おうというのも、Application Contextを渡してはいけないというのも、あるコンテキストにおいては間違ってないんじゃないかなと思います。それぞれ違うコンテキストで話しているので、そこを混同すると混乱してしまいます。

前者は「メモリリークを回避する」という文脈での話で、Activityのリークを防ぐにはApplication Contextを使えばいいのは正しい(はず)です。

一方後者はthemeの適用の観点での話で、Application Contextでは意図した動きにならない・変にハマることがあるからApplication Contextを安易に使ってはいけないという話で、これはこれで正しいわけです。

どっちも正しいのですが、残念ながらコードを書いていてContextを要求された時に、私たちは何らかのContextを渡さないと先に勧めません。何を渡せばいいかは「ケースバイケースなんで一概にどれを使えとは言えない」んで、Contextの要求に対しては、それぞれ適切なContextを渡す必要があります。

ではどのようにしたら、何を使うのが適切かを判断できるのでしょうか。ライフサイクルを考えろということでしょうか。そのライフサイクルはどうやって判断するんでしょうか。

私はその判断を、今はソースコードを読むことで行っています。Contextを要求するメソッドが、クラスが、いったい渡されたContextをどのように利用しているのか。それをもって何を渡すか考えています。

Contextについて私の辿った道

Androidを学び始めた当初は、Contextなんて意識していませんでした。本やサンプルコードの写経する分には意識しないですみますから。

しかし写経から脱し、自分でコードを書き始めるとそうもいきません。こういう処理を実装したいな→このメソッド使えばなんとかなりそう→Contextが必要らしい。はたとキーを打つ手が止まります。

そのメソッドを使うにはContextを渡すしかない。渡さないと先に進まない。とりあえずthisって書いたら動いた。よし、これで先へ進める。なんていうのが、Androidを学び始めた人が辿る道ではないでしょうか。私はそうでした。

学び始めの頃はActivityからなんかすることが多いので、this、すなわちActivityを渡していればとりあえずは動いてくれました。私はそうして「ああ、ContextっていうのはActivityを渡せばいいのだな」と認識するようになりました。

しかしActivity Contextを渡してしまうとメモリリークが発生するぞという話を耳にします。そこで登場するのがApplication Context。getApplicationContext()を使えばメモリリーク対策になるという話を聞いて、「ああ、じゃあとりあえずApplication Contextを渡しておけば問題ないのか」となりました。

しかし今度はApplication Contextを渡すと想定通りにViewが表示されないことがあるという。無思考にApplication Contextを渡すのもどうやらダメらしい。

一周回ってきてしまいました。もう一体どうしろと。このメソッドContext要求してくるのに僕は一体何を渡せばいいんだ。

私のContextとのつきあい方

そんな私が多少なりとも指針を持てるようになったのは、一言で言ってしまえば慣れてきたからなんだと思います。

とりあえずコード書いて、動かしてみて。それを繰り返すうちに慣れてきて。ソースコードを読むようになって「多分こうやれば大きく間違ってはいないはず」という、そんな指針を持てるようになりました。

Contextを要求されたら何を渡すか。重要なのは考え方、つきあい方だと思います。これが今回の記事の本題です。

私はまずライフサイクルを考えるより前に、Contextを要求するメソッドのソースコードを確認しに行きます。そいつがContextへの参照を保持するのか、それともContextを使ってなんかするだけなのか。それを確認するのです。

Contextへの参照を保持するなら、Activity Contextを渡していいのか、ライフサイクルを考えなければなりません。ライフサイクルを考えるというよりは、Activityとともに役目を終えるのか、そうでないのかで判断していますが、正直このあたりは曖昧です。

該当のメソッドが単にStringリソースを読みだすのに使っているとかだったら、別にActivity Contextを使っても問題ないですよね。そもそもActivityがリークするのは、Activityへの参照を持ち、渡した先のオブジェクトがActivityより長生きする場合だからです。

Contextへの参照を持たないのであれば、別にどっちを使おうが問題ないので、好きな方使えばいいじゃんって感じですが、Activityから呼び出すのであれば、わざわざApplication Context渡すのも大げさなので、Activity Contextでいいかと思います。

Androidと付き合っていくうちに、なんとくこんな感じに行き着きました。それでもメモリリークが怖いなら、Contextに何を使えばいいか迷うよりも、それでメモリリークするのかを確認する方に力を注いだほうが良いと思います。

でも、独学でやってるとこれで本当に問題ないのか確証が持てないので、いつもどこかで不安です。誰かに聞く機会もなくてつらい。

見えないメモリリークの影

そもそもContextについて意識し始めるキッカケは、私の場合はActivityのメモリリークです。

このメモリリークも私にとってはContext以上によく分からない存在でした。最初のうちはメモリリークしているかどうかをどうやって確認すればいいのかがわからなかったからです。確認方法が分からないから、メモリリークしないコードを書かなきゃという気持ちだけが肥大化していました。

そんな中で「とりあえずApplication Context渡しておけばライフサイクルの関係でActivityがリークすることはない」という1つの指針は、私にとっては心強かったです。

実際にはApplication Contextを渡すからメモリリークしなくなるわけではないというのは、今であれば分かります。しかしはじめの頃はそんな違いも分からなくて、そんなことよりContextとして何を渡せばいいのかという指針があることがありがたかったのです。

これに関しては、メモリリークしているかどうかを確認する方法を学ぶ方が大切だと、今であれば思いますけどね。当初は確認方法がわからないけど、メモリリークしたら困るからApplication Context渡しとけっていう感じでコード書いてました。

初学者に向けてステップアップのために

初学者がもっとも優先すべきはとりあえず先に進むことだと思います。動くアプリを作ることです。そしてAndroidに慣れることです。

Contextを要求するメソッドに対して、何らかのContextを渡す。Activity Context渡すのかApplication Context渡すのか迷うと思います。最初のうちはとりあえずどっちか渡して動いてればいいと思います。動けば正義です。迷って足を止めるより、いっぱい作ってAndroidに慣れる方が大事だと思います。

慣れてきたらContextを渡すときに「こいつはActivityより長生きする奴なのかな」と考えましょう。そんなときはソースコードを読むことが助けになると思います。

Contextを要求するメソッドは、渡されたContextをどのように使っているのか。そいつもContextが必要なメソッドを呼び出すために必要としているのか。それともContextへの参照を保持するのか。

Contextに何を使うべきなのかはケースバイケースで一概には言えません。Activity Contextじゃないとダメな場合もあれば、Application Contextの方が適切な場合もあり、はたまたどっち渡しても問題ない場合だってあります。・・・なんでこんなややこしいことになってるんでしょうね?

とりあえず、動けば正義でトライアンドエラーを繰り返してAndroidに慣れる。慣れてきたらソースコードを読んでみて、Contextがどう使われているかを気にしてみる。そうやってステップアップしていくしかありません。

私のContextとの現在のつきあい方

長くなったので最後にまとめ。

私はActivity Contextを基本的に使うようにしていて、利用先でContextへの参照を持つ場合にはApplication Contextの利用を考える、というような感じでやってます。

Activity Context使っているのはThemeのためとかじゃなくて、単にgetApplicationContext()を書くと長くなるからとかそんな理由です。明らかにActivityのライフサイクルより長生きするオブジェクトに対しては、Application Context渡していますけれども。

正直な所、メモリリークをおそれてContextにどれを使うか悩むより、メモリリークの確認の仕方を調べて、リークしていないかを確認することに力入れたほうが建設的なんじゃないかなと思います。

何を使うのが適切か迷ってまごまごする前に、どう使われているのかをまず確認する。Contextの使い分けに関してはこれが私の指針なんですが、そんなに大きく間違ってはないですよね?

Kotlin使ってみて感じる便利さと葛藤

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

最近アプリやAndroid Studio用プラグインを作るのにKotlinを使っています。

始めたばかりの頃は「Javaだとああ書くんだけど、Kotlinだとどう書けばいいんだ」ということが多かったです。Javaでまともに書けないのにKotlinに手を出すのは早いんじゃないかとも思っていました。

しかし少しずつ試していくと、Kotlinの便利な部分が分かってきてきました。

私の場合、「Kotlinで書き始めたんだけどやっぱ使い方よくわからないからJavaに戻そう」とう場面が初期の頃はよくありました。はじめはKotlinで書いていたけど、やっぱりJavaで実装しようとという感じです。

そんなときに、「Javaに戻すのめんどくせえ」と感じる部分があって、そこで改めて「Kotlinってやっぱ便利やなぁ」なんて実感しました。

それからというもの、Kotlinの比重が徐々に増えてきて、今では逆にJavaで書く方が面倒くさいと感じるようになってしまいました。

一方で、Kotlinが無敵というわけではありません。Annotation Processingを使うライブラリがKotlinだとうまく使えないことがあったり(基本的には大丈夫ですが、Javaで書けば動くコードがKotlinで同じように書くと動かないことがあったりします)、Javaと比べるとコード補完が遅かったり、Kotlinを使うことで感じるストレスもあります。

ですが、不便さを差し置いてもKotlinで書いた方がすっきり書けるのはやっぱり快適だと思っています。

テストコードから始めるといいかも

どなたかの記事で、Kotlinはテストコードから導入してみたらどうかという記事を読みました。私もいい方法だと思います。Kotlinの便利さを実感するためではなく、どう書くかを知るのにちょうどいいと思います。

私もJUnitでのユニットテストをKotlinで書いています。テスト対象をKotlinで書いてるからとか、セミコロンつけなくてもいいから、とかそんな理由です。ユニットテストについてはKotlinが便利だからという理由はあまりないかもしれません。

ユニットテストにおいてKotlinが便利だと思うのは、バッククオート(`)で囲むことで、メソッド名やクラス名を数字から始めることができたり、途中に空白を含めることができたりすることでしょうか。

私はテスト名に日本語を使うことが多いです。そしてそのときに、メソッド名に使える文字に制約があるのが微妙に困ります。

例えば各月の最終日を求めるメソッドのテストをするのに「4月の場合は30日を返す」というテスト名にしたくてもできません。Javaではメソッド名を数字から始めることができないからです。だからこんなときは「月に4月を指定したら30日を返す」という感じのメソッド名にするのですが、これが微妙なストレスになります。(頭に「月を」つけるだけやんという感じですが、微妙にストレス感じるんですよこれ)

Kotlinではこの制約に煩わされることがありません。メソッド名をバッククオートで囲めば、数字から始めようが、途中に空白を挟もうが問題ないのです。

    @Test fun `2つの時刻の差を求める`(){
        val time1 = LocalDateTime.of(2014, 1, 1, 23, 58, 30)
        val time2 = LocalDateTime.of(2014, 1, 2,  0,  4, 30)
        val actual = Duration.between(time1,time2).seconds
        assertThat(actual, `is`(360L))
    }

なにそれ気持ち悪いと思われるかもしれませんが、これはれっきとしたKotlinの仕様です。

Grammar – SimpleName

Javaのメソッド名規約によってテストメソッド名を考えるのが面倒くさいなぁと感じている人は私だけではないと思いたい。

一方でKotlinでユニットテスト書けば便利なことばかりではありません。例えばassertThatなどを使おうとするとimport文を手書きで書かないと認識してくれないのが不便です。(私の環境の問題なのかもしれません。JavaだとALT+Enterでimportできるんですけどね・・・)

セミコロンつけなくてもいい

単純なことですが、Kotlinは文末にセミコロンをつけなくてもいいのです。

これが便利・・・と言いたいところですが、私は半々かなぁと感じています。

確かにいちいちセミコロンつけなくてもいいので楽です。たまにJavaでコードを書くときに、しょっちゅうセミコロンつけ忘れます。それくらいには快適です。

一方セミコロンが不要なせいで、メソッドチェーンするときに私は微妙にストレスを感じます。

Javaだとメソッドチェーンするときに改行をするとインデントを一段深くしてくれます。

しかしKotlinでは改行した時に文末なのか次の文に移るのかが判別不能なので、インデントを深くしてくれたりしません。これが毎回微妙にめんどうくさいです。私はいつもドットを打ってカーソル戻して改行するという方法をとって回避しています。

多分Reformat Code(Cmd+Alt+l)を使うのが楽なんでしょうけども。みんなどうしてるんだろう・・・。

文字列の扱いが便利

Strings

Javaだと文字列に変数を埋め込もうと思うと、+演算子で連結しなければなりません。もしくはString.format()を使うかですね。

Kotlinだとそんな面倒くさいことをせずとも、文字列中に変数を埋め込めるので便利です。こんな感じに書けるわけです。

val hoge = "num is $num"

埋め込む変数が多くなればなるほど、これはとても便利になります。デバッグのために変数の中身を文字列として出力して確認することがよくあると思いますが、そんなときに特に楽だと思います。

複数行に渡る文字列も、"""で囲むことでそのまま文字列として扱うことができます。ただしこれを使うと、インデントによる空白も文字列に含まれてしまうので、使いドコロが難しい気もします。

配列操作が便利

例えば配列の要素の中から最大値を取得しようと思ったら、Javaだとこんな感じになるでしょう。

int[] array = {1,20,3,40,5,16,7};
int max = 0;
for (int num : array) {
    max = Math.max(num, max);
}

Kotlinだとこうです。

val array = arrayOf(1,20,3,40,5,16,7)
val max = array.max()

わざわざループを回したりしなくてすんで、非常にすっきりします。

他にも、例えば配列のインデックスもあわせて使いたい場合は、.forEachIndexed{index, data ->//処理}なんて書けます。for文を書く頻度がだいぶ少なくなります。

一方で、配列の宣言がいまだに慣れません。

arrayOf()arrayListOf()listOf()mutableListOf()などなど、いろいろ種類があっていつも混乱します。JavaのListを引数に取るメソッドをKotlinから利用するときなど、引数の型が違うという感じでAndroid Studioに怒られるのがしょっちゅうです。

拡張関数が便利

Kotlinの便利さを実感するのがこの拡張関数です。

例えばorg.threeten.bp.LocalDateTimeとjava.sql.Timestampの変換をしようと思うと、DateTimeUtilsクラスを利用すればできます。

val localDateTime = LocalDateTime.now()
val timestamp = DateTimeUtils.toSqlTimestamp(localDateTime)

でもこれ、LocalDateTimeクラスに直接Timestampへの変換を行うメソッドがあったらもっとスマートに書けます。そしてKotlinなら拡張関数を利用することでそれが実現できます。

例えばUtil.ktというファイルを作成して、そこにこんな関数を定義します。

fun LocalDateTime.toTimestamp() = DateTimeUtils.toSqlTimestamp(this)

するとval timestamp = localDateTime.toTimestamp()と書けるようになるのです。

Javaだとユーティリティクラスを作成して、staticメソッドでやっていたようなことを、Kotlinだとずっとスマートに実装することができます。これに手を出し始めるとJavaには戻れないですね。

ただ多用すると知らない人からみたら「なんぞこれ」というコードになってしまいそうです。自分で作って自分で使う分には便利で気持ちいいのですが、チームで使うときにはまた違う感想になるのかもしれません。

まとめ

私自身はKotlin便利で、全部Kotlinで書けたら楽だなぁとは思っているんですが、一方で素直にJavaで書いた方が楽じゃないかと思うときも良くあります。

書き上がったソースコードはすっきりしているものの、書いてる最中はコード補完が遅くてストレス感じます。単純に私のマシンスペックの問題なのかもしれませんが(3年前に買ったMacbook Airなのでいい加減買い換えたい)、固まってイライラすることも少なく無いです。たまにJavaで書くとコード補完が早くて快適に感じます。

KotlinはJavaと100%互換で、基本的にはKotlinかJavaかを意識しなくてもいいのですが、それでもやっぱりJavaとの境界は意識せざるを得ません。Javaのことを意識して書かざるをえないのであれば、はじめからJavaで書いた方が楽という部分はあると思います。

また、Android StudioのInstant Runとの関係かもしれませんが、変更したソースコードが反映されずにデバッグに余計な手間がかかることがたまにあるのもストレスです。毎回起こるわけでもないのがまた微妙なところです。

素直にJavaで実装したほうが楽なのかもしれないけれども、一方でKotlinの便利さを知ってしまったがゆえにJavaで書くのも面倒くさいという葛藤を抱いております。ちょっと前までは「フルKotlinが一番便利でいい」とこだわろうとしていたのですが、今はKotlinとJavaをいい感じに併用していく柔軟性が大事なのかなと考えを改めているところです。