プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

Android Studio が重い

Android Stduio でアプリの改修作業を行っていたら、ある時突然 CPU 使用率が80%~90%超で推移して、フリーズするようになってしまいました。

結論としては・・・修正したコードに無限ループが含まれていたことが原因だったようです(トホホです)。

まあ、それがわかるまでに結構時間を使ってしまったので、一応やったことメモに残しておきます。

  • OS: WIndows 11 22H2
  • Android Studio : 2021.3.1 Dolphin Patch 1

異常発生

何も意識せずに、とあるプロジェクトの修正作業を行っていたところ、急に Android Studio が重くなり、フリーズしてしまいました。当然、何の操作もできなくなりました。

仕方ないので、何が起きたんだ?と思いつつ、タスクマネージャで落としてから、もう一度立ち上げても、しばらくすると同じ現象になります。

今度は、タスクマネージャをよく見ると、 CPU 使用率が非常に高いままになって、一向に下がりません。

どうも、何かトラブったようです。

やったこと

手始めにこんなことをやりました。

  • Android Studio の再起動
  • PC の再起動

これでも改善しません。プロジェクトを壊してしまったかな?と思ったので、一度、別のプロジェクトを開いてみます。なんと、問題なく開けますね。どうも、プロジェクト依存の問題のようです。

ということで、

  • プロジェクトのバックアップコピーをとっておき、 build, .idea, .gradle あたりをディレクトリごと削除して、Android Studio でオープン

としてみました。これやったとき、一時的に治ったかに見えたのですが、結局作業を始めると同じ現象になってしまいました。

そうこうするうちに、画面上に Analyzing... とでて止まっているのに気が付いたので、それでググると、似たような現象の記事がありました。

kotlin - Android Studio stuck at "Analyzing..." - Stack Overflow

それによると、Android Studio のキャッシュを消せ、とのこと。なので、

  • C:\Users\ユーザー名\AppData\Local\Google\AndroidStudio2021.3\cache を削除

とやってみましたが、再度試してもだめ。

困ったなーと思って、上記のディレクトリを眺めていると、 log というフォルダがあります。ここを見ると、いろいろとあるんですが、 idea.log というファイルが今試している更新日時になっています。これが、 Android Studio のログファイルっぽいぞ、とおもったので、エディタで開いてみると、今回のエラーが起きてる日時について、 OutOfMemory だの Low Memory だのというメッセージがたくさん見えます。

あ、これが原因だったのね。(最終的に、これは原因ではありませんでした。あとで、間違いに気づきます。)

対応策?

原因がわかれば、対応できます。

ヒープサイズエラーが出た時の対処方法 - Android アプリ開発「MATRIX」

Android Studio のメモリ不足(ヒープサイズエラー)は昔も対応やったな、と思いつつ、改めてググってみると、上記のような記事が出てきました。

早速、いったん、 Android Studio を閉じて、テキストエディタを開き、このプロジェクトの gradle.properties ファイルを開いて、 -Xmx2048m となっているこころを -Xmx4096m にしてみます。

その後、 Android Studio を起動します。これで、解決!

とおもいきや、状況が変わりません。あれれ?とおもって、さらに調べてみると、どうもこの設定は、一度プロジェクトをリビルドしないと有効にならないとのことです。

Configure Android Studio  |  Android Developers

まじか。。。

ということで、仕方ないので、編集中のファイルを一旦、スタッシュに退避させて、リポジトリを間違いなく動いていたところまで戻して、この状態で Android Studio を立ち上げます。

で、さきほどの gradle.properties を編集後、 rebuild をかけます。問題ないですね。

ここまで出来たら、リポジトリを元に戻して、さらにスタッシュに退避させていたものを反映させます。この状態で Android Studio を操作すると、おぉ、無事に動きます。

ついでに、上記の記事を元に、 IDE のメモリの上限も増やしておきました。

これで解決、と思ったのですが、しばらくすると、やっぱりフリーズします。新たに割り当てたメモリが足りないのかな?とおもいつつ、 2G -> 4G -> 8G -> 16G -> 32G あたりまで試しましたが、やっぱり落ちます。

これは妙だな。なんか違うぞ。

Android Studio の再インストール

やる手がなくなってきたので、 Android Studio も再インストールしてみます。

2021.3.1 Dolphin Patch 1

を 2023.1.1 Patch 2

にしました。

で、結論は・・・やっぱり状況変わらずです。

原因発見!

