NO_NAME

適当に技術・仕事・ライフハックの話を書いていく予定です。

Google DriveのMacアプリで "Please use https://accounts.google.com/ServiceLogin instead." と出てログインできない場合の対応策

Google DriveのMacアプリをインストールしたのだがログイン時に以下のエラーがでてログインができなかった。

"Please use https://accounts.google.com/ServiceLogin instead."

Can't seem to log in. Keep getting message:" Please use https://accounts.google.com/ServiceLogin instead." - Google Groups を読むと二つの解決法が紹介されている。

  1. 別のGoogleアカウントでログイン、ログアウトをした後、対象のGoogleアカウントでログインする。
  2. Googleアカウントの二段階認証 http://support.google.com/accounts/bin/answer.py?hl=ja&answer=180744 をONにする。

まず1を試してみたが別のGoogleアカウントでも同様のエラーが発生したので断念。

2を試すと見事成功した。
二段階認証は後でOFFにすることができ、OFFにしてもログイン済みのGoogle Driveからログアウトされてしまうということはないので二段階認証が嫌という人もOK。
ただし二段階認証がONの状態で、二段階認証を経ていないログイン済みのアプリやAndroid端末でアクセスしてしまうとログアウトされてしまうので注意が必要。
そうなってしまったアプリや端末でログインし直すためにある程度の時間がある際に実施することを推奨する。

ロック画面の上にActivityを表示する

ロック画面にAppWidgetを表示する方法を調べたがそんな方法は存在しないようだ。
かわりにロック画面の上にActivityを表示することができることがわかったのでその方法について書く。

ロック画面の上にActivityを表示するには以下のようにフラグを付けるだけでよい。

getWindow().addFlags(
	WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
	| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);

ただし、XPeria SO-01Bではうまく行かず、KeyguardManagerのdeperecatedのメソッドを使う必要があった(この 2.3.7 では非deprecatedで、4.0.1 ではdeprecatedであること確認)。

final KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
final KeyguarrdLock keyguardLock = keyguardManager.newKeyguardLock("app" /* アプリ名など */);
keyguardLock.disableKeyguard();

この方法を利用する場合、 android.permission.DISABLE_KEYGUARD パーミッションが必要になるので忘れないように。

ロック画面周りの挙動パターン(おまけ)

ロック画面周りの動きはAndroidのバージョンによって変わったり、Javadoc通りに動かなかったりするので試した限りのパターンを記述しておく。

  • スライド式のロックの場合(Galaxy Nexus)
    フラグとして WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD を指定してもキーガードが無効にならず、別のActivityに移る際にロック画面が表示されてしまう。Javadocでは WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD はセキュリティー付きのキーガードでない場合、キーガードを無効にすると書いてあるので、Javadocに反していると思う。
  • ロックなしの場合(Galaxy Nexus)
    WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD を指定しているとキーガードが無効になってしまっているため、音量ボタンなどでも電源がONになってしまうので注意。
  • パターン式などセキュリティ機能付きのロックの場合(SO-01B)
    WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED や WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD を指定していてもロック画面上にActivityが表示されない。そのため、先ほど述べた方法をとる必要がある。
  • スライド式のロックの場合(S0-01B)
    WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED や WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD がJavadoc通りに動作。

Google Maps APIのx86 system imageを作成する

仮想マシンアクセラレーションによるエミュレータの高速化エミュレータ仮想マシンアクセラレーションによる高速化を紹介したがGoogle Maps API用のx86 system imageは配布されていないので、Google Maps APIを使用したい場合、自分でx86 system imageを作成する必要がある。

