KonifarPod

EclipseからAndroid Studioへののせかえ手順まとめ

   

Pocket

Android開発するならAndroid Studioという空気が広まってきたので、Eclipse+ADTプラグインで作って開発していたプロジェクトをAndroid Studioにのせかえてみました。結構苦労したので、手順をメモしておこうと思います。

 

(1)現状の確認

元は以下のような環境でした。

  • コードはgitで管理。
  • クラス数は300個くらい。
  • mavenもgradleも不使用。ライブラリは/libraryディレクトリにまるごと入れて、Eclipseでプロジェクトを作ったあとインポート。
  • lib、library合わせて依存ライブラリの数は18個。

git管理はしてるものの、クラスがそれなりに多いのに依存関係ぐちゃぐちゃで、EventBusとか便利ライブラリ入れていきたいのにちょっと戸惑うなぁという感じでした。俺だってgradleでライブラリ管理したりコンソールから環境ごとにビルドしたりしたいよ!という思いを胸に秘めながら開発していたわけです。

 

(2)Android Studioのインストール

まずはAndroid Studioをインストールします。2014/09時点では0.8.6でした。インストールするとAndroid SDKも一緒に入っています。起動するとAndroid SDKのパスはAndroid Studio.app/sdk/ になっていますので、変えたければ『Android StudioにおけるAndroidSDKやJDKのパス設定』を参考に後で変えてください。まぁ変えなくてもいいと思いますが。

 

(3)プロジェクトの新規作成

EclipseからAndroid Studioへのインポートは、Eclipseのエクスポート機能を使えば簡単に行えます。詳しくはこの記事を参考にしてください。試しに簡単なプロジェクトでやってみたらスルッとインポートできました。

しかし実際に本番のプロジェクトでやってみると、エラーが出まくって動きませんでした。ライブラリたくさん使った大きめのプロジェクトだとうまくいかないこともあるようです。しかもプロジェクト構成はそのままなので、gradleのプロジェクト構成になっていません。これだと結局プロダクトフレーバーといったAndroid Studioの利点が享受できません。

いっそのことgradleプロジェクトを新規作成してbuild.gradleを自分で頑張って書いてコードとリソースをコピってきた方が早そうだったので、新規作成することにしました。新規作成はAndroid Studioのダイアログにしたがっていけば迷わずできました。

なんかよくわからない変な感じになったらアンインストールしてやり直した方がいいかもしれません。アンインストールのやり方はこちら

 

(4)build.gradleを記述

ここが一番の鬼門でした。手順としてはこんな感じ。

  1. 依存ライブラリをGoogleで調べてgradleの記述方法を見つけて記述
  2. gradle syncしてみる
  3. エラーが出たらエラー解消、出なければインポートしたライブラリが正しいかを確かめる

例えば、Aviaryというライブラリのgradleの記述を『aviary sdk gradle』でGoogleで調べてみると、公式ページにgradleサポートの情報が載っています。サンプルを落としてreadmeを見ると、gradleの記述方法が書いてありました。Aviaryの場合はこんな感じです。

dependencies {
    compile ('com.aviary.android.feather.sdk:aviary-sdk:3.4.3.351')
}

syncする時は、AndroidStudio上部のバーのボタンをクリックします。

Android studio migration3 

エラーが出たら、メッセージにエラーが表示されます。

Android studio migration4

 

この作業を繰り返していくだけ、というとそれまでなんですが、それはもう色々とエラーが出まくりました。以下、ハマったエラーの対処方法まとめです。

AndroidStudioでローカルの別プロジェクトをインポートする

GooglePlayServicesがgradleでsyncできない時の対処法

gradle sync時にError:No such property: GROUP for class…エラー

 

結局出来たbuild.gradleはこんな感じです。

apply plugin: 'com.android.application'

// load release properties
Properties props = new Properties()
if (rootProject.file("release.properties").exists()) {
    props.load(new FileInputStream(rootProject.file("release.properties")))
} else {
    println "Please create release.properties in project root."
}