何が原因だろうか?と思っていろいろと調べてみるけど、なかなか理由がわかりません。何かの際に、先ほどの AndroidStudio のログファイルを見直していたら、 OutOfMemory のところにスタックトレースが出ており、自分が修正したクラス名が出ているのがわかります。しかも、その後 HashMap でエラーが起きて止まってます。

ん? HashMap でエラー?なんか怪しいぞ。

と思ってコードをよく見ると、なんと、 HashMap に要素を追加する処理が無限ループになっています!

やっと見つけました。

単に処理中に無限ループがあるのではなく、運悪く既存の Layout クラスを extends して、独自のクラスを定義している時に、一部の初期化処理を static イニシャライザ( static の初期化ブロック)で行っており、その中に無限ループを入れ込んでいました。

static のため、プロジェクトを実行しなくても、このクラスを用いたレイアウトファイル(xml ファイル)を表示するだけで、初期化処理が動いて無限ループが呼ばれたようで、当然メモリが足りなくなり、落ちていたということのようです。

あー、こんなことが原因になるなんてな・・・

まとめ

結局、ヒープメモリ設定は悪くなく、上記のバグ修正後は元に戻しても快適に動いていました。思わぬところで OutOfMemory を引き起こしていましたね。こんなミスもあるよ、ということで、晒しておきます。何かの参考になれば幸いです。

にしても、最初に Android Studio のログ見たときに、もうちょっと注意していれば、解決が早かったように思えて、悔やまれます。ログの確認は大事ですね。

kintone の Javascript カスタマイズに関する雑多なあれこれ

kintone の Javascript カスタマイズの際に気がついた、いままで書いてない雑多なことをまとめておきます。

カスタマイズ関連のあれこれは下記にも書いてますので、こちらもご参考にしてください。

スペーステンプレート

スペーステンプレートを利用すると、簡単にスペースおよびそこに含まれるアプリのコピーを作ることができるので、移行の際には非常に便利です。

ただ、スペースやアプリの更新には使えず、常に新規作成になるので アプリ ID (スペース ID)が変わってしまうため、 Javascript カスタマイズとの相性はいまいちですが、まあ、便利です。

ポータルに画像を含む場合

で、このスペーステンプレートを利用して、ポータルが有効なスペースで、かつポータルに画像を設定しているスペースに対して、スペーステンプレートを作成したら、エラーとなったときがありました。

その時の状況としては、

  • 同一ドメインの別スペースで読み込んだ画像を設定しており、その後最初に読み込んだスペースを削除した場合に起きた
  • 詳細は条件は不明だが、アップロードした素材への参照が問題になることがあるようだ
  • エラーになった画像を、再アップロードしてポータルに設定したら問題なく作成できた

という感じでした。

スペーステンプレートを作成する際には、ちょっと気を付けたほうがいいかもしれませんね。

レコードの書き出しと読み込み

アプリを移行する場合、レコードの内容は移されません。なので、別作業で、レコードの書き出しと読み込みを行う必要があるかと思います。

この操作の時に、こんなことに気が付きました。

  • ルックアップのコピーフィールドは、書き出さなくても、読み込み時に適切に対応してくれる(ルックアップ元がある場合)
  • 読み込み時に、『一括更新のキー』として『レコード番号』にチェックがついていると、新規レコードの読み込みに失敗する

最初、2つ目の制限に気がつかなくて、ちょっと焦りました。結局、これは、下記

ファイルからレコードのデータをアプリに読み込む | kintone ヘルプ

のリファレンスの補足にも明記されていることになります。

ということで、アプリを移行するような場合(新規にレコードをコピーするような場合)には、『一括更新のキー』のチェックはなくても問題ないので、チェックを外した設定で読み込めば問題なく取り込めます。

ルックアップと変更イベント

ルックアップ(フィールド)を元に、ルックアップしたら何かしら行いたい、というカスタマイズがあると思います。ですが、ルックアップフィールド自体には、変更イベントが発生しません。

なので、こういう時は、ルックアップ取得時に一緒にコピーするフィールドを用意しておき、そのコピーフィールドに変更イベントを定義して、変更したことを分かるようにすれば対応できます。ちなみに、このコピーフィールドは、変更禁止にしておくとルックアップによる変更イベントのみを発生させて受け取ることができるようになります。

計算フィールドと変更イベント

で、これは聞いた話で自分ではまだ試してないんですが、同様のことが、計算フィールドでも起きるということだそうです(@kimiko0217 さんに教えていただきました)。

まとめ

ある程度カスタマイズしようとすると、こういった kintone の流儀を身に着けておかないと、なかなか効率があがりませんね。きっと、また別のところではまるでしょうが、とりあえずはこんなところで。