作成手順

  1. ベースとなるGoogle Maps APIのAVDを立ち上げる。ベースとするx86 system imageと同じAPIバージョンのものを立ち上げること。
  2. Google Maps APIのライブラリを抜き出す。

    adb pull /system/etc/permissions/com.google.android.maps.xml
    adb pull /system/framework/com.google.android.maps.jar
    

  3. ベースとなるGoogle Maps APIのAVDを落とす。
  4. ベースとなるx86 system imageを使ったAVDを立ち上げる。立ち上げる際には
    -partition-size 128
    を指定するなどし、パーティションサイズを大きくしておかないと次のライブラリのコピー時にOut of memoryというエラーが発生し、失敗するので注意。
  5. Google Maps APIのライブラリをコピー。

    adbremount rw
    adb push com.google.android.maps.xml /system/etc/permissions
    adb push com.google.android.maps.jar /system/framework
    

  6. Yaffs2 tool(system imageを作成するためのツール)をダウンロードする。http://jiggawatt.org/badc0de/android/ のYaffs2 tool: x86 Linux version からダウンロードできる。
  7. AVDからsystem imageを作成する。

    adb push mkfs.yaffs2.x86 /data
    adb shell
    cd /data
    chmod 777 mkfs.yaffs2.x86
    ./mkfs.yaffs2.x86 /system system.img
    exit
    

  8. 作成したsystem imageを抜き出す。なぜか非常に時間がかかる。

    adb pull /data/system.img
    

  9. 作成したsystem imageをAVDのディレクトリにコピーする。
    macの場合、

    ~/.android/avd/
    

    の下にある。

  10. エミュレータを再起動する。

参考

38911 bytes free: How to use Google Maps API in Android emulator SDK version 17http://38911bytes.blogspot.de/2012/03/how-to-use-google-maps-api-in-android.html

仮想マシンアクセラレーションによるエミュレータの高速化

最近のCPUは仮想マシンを走らせるため拡張が組み込まれており、それを利用してAndroidエミュレータを高速化することができる。
詳しくは http://developer.android.com/tools/devices/emulator.html#accel-vm を参照。
先に言っておくが、これを実行してもiPhoneエミュレータほどは速くならないし、起動もそこまで速くならない。
ただ起動後の動作はこれまでのAndroidエミュレータと比べると大分ましになる。

CPUの要件のどちらかの仮想化技術を保持していることなので、Intel CPUの場合はたいていの場合みたいしているはず。

ただしこの方法には4つほど制限がある

  • x86 system imageが必要。現時点(2012/07/27)でAndroid SDK Managerで配布されているのは2.3.3(API 10)と4.0.3(API 15)の二種類のみ。
  • VMWareVirtualBoxなどの仮想マシン内では利用できない
  • VMWareVirtualBoxなどのVMドライバとは同時に使用できない
  • OpenGLのエミュレーションは実デバイスと同じレベルでは実行できないかもしれない。

インストール手順

  1. Android SDK ManagerでSDK Toolsをリビジョン18以上にアップデートする。17でもこの機能は動くが、experimentalとなっているので18以上が良いと思う。
  2. Android SDK ManagerでExtrasにあるIntel Hardware Accelerated Execution Managerをインストールする。Android SDK Managerで落としただけではインストールされないので別途作業が必要。
    プラットフォーム毎に違う手順になるのでMacの場合の作業手順を記述する。
    1. Intel Hardware Accelerated Execution Managerが
      <sdk>/extras/intel/Hardware_Accelerated_Execution_Manager/IntelHAXM.dmg
      にダウンロードされるので実行してインストールする。
    2. インストール後、
      kextstat | grep intel
      を実行し、
      com.intel.kext.intelhaxm
      となっていればインストールに成功している。
  3. Android SDK Managerで動かしたいバージョンのx86 system imageをインストールする。現時点(2012/07/27)では2.3.3(API 10)と4.0.3(API 15)の2バージョンのみ配布されている。
  4. AVD Managerを立ち上げ、x86 system imageを使ったAVDを作成する。Targetに先ほどインストールしたx86 system imageのバージョンを指定すると、CPU/ABIでx86を選択できるようになるので選択する。ただし、TargetにGoogle APIsを指定するとCPU/ABIでx86 system imageを選択できないので注意。
  5. 作成したAVDを実行する。ちなみに初回起動は遅い。

UIライブラリの試用に便利なAndroidアプリ「Android UI Patterns」

UIライブラリのサンプルアプリを詰め込んだ「Android UI Patterns」が便利。
気になる点としてはパーミッションとして以下のものを必要とすることが上げられる。

  • 完全なインターネット アクセス
  • 連絡先データの読み取り
  • USB ストレージのコンテンツの変更/削除、SD カードのコンテンツの変更/削除

それぞれサンプルアプリに利用しているのだろうが(完全なインターネットアクセスは広告表示にも必要なのかと)、私は不安なのでエミュレータで実行している。

