読者です 読者をやめる 読者になる 読者になる

Android Studio 2.2 正式版 を一週間触ってみての気づいた挙動メモ(use InstantRun)


総評

  • InstantRun以外は、正直微妙
  • 動作がかえって重くなった

あたりのお話が有るAS2.2ですが、触っていて気になったことをメモしておきます

記述によっては既存動作かもしれません。

過去バージョンからのプロジェクトの互換性

正式版アップデート後、AS自体の挙動が遅い*1/おかしな挙動が発生*2しました。

  • .iml/.ideaフォルダを消してプロジェクトを開き直し
  • File > Invalidate Caches/Restart でAS自体の古い動作キャッシュを消す

で改善


  • テストしていたとしても*3
    • 新規インストール/新規プロジェクト作成

でしかテストしてないんだろうなーという感じもしますね

InstantRun関係

基本挙動に関して

  • JRebelと同じように
    • 基本ベースapk(cleanビルドしたての状態) + 差分データ(カスタムクラスローダ)

の形式のようです。

  • ただJRebelと違うのは
    • 端末上から直接実行できる。IDEからの起動が必須ではない
      • これはJrebel側に質問したことが有るのですけど[端末からの直接起動]は難しい。と言っていたので凄いとは思う*4
    • ただし起動がInstantRun無しapkより遅く、画面が白くなったり赤くなったりするのが数秒発生したりする*5
    • InstantRunを繰り返していると、アプリ内データが鬼のように増えてく

Build Cache 有効化は必須か?

使い方自体は下記参照

cleanビルド率が大分減りましたが、それでもcleanビルドからでは結構遅いです

指定なしと比べて1-2割ぐらい違うので必須かと思います、

ただライブラリ構成とか弄ると、clean build上手く反映されているか挙動が怪しいような気がします*6

ただ

  • ASのIDE自体を再起動
  • プロジェクトを閉じて開き直し
    • Gradle Sync のタイミング? *7

すれば、ちゃんと反映するような感じですが、indexを更新するタイミングで作り直しているのかな?と

InstantRunのHotSwapに関して

