Android Studioで ExecutionException: java.lang.OutOfMemoryError: GC overhead limit exceeded
環境
Android Studio 3.0.1 Build #AI-171.4443003, built on November 10, 2017 JRE: 1.8.0_152-release-915-b08 x86_64 JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o Mac OS X 10.12.6
Java 8
現象
とあるプロジェクトにライブラリ追加したりコード書いたりしてビルドしたら以下のエラーが出てビルドできなくなった。
Information:Gradle tasks [clean, :app:assembleStaging] Error:Uncaught translation error: java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: GC overhead limit exceeded Error:Uncaught translation error: java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: GC overhead limit exceeded Error:Uncaught translation error: java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: GC overhead limit exceeded Error:Uncaught translation error: java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: GC overhead limit exceeded Error:4 errors; aborting Error:Execution failed for task ':app:transformClassesWithDexForStaging'. > com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: com.android.ide.common.process.ProcessException: Error while executing java process with main class com.android.dx.command.Main with arguments {--dex --num-threads=4 --multi-dex --main-dex-list /Users/xxxxxx/dev/xxxxxxx/app/build/intermediates/multi-dex/staging/maindexlist.txt --output /Users/xxxxxxxxxxxxx/app/build/intermediates/transforms/dex/staging/folders/1000/1f/main /Users/xxxxx/dev/xxxxxxxx/app/build/intermediates/transforms/jarMerging/staging/jars/1/1f/combined.jar} Information:BUILD FAILED in 2m 25s Information:6 errors Information:0 warnings Information:See complete output in console
ビルドで OutOfMemoryError
だと...?
そんな重いことしてないはず。
RxJava, RxAndroid, Retrofitライブラリを追加しただけなのに!
ググる
情報でた
解決方法
app/build.gradleに以下を追加してメモリを抑える
android { dexOptions { javaMaxHeapSize "4g" } }
ビルドしてみると、、、ビルド通った!
ビルド完了!!
めでたしめでたし
PM2でnodejsアプリを動かす
foreverは使ったことあるが、pm2の方が高機能という話を聞いたので使ってみる。
PM2のインストール
グローバルインストールする
npm i -g pm2
pm2コマンドが認識されていればインストールOK
PM2を使う
開始するだけなら
pm2 start jsファイル
--name
で名前つきでスクリプトを起動すると便利
というか、名前がないと操作が不便すぎるので必須だと思う
slack-botという名前で起動する
$ pm2 start main.js --name slack-bot [PM2] Starting /home/pi/dev/slack-bot/main.js in fork_mode (1 instance) [PM2] Done. ┌───────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├───────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤ │ slack-bot │ 0 │ fork │ 1432 │ online │ 0 │ 0s │ 12% │ 20.0 MB │ pi │ disabled │ └───────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘
再起動
pm2 restart アプリ名
$ pm2 restart slack-bot Use --update-env to update environment variables [PM2] Applying action restartProcessId on app [slack-bot](ids: 0) [PM2] [slack-bot](0) ✓ ┌───────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├───────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤ │ slack-bot │ 0 │ fork │ 1480 │ online │ 1 │ 0s │ 66% │ 15.1 MB │ pi │ disabled │ └───────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘ Use `pm2 show <id|name>` to get more details about an app
いけたっぽい
停止する
pm2 stop アプリ名
$ pm2 stop slack-bot [PM2] Applying action stopProcessId on app [slack-bot](ids: 0) [PM2] [slack-bot](0) ✓ ┌───────────┬────┬──────┬─────┬─────────┬─────────┬────────┬─────┬────────┬──────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├───────────┼────┼──────┼─────┼─────────┼─────────┼────────┼─────┼────────┼──────┼──────────┤ │ slack-bot │ 0 │ fork │ 0 │ stopped │ 1 │ 0 │ 0% │ 0 B │ pi │ disabled │ └───────────┴────┴──────┴─────┴─────────┴─────────┴────────┴─────┴────────┴──────┴──────────┘ Use `pm2 show <id|name>` to get more details about an app
stoppedになってる
ログを見る
pm2 logs アプリ名
$ pm2 logs slack-bot [TAILING] Tailing last 15 lines for [slack-bot] process (change the value with --lines option) /home/pi/.pm2/logs/slack-bot-error-0.log last 15 lines: ...
モニタリング
起動しているアプリのログやプロセスの確認ができる。
pm2 monit
見やすくて少しカッコいい
設定ファイルを用いた起動指定
YamlとJsonで起動オプションを設定ファイルに定義できる。
今回はYamlで作成してみる。
# ファイル名:pm2config.yml(ファイル名は自由です) name: slack-bot # アプリ名 script: main.js # スクリプトファイルパス watch: true # フォルダやサブフォルダ内のファイルが変更された場合、アプリは再読み込みされます log-date-format: "YYYY-MM-DD HH:mm Z" # ログに日付を追加
設定ファイルで使える項目は以下リンク http://pm2.keymetrics.io/docs/usage/application-declaration/#attributes-available
設定ファイルで起動する
pm2 start pm2.yml pm2 stop pm2.yml pm2 restart pm2.yml
参考にしたリンク
「アイデアは考えるな」を読んだ感想
書籍
アイデアは考えるな 面白法人カヤック代表 柳澤大輔 http://amzn.asia/eyJUrs7
- 会社の本棚にあったので何気なく読んだら、すごく面白くて一気に読み切ってしまった。
- PM、エンジニア、デザイナー問わず読める本。
アイデアは質より量
- すごいアイデアは誰にでも出せるわけではない、まずはすごくないアイデアをたくさん出そう。
- すごいアイデアを1つ出すのではなく、すごくないアイデアを10出そう
- すごくないアイデアを出していけばだんだんとポジティブになり、アイデア出しが面白くなってくる、そこが狙い
なんでも乗っかれ
- なんでもとにかく目の前に現れたものに悩ますに乗っかろう、自分の判断はあてにならない、自分フィルターを外そう。
- 乗っかったら面白がれるポイントを探そう(ポジティブに)、視点を変えよう、それが意外な発見につながり、斬新なアイデアに繋がる。
- ポジティブでなければアイデアはたくさん出せない。
- アイデアとは既存の要素の組み合わせ以外何ものでもない
- 乗っかるからこそヒントを得てヒットを生み出せる
- アイデアを出すと気には締め切りは絶対に必要、締め切りとは人間の力を目一杯引き出す装置。
- 情報を集めたほうが格段にアイデアを出しやすい、情報を集めるなら本を読む、ネットで集める、人に会って聞くなど自分にあった方法を見つけろ。
発想法
1人でアイデア出しの練習にはマンダラチャートが良い
ブレスト
- とにかくアイデアの量を出す
- とにかく相手を否定しない
- とにかく相手の意見に乗っかる
ブレストの人数は5〜7人くらいがちょうどいい、それ以上だと発言の量などによる効率が悪い
Android Studio 3.1.1でSyncエラー Data Binding annotation processor version needs to match the Android Gradle Plugin version. You can remove the kapt dependency com.android.databinding:compiler:3.1.0 and Android Gradle Plugin will inject the right version.
タイトルが長すぎるけど気にしない。
Android Studio 3.1.1にアプデしたらSyncエラーが出た。
Data Binding annotation processor version needs to match the Android Gradle Plugin version. You can remove the kapt dependency com.android.databinding:compiler:3.1.0 and Android Gradle Plugin will inject the right version.
日本語訳
データバインディングアノテーションプロセッサのバージョンは、Android Gradle Pluginのバージョンと一致する必要があります。 あなたはkapt依存関係com.android.databinding:compiler:3.1.0を削除することができ、Android Gradle Pluginは適切なバージョンを注入します。
修正方法
メッセージ内容を読むと分かる通り、
以下のapp/build.gradeにあるandroid.databinding:compilerのバージョンをAndroid Gradle PluginのGradleバージョンと合わせれば直る。
Android Gradle PluginのGradleバージョン
dependencies { classpath 'com.android.tools.build:gradle:3.1.1'
3.1.1だね
app/build.gradeにあるandroid.databinding:compiler
kapt 'com.android.databinding:compiler:3.1.0' <- 古い!!
3.1.0かー、ちょっと古いね
こいつを3.1.1にあげよう
kapt 'com.android.databinding:compiler:3.1.1'
これでSyncもビルドも通った。
でも今までkaptもandroid.databinding:compilerもなんとなく使ってきたので、こいつらは一体なんなのか軽く調べた。
kaptとは?
KotlinからJavaのコードを利用可能にするモノらしい。
android.databinding:compilerとは?
以下リンクを見ると、コンパイル時にLayout xmlからBindingのコードを吐き出すものらしい。
つまり?
kapt 'com.android.databinding:compiler とは
DataBindingが定義されたLayout XMLをコンパイル時に生成されるコードを、Kotlinで利用可能にするモノのようだ
Androidで処理時間を計測するならSystem#currentTimeMillisよりもSystemClock#uptimeMillis
タイトルまんまの内容。
メソッドの処理時間を計測したかった
Androidで処理時間を計測しようとSystem.currentTimeMillis()
を使ってたら、SystemClock.uptimeMillis()
のがいいよとアドバイスをもらった。
なぜか?
System.currentTimeMillis()
の場合、端末時間をユーザーが変更できたりOSの時刻補正によって現在時間がかわる恐れがあるため
SystemClock.uptimeMillis()
は端末ブートしてからの時間を記録してるので、時間に変更が入る心配がない
ただし、SystemClock.uptimeMillis()
はディープスリープに入ると止まるため、長い計測を行う場合には注意が必要だ。
参考リンク
Yukiの枝折: 経過時間を求める方法 http://yuki312.blogspot.jp/2011/11/blog-post_30.html
Android KotlinでRealmを使う
環境
- Android Studio 3.0.1
- kotlin_version = '1.2.21'
- Realm 5.0.0
資料
Realm公式 Realm: Create reactive mobile apps in a fraction of the time
Realmの導入
プロジェクトのbuild.gradleにrealmを追加
buildscript { ext.kotlin_version = '1.2.21' repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.realm:realm-gradle-plugin:5.0.0" //追加 } }
appのbuild.gradleにrealmを追加
ここはrealmを最後の行に書かないとエラーになるはず
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'realm-android' //追加
Realm初期設定
Realm.init
これはアプリ起動中に1回行うだけでいいので、カスタムアプリケーションクラスでやるのがオススメ
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); Realm.init(this); }
AndroidManifestにカスタムアプリケーションを登録するのを忘れずに
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ikemura.com.kotlinrealmmigration" > <application + android:name=".MyApplication" android:allowBackup="true"
Realm使用
インスタンス化
いよいよ画面でrealmを使えるようになった
lateinit var realm: Realm private fun initRealm() { val realmConfiguration = RealmConfiguration.Builder() .deleteRealmIfMigrationNeeded() .schemaVersion(0) .build() realm = Realm.getInstance(realmConfiguration) }
Insert
Transaction化しないとIllegalStateExceptionが発生する
realm.executeTransaction { realm.insert(Book(1,"first")) }
Read
titleが「first」と一致する値をbook
に取得する
var book = realm.where(Book::class.java) .equalTo("title","first") .findAll() Log.d(TAG,book.toString())
equalTo
:カラム名, 値 の比較を行える
findAll
:クエリ条件を満たすすべてのオブジェクトを同期検索する
Delete
削除もTransaction化しないとIllegalStateExceptionが発生する
//削除対象を取得 var book = realm.where(Book::class.java) .equalTo("title","first") .findAll() //削除 realm.executeTransaction { book.deleteAllFromRealm() }
nowで作成したAPIをKotlinとRetrofitとRxJavaとJacksonでGET/POSTする
いつもAndroidでRetrofit+RxJavaを使ってAPI通信する処理を書いてるが、今回はKotlinで挑戦してみる。
ついでに、使ったことのないnowもJacksonも触ってみた。
作成するもの
環境
- macOS Sierra
- kotlin_version = '1.2.21'
- Android Studio 3.0.1
- node v9.4.0
API
準備
APIモックをexpressを使ったnodejsで作成し、nowにデプロイしていく。
nowについてはこの記事を参考にした。
以下、作業ログ
// APIモックのプロジェクト準備 $ mkdir try-now && cd try-now // package.json作成 $ npm init -y // expressを追加 $ npm i -S express // apiの処理を書くjsを作成 $ touch index.js // nowのインストール $ npm i -G now // nowのログイン(メールアドレスを入力すると確認メールが届くのでverifyする) $ now login
実装
index.jsに以下を貼り付ける。
レスポンス変数は適当に作った。
var express = require('express') var app = express() app.get('/', function (req, res) { const json = { status: 1, card_status: 10, card_type: 'normal', card_id: "ca1111", } res.json(json) }) app.post('/', function (req, res) { const json = { status: 2, card_status: 20, result_code: 1 } res.json(json) }) app.listen(3000)
ポートは3000を指定してるけど、なくても良い。
これでAPIモックはできあがった。
$ node index.js
としてローカルでも使えるが、今回はnowにデプロイする
デプロイ
nowでデプロイ、超簡単
$ now
https://try-now-kplvmhijbn.now.sh
というアドレスが表示されたので、これをAPIモックとして利用できる。
このアドレスに対してgetやpostをしていく。
Androidアプリ
使用するライブラリ
app/build.gradleに以下のライブラリ達を追加する
// Retrofit2 implementation 'com.squareup.retrofit2:retrofit:2.3.0' implementation 'com.squareup.retrofit2:converter-jackson:2.3.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' // RxJava2 implementation "io.reactivex.rxjava2:rxjava:2.1.9" // RxAndroid implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' // Jackson implementation 'com.fasterxml.jackson.core:jackson-core:2.9.4' implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.4' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.4' implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.4"
ソース:https://github.com/banbara23/try-now
実装
Retrofitのインタフェースを作成。
戻り値はRxjavaのObservableにする。
interface ApiService { @POST("/") fun post(): Observable<PostResponse> @GET("/") fun get(): Observable<GetResponse> }
APIクライアントを作成していく。
今回、baseUrlにはnowで作成したurlを設定する。
class ApiClient { var apiService: ApiService init { val retrofit: Retrofit = Retrofit.Builder() .baseUrl("https://try-now-kplvmhijbn.now.sh") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(JacksonConverterFactory.create(ObjectMapper())) .build() apiService = retrofit.create(ApiService::class.java) } /** * GETリクエスト */ fun get(): Observable<GetResponse> = apiService.get() /** * POSTリクエスト */ fun post(): Observable<PostResponse> = apiService.post() }
APIクライアント呼び出しは以下
ラムダ初めて触った、すごい
// get apiClient.post() .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe( { res -> Log.d(TAG, res.toString()) }, { error -> Log.e(TAG, "{$error.message}") }, { Log.d(TAG, "post completed") } ) // post apiClient.post() .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe( { res -> Log.d(TAG, res.toString()) }, { error -> Log.e(TAG, "{$error.message}") }, { Log.d(TAG, "post completed") } )
モデルについては、 Response(共通レスポンス)を継承した各モデルが存在するイメージで作成した。
/** * 共通レスポンス */ open class Response( @field:JsonProperty("status") val status: Int = 0, @field:JsonProperty("card_status") val cardStatus: Int = 0 )
/** * GETレスポンスのモデル */ data class GetResponse( @field:JsonProperty("card_type") val cardType: String = "", @field:JsonProperty("card_id") val cardId: String = "" ) : Response() { override fun toString(): String = "GetResponse(status='$status' cardStatus='$cardStatus' cardType='$cardType', cardId='$cardId')" }
/** * POSTレスポンスのモデル */ data class PostResponse( @field:JsonProperty("card_type") val cardType: String = "", @field:JsonProperty("card_id") val cardId: String = "", @field:JsonProperty("result_code") val resultCode: Int = 0 ) : Response() { override fun toString(): String = "PostResponse(status='$status' cardStatus='$cardStatus' resultCode='$resultCode')" }
この継承してるあたり、もうちょいうまくできないだろうか。
ビルドして実際に動作させると、Logcatに以下のログが表示されていればGET/POSTが成功している
getのログ D/AppCompatActivity: GetResponse(status='1' cardStatus='10' cardType='normal', cardId='ca1111') D/AppCompatActivity: get completed postのログ D/AppCompatActivity: PostResponse(status='2' cardStatus='20' resultCode='1') D/AppCompatActivity: post completed
ソース:https://github.com/banbara23/Kotlin-Jackson-Sample
ハマったところ
Jacksonが@JsonPropertyを認識してくれない
APIレスポンスをJacksonでシリアライズする際、共通レスポンスモデルであるResponse.ktがKotlin用のアノテーションでないとエラーになる。
Jacksonが継承元の変数を@JsonPropertyだと認識してくれなかった。
継承先は@JsonPropertyでも大丈夫なのに…
NG : @JsonProperty
OK : @field:JsonProperty
実際のコード
/** * 共通レスポンス */ open class Response( // NG //@JsonProperty("status") val status: Int = 0, //@JsonProperty("card_status") val cardStatus: Int = 0 // OK @field:JsonProperty("status") val status: Int = 0, @field:JsonProperty("card_status") val cardStatus: Int = 0 )
同様の問題が起きてないか調べたところ、以下のリンクに辿り着き、kotlinアノテーションを使うことで解決できた。 - https://github.com/FasterXML/jackson-module-kotlin/issues/56#issuecomment-321932309 - https://github.com/FasterXML/jackson-module-kotlin/issues/98#issuecomment-352016501
感想
nowが楽すぎてすごい
kotlinだとJavaよりコード量少なくて楽で良い