Android UI Patterns - Android Apps on Google Play
https://play.google.com/store/apps/details?id=com.groidify.uipatterns

Loader API と AsyncTask の比較

Android 3.0 から Loader API が導入された。
Support Package を利用するとAndroid 1.5以上でもこのAPIを使えるようになる。

Loader API が AsyncTask と立ち位置が重なるように思えたので AsyncTask との比較してみた。

AsyncTaskとの比較しての結論

結論からいうと 読み込み処理では AsyncTask を使うより、こちらの API を利用した方がコードの再利用性、コードの読みやすさ、保守性が高くなりそうだ。
特にデフォルトで存在する CursorLoader が利用できるなら、これを利用した方が良いだろう。

読み込み処理については、

  • 名前が Loader であること
  • Loader#reset() など読み込みにしか利用しないようなメソッドがついている

ことから適さないと思う。

AsyncTaskとの比較

  • AsyncTask#onPostExecute() ではUIの操作を伴うことが多く、そのため特定の Activity や Fragment と密接に絡みあってしまうことになる。 Loader では処理終了時の操作はコールバックに書くことになるので、Loader を特定の Activity や Fragment に依存しない再利用性が高いクラスとして利用することができる。
  • Loader APIでは Activity や Fragment の停止・開始に合わせ、LoaderManager が適宜 Loader の状態を管理するため、AsyncTask 実行でよく問題になる破棄されている Activity にアクセスしてしまいエラーになる問題を避けられる。
    また、Activity 破棄後、実行終えていない AsyncTask が実行されるまでメモリを逼迫するということも起きない。
  • きちんと実装されている Loader はロード対象としたデータが変更された場合、自動的にロードし直す機能がついているなど AsyncTask にはない機能もついている。

(参考)Loader API とは

Activity や Fragment で非同期に容易にデータをロードするためのAPI

Loader は単体で利用することもできるが LoaderManager と合わせて利用することを想定している。
LoaderManager は Activity や Fragment で利用されることを想定しており、Loader も基本的には Activity や Fragment で利用することになる。

Proguardでコードの圧縮・最適化・難読化を行う

Android SDK には Proguard が組み込まれており、コードを圧縮・最適化・難読化できる。
特にライセンス認証を行っていたり、コア技術が入っていたりする場合、難読化は必須。

Androidプロジェクトに Proguard を適用する際にわかったことを記述する。

Androidプロジェクトで Proguard を利用する方法

<project_root>/project.properties ファイルに以下のように proguard.configプロパティで proguard の設定ファイルを指定するとリリースビルド作成時(Eclipse、Antどちらにも適用される)に自動で Proguard が適用される。

proguard.config=proguard.cfg

Progurad への入力

Proguard へは通常以下のファイルを入力として与える必要がある。
ただし Android SDK でのビルドで自動的に Proguard が適用される場合、自動で設定される。

  1. 加工するクラスファイル(Jar, wars, ears, zips, ディレクトリなどで指定)
    • プロジェクトのクラスクラスファイル
    • プロジェクトが依存しているライブラリのクラスファイル
      通常のプロジェクトではライブラリは 2 のほうに分類されるが、Android プロジェクトではビルド時にライブラリは分解されプロジェクトのクラスファイルと混ざるため。
  2. 加工するクラスファイルが利用しているライブラリ(ライブラリ自体は変更されない)
    • target に指定されている Android Framework

エントリーポイントの指定

クラス・メソッドの呼び出し階層を自動でたどれないような箇所はエントリーポイントに指定する必要がある。
エントリーポイントは Proguard で変更されない。
エントリーポイントとして指定されるのは主に外部との接合部分になる。
たとえば次のような箇所をエントリーポイントとして指定する。

  • リフレクション・イントロスペクションを直接的・間接的(ライブラリやフレームワークを通して)に利用しているケース
  • ダイナミックProxyを使っているクラス
  • シリアライズ・デシリアライズされ永続化されるSerializableクラス

リフレクション・イントロスペクションの自動検出・対応