android {
    compileSdkVersion 19
    buildToolsVersion '19.1.0'

    defaultConfig {
        applicationId 'com.konifar'
        minSdkVersion 10
        targetSdkVersion 19
        versionCode 1
        versionName '1.0.0'
    }
    productFlavors {
        development {
        }
        staging {
        }
        production {
        }
    }

    signingConfigs {
        release {
            storeFile rootProject.file("konifar.keystore")
            storePassword props.storePassword
            keyAlias props.keyAlias
            keyPassword props.keyPassword
        }
    }
    buildTypes {
        debug {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
    lintOptions {
        checkReleaseBuilds false
        abortOnError false
    }
    packagingOptions {
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE.txt'
    }
    dexOptions {
        jumboMode true
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile project(':libraries:pulltorefresh')
    compile project(':libraries:facebook')
    compile project(':libraries:pagerslidingtabstrip') // customized for taptrip
    compile ('com.aviary.android.feather.sdk:aviary-sdk:3.4.3.351') {
        exclude module: 'support-v4'
    }
    compile ('com.android.support:appcompat-v7:19.+') {
        exclude module: 'support-v4'
    }
    compile ('com.nostra13.universalimageloader:universal-image-loader:1.9.1') {
        exclude module: 'support-v4'
    }
    compile ('com.mobeta.android.dslv:drag-sort-listview:0.6.1') {
        exclude module: 'support-v4'
    }
    compile ('com.jakewharton:butterknife:4.0.1')
    compile ('net.danlew:android.joda:2.3')
    compile ('com.google.code.gson:gson:2.2.4')
    compile ('com.google.android.gms:play-services:4.2.42')
    compile ('commons-validator:commons-validator:1.4.0')
    compile 'com.bugsense.trace:bugsense:3.6'
    compile 'de.greenrobot:eventbus:2.2.1'
}

 

(5)コードとリソースをコピー

gradle syncして依存ライブラリをダウンロードできたら、Eclipseのプロジェクトのコードとリソースを新しいgradle プロジェクトにコピーします。gradleプロジェクトだと app/src/main/java以下にコードを配置します。リソースはapp/src/main/res以下です。

これはただコピーするだけで大丈夫でした。lintの設定が違う場合は少しエラーが表示されるかもしれません。(4)でインポートしたライブラリが間違っていた場合はここでエラーが大量発生するので、頑張ってエラーを解消しましょう。

ちなみにbutterknifeは、Android StudioだとEclipseと違って面倒なビルドオプションの設定なく使えるので超楽です。

 

(6)ビルドして実行

いよいよビルドして実行です。ここでも色々ハマりました。以下、対処方法まとめです。

Android Studioビルド時に Duplicate files copied エラー

Android Studioでgradleビルドエラー

Android Studioでビルド時にCannot merge new index xxxx into a non-jumbo instruction!

build.gradleからsigningConfigsの情報を別ファイルを切り出す

 

その中でも、一番の難関はこのエラー。

gradleでビルド時にjava.lang.IllegalArgumentException: method ID not in [0, 0xffff]: 65536

Android2でインストール時にINSTALL_FAILED_DEXOPTエラー

ライブラリを含めたメソッド数が65536個以上だとビルドやインストール時にエラーになるというもの。これを解決するためにはProguardを使って不必要なメソッドを入れないようにしたりしないとダメみたいだったので、Proguardを記述することにしました。

 

(7)Proguardを記述

ビルド時にProguardを使うには、build.gradleのbuildTypesにrunProguard trueを記述します。

buildTypes {
    debug {
        runProguard true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

app/proguard-rules.pro には、proguardの設定を記述します。

何も指定しないと難読化してはいけないコードまで難読化されてしまって、まったく動かなくなります。keepオプションなどを使っていい感じに除外していく必要があります。例えば、gsonを使う場合は実行時にこんなエラーが出てしまいます。⇒ Proguardを使っている時のgsonのエラー

最終的にこんなproguard-rules.pro を書きました。keepオプションなんかはまだまだ超適当です。 本当はもっとちゃんと書かないとダメです。他のプロジェクトのproguardファイルとか見せてもらいたいです。

# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/konifar/AndroidSDK/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

-dontwarn com.konifar.**
-dontwarn org.apache.**
-dontwarn javax.**
-dontwarn butterknife.**
-dontwarn com.androidquery.**
-dontwarn com.squareup.okhttp.**


-keep class com.konifar.** { *; }
-keep interface com.konifar.** { *; }
-keep class com.facebook.** { *; }
-keep interface com.facebook.** { *; }
-keep class org.apache.** { *; }
-keep interface com.androidquery.** { *; }
-keep class com.androidquery.** { *; }
-keep interface com.google.** { *; }
-keep class com.google.** { *; }
-keep interface java.** { *; }
-keep class java.** { *; }
-keep interface org.json.** { *; }
-keep class org.json.** { *; }
-keep interface android.** { *; }
-keep class android.** { *; }
-keep interface com.aviary.** { *; }
-keep class com.aviary.** { *; }


##---------------Begin: proguard configuration common for all Android apps ----------
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
-dump class_files.txt
-printseeds seeds.txt
-printusage unused.txt
-printmapping mapping.txt
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

-allowaccessmodification
-keepattributes *Annotation*
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
-repackageclasses ''

-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
-dontnote com.android.vending.licensing.ILicensingService

# Explicitly preserve all serialization members. The Serializable interface
# is only a marker interface, so it wouldn't save them.
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

# Preserve all native method names and the names of their classes.
-keepclasseswithmembernames class * {
    native <methods>;
}

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

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

# Preserve static fields of inner classes of R classes that might be accessed
# through introspection.
-keepclassmembers class **.R$* {
  public static <fields>;
}

# Preserve the special static methods that are required in all enumeration classes.
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}
##---------------End: proguard configuration common for all Android apps ----------


##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }
##---------------End: proguard configuration for Gson  ----------

 

(8)変更をgit push

うまくいけばプロジェクトを実行できるはずですが、コードのディレクトリなどがガラッと変わってしまうことでgitの履歴が消えてしまうのは嫌ですよね。

なので、今まで作った構成を参考に元のgit プロジェクトを同じ構成に作り直すことにしました。具体的には、git mvコマンドを使って(5)の作業をやり直します。git mvを使うと、変更履歴を保持したままファイル位置を変更することができます。

gitプロジェクトを同じ構成に変更できたら、Android Studioにインポートして動くかどうか見てみましょう。

 

 

ざっくりですが、こんな流れでのせかえ完了しました。

やってみた後に振り返るとこれだけだっけ?という感じですが、実際に手探りでやってみると結構大変でした。これからのせかえを検討している方々の参考になれば幸いです。

 

Pocket

 - Develop ,