Googleの公式Doc等を読んでいたら、

  • HotSwapは minSdkVersion 21 を指定しないと動かない
    • 考えてみれば、内部でMultiDexが自動適応されるから当たり前でしたね(汗
  • MainProcessのアプリのみ適応される
    • android:process 指定とか使ってると駄目*8

な条件でした。

  • app/build.gradle
buildTypes {
    debug{
        //AS上から実行されたときのみ適応☆
        def isIdeBuild = project.properties['android.injected.invoked.from.ide']
        ant.echo "[isIdeBuild]:"+isIdeBuild 
        if("true".equalsIgnoreCase(isIdeBuild)){
            defaultConfig.minSdkVersion 21
        }
    }
}

って対応するのがベストなのかな?

☆ の記述は テクブさんの

  • Android実践プログラミング 現場で生まれた設計パターン
    • 5.34 IDEからのビルドを判別したい

辺りに書かれていた記述ですけど*9

これでInstantRun実行時のときの環境変数がわかればな~と<汗

InstantRunの判定オプション(2016/10/4追記)

def isIdeBuild = project.properties['android.optional.compilation']
ant.echo "[isIdeBuild]:"+isIdeBuild 
  • フルビルドの時

[isIdeBuild]:INSTANT_DEV,FULL_APK

となるよう *10

だから

の判定だとたまに通らないことが有るわけですね。。。(汗

  • app/build.gradle
buildTypes {
    debug{
        //AS上からInstantRunが実行されたときのみ適応☆
        def isIdeBuild = "false"
                def compilation = project.properties['android.optional.compilation'] as String
                if(compilation != null && compilation.indexOf("INSTANT_DEV")!= -1){
               isIdeBuild = "true"
                }
        ant.echo "[isIdeBuild]:"+isIdeBuild 
        if("true".equalsIgnoreCase(isIdeBuild)){
            defaultConfig.minSdkVersion 21
        }
    }
}

と書くのが正しいわけですね(汗

InstantRunの暴走

安定して動くようになったように見える? InstantRunですが、よく暴走します

デバック実行とかプロセスアタッチしていると頻繁に起こります。

  • バイスの転送先から消える
    • 転送先がないのでInstantRunができないとエラーが出まくる
  • 激重になり、USB系のマウスとか使っていたらまず操作ができなくなる

    • 接続が切れたときにすごい勢いでポーリングしているっぽい挙動*11
  • 対処法として

    • USBを全部抜く
    • adb kill-server/start-server しなおす

あたりまでしないと復旧できません。実は結構ストレスフルになったりしますね。この挙動。

ManifestMargerの自体の挙動

  • AndroidManifest.xml 自体はチェックTaskでしかチェックしていない?
    • library-projectに関しても build.gradleに記載がある場合、そちらで上書き
  • build.gradleにベタで minSdkVersion/targetSdkVersion を書いてないと上手く判定されない
  • 上手く判定されないケース
    • project.ext で宣言参照
    • DefaultManifestParserでAndroidManifest.xml から読み込み

この場合、メインプロジェクトで指定した指定がそのまま反映される形になります

で現在推奨されているっぽい書き方

  • library-project側では
    • build.gradle にその手の情報を記載しない
    • AndroidManifest.xml側にその手の情報タグを削る
  • 下記の形で library-projectを参照する
    • 導入するライブラリプロジェクトを [gradlew assembleDebug/Release] 等でaar形式にしてつかう

libP/build.gradle

  • 今風の書き方
configurations.maybeCreate("default")
artifacts.add("default", file('SDKTest-rel-1.01.aar'))
  • 昔の書き方
repositories {
    flatDir {
      dirs 'aars'
    }
}

dependencies {
    compile(name:'SDKTest-rel-1.01', ext:'aar')
}

既存仕様っぽい挙動

databinding

ライブラリ自体が大きいため、使うときにMultiDexが必須となりますが*12

library-projectでDataBindingを使って、そちら側に multi-dexの記述を書いても反映されないようです

あくまでMain側に記載しないと駄目。凄くタルい。。

ただ MainApplication.java とかに

MultiDexs.install(this);

あたりの記述がなくてもビルドが通るようになってしまうのは正直謎。

  • app/build.gradle
android {
    dataBinding {
        enabled = true
    }
    
    defaultConfig {
        if(dataBinding.enabled){
            multiDexEnabled true
        }   
    }
}

dependencies {
    if(android.dataBinding.enabled){
        compile 'com.android.support:multidex:1.0.1'
    }   
}


今更ながら気づいた書き方

Logとかで使うTAGの宣言

  • 昔書いてた書き方
    • 下記を全部のクラスで書いてましたorz
private final String TAG = Hoge.class.getSimpleName();
  • 推奨の書き方(継承元のクラスで)
protected final String TAG = getClass().getSimpleName();

これであれば、継承元の方でログ記述していても、対象クラスのログとして出力できる

ココらへんは便利だなーと

ただ残念なのは。。

staticにしてしまうと使えないんだよな。。。

private final static String TAG = "Hoge";

と書くしか無い。

  • IntentServicesのコンストラクタ
  • InnnerクラスのAsyncTask上でのログタグ用参照とか

厳しいですね。。なんか良い書き方がないものか。。((Java的な書き方かもしれないですけど。。))

*1:Project選択画面をデフォルトにしているのに

*2:たまにビルド時によくわからんエラーが出る

*3:Jenkinsとかで?

*4:その時は 別apk名にするからgradlewコマンドで別途作って別管理してねという回答をいただきましたが

*5:ClassLoaderで動的に読み込んでるっぽいから当たり前?

*6:ここらへんがExperimentalな所以?

*7:このタイミングで反映されているかは怪しい?

*8:Service使用も駄目みたい。まあDebuggerでattachとか出来ないし。。

*9:紙面版は誤記が有るようなので記述は修正した

*10:海外ブログを見ると -Pandroid.optional.compilation=INSTANT_DEV でinstantRunビルドが出来ると有るが。。

*11:アクティビティモニターでCPU200-300とかいく。。

*12:statckOverFlowExceptionが発生