ダイナミックにオブジェクトの生成やメソッド呼び出しが実行されるので基本的には Proguard で自動対応出来ず、エントリーポイントで指定する必要がある。
しかし以下のようなケースは自動で検出・対応される。

  • Class.forName("SomeClass")
  • SomeClass.class
  • SomeClass.class.getField("someField")
  • SomeClass.class.getDeclaredField("someField")
  • SomeClass.class.getMethod("someMethod", new Class[] {})
  • SomeClass.class.getMethod("someMethod", new Class[] { A.class })
  • SomeClass.class.getMethod("someMethod", new Class[] { A.class, B.class })
  • SomeClass.class.getDeclaredMethod("someMethod", new Class[] {})
  • SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class })
  • SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class, B.class })
  • AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, "someField")
  • AtomicLongFieldUpdater.newUpdater(SomeClass.class, "someField")
  • AtomicReferenceFieldUpdater.newUpdater(SomeClass.class, SomeType.class, "someField")

さらに (SomeClass)Class.forName(variable).newInstance() のようなケースでは SomeClass とそのサブクラスの圧縮・最適化・難読化 を防止したほうがよいことを検出・提案してくれる。

設定例

Androidプロジェクトでの Proguard の設定例を記載する。
わかりやすいように日本語でコメントを書いてあるが、日本語でコメントを書くとうまく動かなくなることがあるので避けた方が良い。
プロジェクトのパッケージ名は com.example ということにしておく。
ProGuard Examples も参考に。

# =======================
# Android SDKにより自動生成される部分
# =======================
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses 
-dontpreverify # Dexコンパイラでは事前検証は意味を持たないので不要
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # Dalvik VMが対応していない最適化を行わない

# AndroidManifest.xml に記載されるような、コンポーネントはクラス名を変更しない
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService

# nativeメソッドの名前はネイティブコードへ合わせる必要があるので変更しない
-keepclasseswithmembernames class * {
    native <methods>;
}

# layoutファイルに記載されるようなクラス名を変更しない。
-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

# menuファイルに記載されるようなメソッドがある場合、クラス名、メソッド名を変更しない。
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# =======================
# 独自に付け足した部分
# =======================

# ライブラリは詳しく把握していないため、
# Proguard の影響が想像つかないため避けておく。
# ライブラリ部分をきちんと把握しており、
# また Proguard の効用を限界まで高めたい場合はこれは必要ない。
-keep class !com.example.** {
    *;
}

# JNI で利用されているクラスをエントリーポイントとして指定する。
# nativeメソッドだけがネイティブコードから利用されている場合は、
# 上記にあるnativeメソッドをエントリーポイントとして指定するだけで良いが、
# クラス内のフィールドをネイティブコードから利用している場合、
# そのフィールドもエントリーポイントに指定する必要がある。
-keep class com.example.NativeClass {
    *;
}

# アノテーションを必要とするライブラリがある場合には
# アノテーション属性を削除しないように指定する。
# これを指定しないとクラスファイルについた属性は全て削除される。
# エントリーポイントとして指定した部分も例外ではない。
# Jacksonライブラリなど、この指定がないと実行時にエラーが発生する。
-keepattributes *Annotation*

# 以下の指定をすると Proguard の最中、次のクラスが見つからなくてもエラーを出さない。
# ライブラリ中の利用していない箇所から参照されているクラスでエラーが出た場合、指定すると良い。
# ちなみにこれはJacksonライブラリへの対応。
-dontwarn org.w3c.dom.bootstrap.**
-dontwarn org.joda.**

注意点

日本語コメントを書くとうまく動かなくなることがあるので日本語は使わない方が良い。
最悪のケースではビルドは通るが、ビルドされたアプリの実行中にエラーがでる。

おまけ:Proguard の実行ステップ

Proguardの実行ステップは以下の4つからなる。

  • 圧縮ステップ
    エントリーポイントして指定された場所からスタートして利用しているクラスやクラスメンバーを確定し、利用されていないクラス、クラスのメンバーを削除する。
  • 最適化ステップ
    エントリーポイントでないクラスやメソッドを private, static, final にしたり、利用されていないパラメータを削除したり、インライン化する。
  • 難読化ステップ
    エントリーポイントでないクラスやメソッドをリネーム化する。リネーム結果にあわせてオプションとして加工する Jar に含まれているリソースのファイル名やコンテンツを変更することも可能。
  • 事前検証ステップ
    事前検証情報をクラスにつける。 JavaMEでは必須、Java6だと起動時間の短縮になる。