何かの参考になれば幸いです。

kintone のテーブルについて

こちらの記事の続きです。

blog.mori-soft.com

今回は、 Javascript カスタマイズに限らず、テーブル周りに関するあれこれをまとめておきます。

グループとテーブル

カスタマイズとはちょっと異なりますが、テーブルはグループに入れられないです。

個人的には、地味にいやな制限です。

テーブルの要素

フォームで新規登録する場合などは、全ての列フィールドが必須でない場合、行の全フィールドが未入力の行があっても、空行が最低1行は入ってしまいます。テーブルの中身が0行というのはフォームで操作する場合は作れないようです。

カスタマイズの時などは、ちょっと意識しておく必要がありますね。

テーブルの行追加・削除の無効化

テーブルに追加したフィールド単位の有効・無効は制御できます。ですが、テーブル全体の無効化はできないみたいです。全部のフィールドを無効にすればいいかな?と思いきや、それでも行の追加ができるので微妙な感じになります。

ネットを調べてみても、テーブル自体を無効(行の追加、削除)にすることはできないっぽいです。

なぜか、公式のドキュメントを見つけらなかったのですが、自分でも試したところ、ダメでした。

どうしてもこれをやりたければ、調べてみると、以下の3つの方法で代替できそうです。

DOM で直接操作

下記などを参考にして、直接 DOM で操作すれば何とかなりそうです。

サブテーブルの追加削除を制限したい - kintone カスタマイズ - cybozu developer community

実際に試してみると、結構簡単に制御できたので、これは使えそうですね。

Kintone UI Component v1 の ReadOnlyTable を使う

テーブル上に情報を表示するだけなら、基本的には関連テーブルでできるんですが、何かの事情でそれができないなら、 ReadOnlyTable を使えば編集不可の情報を表示するだけのテーブルが作れます。

ReadOnlyTable | kintone UI Component · Be a smart kintone developer.

個人的には、これが一番しっくりくる解決策です。

試した際の注意点としては、 ReadOnlyTable のコンストラクタを呼び出すタイミングで、表示したい行データも与えておく必要があります。インスタンス生成後に行データを追加しても画面に反映されませんでした。

Kintone UI Component v1 の Table を使う

Kintone UI Component v1 の Table には、ボタンの追加削除を設定できる actionButton プロパティがあるので、それで制御できるかと思います。

Table | kintone UI Component · Be a smart kintone developer.

既存のレコードは編集させたいが、行の追加・削除はさせたくないという場合は、これもありかと思います。

テーブルの行ごとの更新

テーブルの更新はちょっとややこしいです。

更新時は、更新対象の行だけでなく、その他の行も更新時のリクエストに含める必要があります。

フィールド形式 - cybozu developer network

こちらのリファレンスに

テーブルへの追加、更新時には、既存のすべての行の値を指定してください。

とさらっと書かれている点ですね。リファレンスよりは、下記の解説記事のほうがわかりやすいかもしれません。

レコード更新におけるテーブル操作のテクニック - cybozu developer network

同じページのここのところに、

実際の挙動は「既存の行をすべて削除し、リクエストに含まれるデータをテーブルへ追加する」となります。

とあるように、更新とは言うものの内実は、テーブルの中身を削除して、もう一度追加する、ということのため、このような仕様になっているようです。

なので、更新対象以外の行は、 ID だけでいいので指定する必要があります(変更しないフィールドは含めなくていいというのが、作業的にはちょっとはましなところですかね)。

テーブルの行番号の取得方法

テーブルには行番号という概念がありません。どうしても行番号を表示したければ、行追加時の例の記事、

サブテーブルの追加行を特定したい - kintone カスタマイズ - cybozu developer community

にあるのと同じように、その行が、テーブルの行データ(配列)の何番目に一致するのかを自分で調べてやる必要があります。

ちなみに、テーブルの行に対して、自動で連番を振る、というのが下記で紹介されているので、これを利用する方法もありますね。

テーブルに連番をつける - cybozu developer network

テーブルの行数を取得する

こちらは、フィールドの設定でいけるようです。

テーブルの行数をカウントしたい | kintone ヘルプ

テーブルの行にカウントを設置して、それを計算フィールドで集計すればよいとのことです。

まとめ

テーブル周りは他にもいろいろと落とし穴がありそうです。あんまり使いたくないけど、パッと情報を確認できるのでユーザーからすると便利なんですよね。

もうちょっとテーブル操作用のAPIを用意してくれるとありがたいんですけどねー。