2012/03/08

Android:Windowsでapkを逆コンパイルする方法


.apkからソースコードを取り出す方法です。
# バッチの実行コマンドなどは説明を簡略化するためにフルパスで指定しています。

手順1:.apkからclasses.dexを取得
逆コンパイルするには、まずclasses.dexが必要です。
逆コンパイルしたいapkを取得して、zip解凍ソフトで解凍してください。
# よくわからない場合は拡張子をapkからzipに変えてダブルクリックで解凍。
(今回はCドライブ直下に解凍しました)


手順2:dex2jarで.dex→.class変換
手順1で解凍したフォルダの中にclasses.dexがあると思います。
逆コンパイルするにはclasses.dexを.classファイル形式に変換します。

.dex→.class変換にはdex2jarというツールを使います。
dex2jarツールは下記サイトにあります。
http://code.google.com/p/dex2jar/downloads/list
(今回はdex2jar-0.0.9.8.zipをダウンロードしました)


dex2jar-V.V.V.V.zipを解凍すると多くのシェルやバッチが入っています。
(今回はCドライブ直下に解凍しました)

バッチの中にdex2jar.batがあるので確認しましょう。
dex2jar.batとclasses.dexを確認したらコマンドプロンプトで下記を実行します。

C:\> <dex2jar.batのパス> <classes.dexのパス>
例)C:\> C:\dex2jar-0.0.9.8\dex2jar.bat C:\FragmentTest\classes.dex

正常終了したらclasses.dexと同じ階層にclasses_dex2jar.jarが出来ます。
この.jarをzip解凍ソフトで解凍します。(今回はCドライブ直下に解凍しました)
# よくわからない場合は拡張子をzipにして解凍

jarを解凍するとソースディレクトリが取得でき、中にclassファイルが格納されています。


手順3:jadで.class→.java変換
jadツールを使用すると.classファイルから.javaファイルが取得できます。

jadツールは下記サイトにあります。
http://www.varaneckas.com/jad
(今回はJad 1.5.8g for Windows 9x/NT/2000 on Intel platformをダウンロードしました)


jad158g.win.zipを解凍します。
(今回はCドライブ直下に解凍しました)
解凍するとjad.exeがあるので確認しましょう。

jad.exeと.classファイルが揃ったらコマンドプロンプトで下記を実行します。
C:\> <jad.exeのパス> -8 -d <出力パス> -s .java -r <.classファイルパス>
例)C:\> C:\jad158g.win\jad.exe -8 -d C:\out -s .java -r C:\yuki\**\*.class

例ではjarファイルを解凍したC:\yuki以下にある全てのclassファイルを変換対象として
います。


手順4:javaファイルの取得
jadコマンドが正常終了すると、出力パスに指定したディレクトリに.javaファイルができ
ていると思います。

これで逆コンパイル完了です。

以上です。

2012/03/07

Android:ナビゲーションバーがアプリ描画領域を狭めることへの配慮


前回のsw<N>dpで記載した"アプリ描画領域"について、もう一歩踏み込んで調査します。

<N>値にはレイアウトに最低限必要なサイズを指定します。
もし、<N>値に画面解像度の一辺(幅or高さ)を指定する場合は下記の点に注意しなければ
いけません。

<N>に指定する値は"アプリが利用できる画面領域"であり"解像度の一辺(幅or高さ)"では
ありません。
例えば、画面解像度が480px×800pxで画面密度がmdpiの端末をターゲットにした場合。
values-h800dpのリソースフォルダを用意すれば、システムがこれを参照することを期待
するかもしれませんが、おそらくこれは参照されません。
なぜなら、解像度で800dpが確保されていてもステータスバーやナビゲーションバーの領域が
アプリが利用できる画面領域を削るので、ほとんどの場合で解像度よりも小さな値となる
からです。

下記の設定値を持つ端末とリソースで動作確認してみます。
【エミュレータA:ナビゲーションバーなし
・解像度:480×800
・画面密度:mdpi(160dpi)
・ステータスバーの高さ:25dp
・ハードキー:搭載


【エミュレータB:ナビゲーションバーあり
・解像度:480×800
・画面密度:mdpi(160dpi)
・ハードキー:非搭載
・ステータスバーの高さ:25dp
・ナビゲーションバーの高さ:48dp


【リソース】
下記のリソースを持つ.apkを作成
res/values-h727dp/*
res/values-h775dp/*
res/values-h800dp/*

端末の解像度はどれも480×800です。
しかし、values-h800dpがリソースとして選択されることはありません。
これは、ステータスバー領域がアプリが利用できる領域を25dp削っているからです。

エミュレータAでは、ステータスバー領域分の25dpを削った残りが、アプリ描画領域(775dp)
になるのでvalues-h775dpのリソースが参照されます。

しかし、エミュレータBではh775dpのリソースは参照されません。
これは、エミュレータBがナビゲーションバーを表示しており、更に48dp削られるからです。
そのため、エミュレータBではvalues-h727dpのリソースが参照されます。

x<N>dpが導入された経緯や目的を考えれば正しい動作と言えます。


●ナビゲーションバーとアプリ描画領域

Android4.0ではナビゲーションバー領域を新設し、バック、ホームおよび最近使用したアプリを
表示するための仮想ボタンをここに表示しています。



Android4.0では ナビゲーションバー は必ず表示されるというわけではありません。
バックやホームなどのボタンをハードキーとして備えている端末では、ナビゲーションバー
の存在は冗長なのでこれをシステム側で非表示に設定します。

ナビゲーションバーはWindowManagerから取得できるDisplayMetrics値に影響します。

例えば、下記のような解像度を取得するコードはよく目にします。
DisplayMetrics metrics = new DisplayMetrics();  
this.getWindowManager().getDefaultDisplay().getMetrics(metrics);     
Log.d("yuki", "widthPixels=" + metrics.widthPixels);
Log.d("yuki", "heightPixels=" + metrics.heightPixels);

metrics.widthPixels, metrics.heightPixelsの戻り値は ナビゲーションバー の有無で
下記のように変化します。

【ナビゲーションバー無】
widthPixels=480px
heightPixels=800px

【ナビゲーションバー有】
widthPixels=480px
heightPixels=752px

DisplayMetrics値がナビゲーションバーの高さ分の影響を受けていることが分かります。
端末のスペックにある解像度とDisplayMetrics値で得られる解像度に差異が出るというこ
とです。

以上です。