はてなブログに移行した

deno_blogで構築してDeno Deployでホスティングしていたブログをはてなブログに移行した。
大きな理由としては、はてな社員の身なので太いものに巻かれるというのと、最近のDeno事情を全く追っておらずすべてが面倒になったの2つ。やはり太いものに巻かれるのはいいことで何も考えず気楽に記事を書けるようになった(書いてないけど)。後者についてはdeno_blogの更新が止まっているし、触っていない間にDeno Deployがリニューアルされているらしく、爆破して作り直す方が逆に楽なのでは? となった結果そうせずに移行することにした。
移行作業で主に行ったのは以下の2つ

  • 過去記事のインポート
  • 独自ドメインの設定

過去記事のインポートについては対応する形式が存在し、MT, WXR, ZIPの3種類。deno_blogでは記事ごとにMarkdownファイルを作る形式が取られていて、そのままではインポートできないので変換が必要だった。

help.hatenablog.com

変換先は上記ヘルプページにインポートフォーマットのドキュメントリンクが張られていたことからMT形式に決定。記事数が少なく最悪温かみのある手作業で変換してもよかったが、せっかくなので (?) AIコーディングエージェントにお願いしてみた。
フォーマットがしっかりしていて入出力が明確なことから1回目でほぼほぼ満足いくものが出力されてよかった。

Junieにお願いした図

出力された変換スクリプト: github.com 内容は結構素朴。

独自ドメインの設定はすんなりいくと思いきや、地味にハマってしまった。Cloudflareで管理しているドメインの場合はレコードの設定でProxyをOFFにしないといけないらしい。あとはヘルプページ通りに設定すればOK。

DNS onlyにしよう

DroidKaigi 2025 参加ログ

DroidKaigi 2025に現地参加してきた。例年現地で参加していたが今年は迷っていて、最終的には参加することにした。そういった事情がありつかれそうな気もしていたので、スポンサーブースをまわるのは最低限で基本的にはセッションを聞いていた。

セッションの感想

いくつか見た中で印象に残ったセッションの感想をそれぞれ書いていく。

UIだけじゃないComposeの可能性 ━ 宣言的に奏でるメロディ

Composeのランタイムを活用して独自の宣言的ライブラリを作っていく話。ランタイム自体はUIの描画に関係なく汎用的なもののため別のライブラリを作って活用しよう、という考えを自分では思いつくことがないので興味深くて面白かった。Composeが裏で何をやっているかも大まかに理解できるので意外と初学者にもオススメかもしれない。

プロパティベーステストによるUIテスト: LLMによるプロパティ定義生成でエッジケースを捉える

AndroidのUIテストでPBTを活用してみる話。始まってから部屋に入ったので最初の5分くらいを聞くことができなかった… PBTというものを知らず、セッションで出てきたEBTではコードが正しいということは分かるけど書き手の想定していなかった不具合を見つけられなく(= テストで見つけたく)ない? と思っていた自分にぴったりのセッションだった。今後テストを書くときの考え方の一つとしてぜひ持っておきたい。

Androidエンジニアとしてのキャリア

スピーカーであるmhidakaさんの体験も交えたキャリアについて行動するキッカケとなるような話。半年ほど前からキャリアについて考えるようになり、色々やっていたことは間違いではなかったんだなと感じられて安心した。他にも自分の考え方に近くて参考になる話もいくつかあったのでこのままうまいこといけばいいなと思った。

意外と知らない AndroidGoogle Play の世界

SNS等での言及を控えてとのことで具体的な感想は書かない。スピーカーはGoogle Playの中の人で、なかなか興味深い話を聞くことができた。途中普段開発しているアプリが映ったときに心の中でそれうちが開発してるやつ! となって嬉しかった。

スマホ新法って何?12月施行?アプリビジネスに影響あるの?

今年12月から施行される予定であるスマホソフトウェア競争促進法はどこに影響するのか、どう未来が変わるのかについて制定に携わった公正取引委員会の方からの話。7年前から検討してようやく制定されたであったり、思ったより企業と協力して組み上げているんだと思ったりと驚く点が多く、とても面白かった。個人的にはスピーカーの方が過去担当した案件についてあーアレねwと感じるものが多くてニヤニヤしていた。

Cache Me If You Can

Gradleによるビルドのパフォーマンスをより向上させるためにどのようにキャッシュを活用するのかという話。なんとなくでしか理解していなかったGradleのキャッシュについて、より解像度高く知ることができた。普段開発しているアプリではそこら辺が割となおざりになっているので改善したいという意欲が湧いてきた。まずは最後に紹介されたBuild ScanとIDEプラグインを使って眺めてみようと思う。

その他感想

開発におけるAIの活用についてのセッションを期待していたが英語でされるセッションが(同時翻訳があるものも含めて)多く、会場で聞くのがつらいという感情もあって他のセッションを聞くことにした。これについてはとんでもない速さでアップロードされ動画がYouTubeにあり、家で翻訳しつつ見ることができて大変助かるのでDroidKaigiスタッフに感謝を伝えたい。ただ他のセッションを聞いていた時間はスポンサーブースをまわり、現場でAIどう活用しているかなどの生の声を実際に聞くなどすればよかったのでは? と終わったあとに気づいた。事前に仕入れたい情報があるのであればセッションだけでなく人と関わる必要があるなと感じた。

AGP 8.4以降でアプリがクラッシュするようになった話

この記事ははてなエンジニア - Qiita Advent Calendar 2024 17日目の記事です。昨日の記事はbps_tomoyaさんによるAndroid Studio の Debug window ツールバーをカスタムするお話でした。


Android Gradle Plugin(AGP)のバージョンを今年の4月に公開された8.4.0以降に更新してreleaseビルドするとアプリでクラッシュするという不具合が発生した。releaseビルドでのみクラッシュが発生すると聞くとAndroidアプリ開発者は真っ先に圧縮・難読化・最適化を疑うはずで、 実際にAGP 8.4.0のリリースノートにはLibrary classes are shrunkとあるため、shrinkまわりの処理が変更された結果クラッシュするようになったと考えられる。
この記事ではどのような問題が発生したか、どのように解決したかを話していく。

クラッシュした直接の原因

アプリはマルチモジュールで構成されており、各モジュールで実装したプラグインをアプリモジュールで読み込むという機能が存在している。 この機能はプラグイン用のInterfaceを実装したClassをDaggerの@IntoSetを用いてマルチバインディングする形をとっていて、DIされたプラグインに重複があれば例外をthrowする処理になっていた。
AGPを8.4.0に更新するとこのタイミングでクラッシュ、ようはプラグインが重複して読み込まれるようになってしまった。debugビルドではクラッシュしないのでプラグインの重複はなく、クラッシュするreleaseビルドでのみプラグインが重複してDIされていることになる。

なぜ重複して読み込まれるのか

releaseビルドの重複を確認するタイミングでログを出力してみると、

{class B.b=[B.b@218e2fc, B.b@7989594]} // Map<KClass<out T>, List<T>>

のように確かにプラグインが重複してDIされていた。本来であれば以下のように要素が1つのListを持つKeyがプラグインの数だけ存在する状態が期待される。

{class B.b=[B.b@218e2fc], class C.c=[C.c@7989594]} // 要素が1つのListを持つKeyがプラグインの数だけ存在する例

このままでは難読化されていて何が起きているか分からないので、定義したClassと難読化されたClassの対応をビルド時に生成されるmapping.txtファイルで確認する。
AGP 8.4.0更新後のmapping.txtは以下となる。(文字列は例なので適当)

com.example.sample.app.PluginInterface -> A.a:
    void invoke() -> d
com.example.sample.feature.b.BPlugin -> B.b:
    void com.example.sample.feature.b.BPlugin.invoke() -> d
    void com.example.sample.feature.c.CPlugin.invoke() -> d

どうやらCPluginの実装がBPluginClassにマージされているらしい? (参考)

試しにAGP 8.4.0更新前でmapping.txtを生成してみるとこんな感じ。

com.example.sample.app.PluginInterface -> A.a:
    void invoke() -> d
com.example.sample.feature.b.BPlugin -> B.b:
    void com.example.sample.feature.b.BPlugin.invoke() -> d
com.example.sample.feature.c.CPlugin -> C.c:
    com.example.sample.feature.c.CPlugin.invoke() -> d

更新前では確かにCPluginClass自体が難読化されているため、AGP 8.4.0のリリースノートにあるLibrary classes are shrunkの影響で過度に圧縮・最適化されてしまったと考えられる。

解決策

コードを保持したい場合の解決策としてkeepルールの指定がある。今回はプラグインのInterfaceを実装したClassの圧縮・最適化を防ぎたいのでProGuardルールファイルに以下の記述を追加することになる。

-keep public class * extends com.example.sample.app.PluginInterface { *; }

しかしこのkeepルールでは難読化が行われず元の名前が保持されてしまうため、ストアで公開するアプリで指定するのはできれば避けたい。そこでR8のFAQページで紹介されているAttributesを保持する最も弱いルールを適用する。

-keep,allowshrinking,allowoptimization,allowobfuscation public class * extends com.example.sample.app.PluginInterface { *; }

このようにkeepルールを指定することで難読化を有効にしたまま、圧縮・最適化を回避することができた。

どうして圧縮・最適化の対象になったのか考察

公式のCreate an Android libraryというドキュメントを読むと

By embedding a ProGuard file in your library module, you help ensure that app modules that depend on your library don't have to manually update their ProGuard files to use your library. When the Android Studio build system builds your app, it uses the directives from both the app module and the library. So there's no need to run a code shrinker on the library in a separate step.

とあり、アプリモジュールでライブラリモジュールのコードごとまとめて圧縮すればよいことが分かる。
そして今回のリリースノートには

Starting with Android Gradle Plugin 8.4, if an Android library project is minified, shrunk program classes will be published for inter-project publishing. This means that if an app depends on the shrunk version of the Android library subprojects, the APK will include shrunk Android library classes.

と書かれている。裏を返せばライブラリモジュールで圧縮されていなければ未圧縮のコードがアプリモジュールに公開されるということであり、未圧縮のライブラリモジュールのコードも含めてより効果的な圧縮が行われるように処理が変更されたのではと推測する。
実際の圧縮・最適化処理の変更までは確認していないが、プラグインの実装ClassはDaggerのバインディング以外から参照されていないために対象となる可能性は元から高い状態ではあった。

おまけ

デフォルトのProGuardルールファイルとして使われることの多いAGPが生成するproguard-android-optimize.txtの記述が変更されていることに今回の調査中気づいた。ここで注目したいのは

-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*

と書かれている行がAGP 8.3.0以降のproguard-android-optimize.txtから削除されている点だ。
内容としては特定の最適化を有効化/無効化するもので、この行では最適化時のClass Mergingを無効化していることになる。つまりこの行が削除されているAGP 8.3.0以降の最適化でClass Mergingが行われるようになったために、今回のクラッシュが発生するようになったのだと考えた。
しかしながら公式の最適化に関するドキュメントを見てみると

R8 does not allow you to disable or enable discrete optimizations, or modify the behavior of an optimization. In fact, R8 ignores any ProGuard rules that attempt to modify default optimizations, such as -optimizations and -optimizationpasses. This restriction is important because, as R8 continues to improve, maintaining a standard behavior for optimizations helps the Android Studio team easily troubleshoot and resolve any issues that you might encounter.

とあり、R8で最適化を行っている場合-optimizationsは無視されるため、個別のProGuardルールファイルに記述してもClass Mergingを抑制することはできない。
おそらくR8がデフォルトとなってから数年経っていて無意味な記述をこのタイミングで消しただけなのだろう。そもそもAGP 8.3系で今回のクラッシュは発生していないので直接的な関係は皆無と思われる。


明日のはてなエンジニア - Qiita Advent Calendar 2024記事を担当するのはergofriendさんです。

DroidKaigi 2024 参加ログ

9/11-13に開催されていたDroidKaigi 2024にオフライン参加してきたのでその感想。
初日のワークショップは仕事をしていて不参加で、実際に参加したのはセッションのある9/12-13の2日間。 両日とも早めの時間に会場へ着いてしまったが、その時間帯でもとても賑わっていてよかった。サークル同士で挨拶などしている開場前の同人即売会のような雰囲気。自分はというとそもそも数えるほど知人がいないのでWelcome Talkが始まるのをjellyfishで座って待っていた。開催期間中割とずっとこんな感じ。関わりを広く持ちたいね、これは来年のDroidKaigiまでのミッション。
そんな話は置いておいて聞いてきたいくつかのセッションについて感想を。

セッションの感想

KSPの導入・移行を前向きに検討しよう!

kaptとKSPの違いをなんとなくでしか理解していなかったのでそこから説明されていてよかった。KSPを用いた実装も思っていたより複雑ではなさそうで苦手意識が薄まった。今度軽い実装をして触ってみようと思う。
kaptとKSPが混在しているとビルドが遅くなるというのは知らなかった。業務でメインで触っているコードはKSPへ完全に移行しているので安心。

Android 15と日本語

去年のDroidKaigi 2023でここ数年のAndroid Textの新(?)機能についてというセッションで発表されていた方のセッション。そのセッションが面白くて今年も聞きに行った。
相変わらず興味深くて、日本語周りの新機能・追加点も分かりやすく紹介されていた。localeは大事。たまたま近くに座っていた同僚とルビ振りや縦書きのサポートが欲しいねという話をした。

Jetpack ComposeにおけるShared Element Transitionsの実例と導入方法 またその仕組み

ここ半年くらい気になっていたShared Element Transitionsの話。Jetpack ComposeでAPIが追加されたのは最近のことらしい。
XMLでレイアウトを組んでいた頃の面倒なぼんやりとしたイメージを持っていたが、Jetpack ComposeではシンプルめなAPIになっていて感動した。親からスコープを持ってこないといけないため既に深いネストを形成してしまっているアプリへの導入は大変そうだが… それでも導入したくなる魅力のあるAPIだった。

タッチイベントの仕組みを理解してジェスチャーを使いこなそう

こちらはついこないだまで頭を抱えていたタッチイベント(ジェスチャー)についてのセッション。最終的にライブラリを使うことになったが、セッション直前でその作者が発表者だということに気づいて驚いた。
Zoomableというライブラリを作る上で躓いた内容などを元にしたJetpack Composeにおけるジェスチャーの話には共感しかなく、セッション中ずっとうんうんと頭を振っていた。最後列の席で助かった。
イベントの消費についてはNestedScrollと似た概念で理解していたが、PointerEventPassというイベントの伝達順序については知らなかったのでとても勉強になった。

当日の感想

会場でのメモはすべて自作アプリ^1を使った。これはスレッドのような形でメモを取るアプリで、アプリを見た人にZennのスクラップみたいだねとよく言われる。DroidKaigiの会場からスマホを使ってスレッド形式でメモを取りたいという案を元にして作ったので、想定していたシナリオで大活躍してくれて大満足。
Pixel Foldの内側ディスプレイでTwitterと分割してトレンドを眺めながらメモする体験はとても快適だった。正直に言ってPixel Foldを持っていて嬉しかったことが今回くらいしかないので、実はいらないのでは?と思い始めている。しかし、会場でPixel 9 Pro Foldを持っている人を数人見かけて羨ましくなったのでやっぱり欲しいのかもしれない。

前書きで書いたとおり知人がほとんどおらずセッション以外の時間は同僚(と元同僚)と話すことが多かったため、せっかくの機会なのにもったいなかったなとは思う。オフライン参加は3度目のはずだが、未だに新しい関わりを持つのが苦手である。元々会話しているところに入っていけない人間で、スポンサーブースに行きやすくする施策であるはずのスタンプラリーもあまり活用できなかった。無念。
その点、2日目のアフターパーティでは半強制で他の人と席を共にするので少しは交流できたのではないか。
jellyfish後方にあったコミュニケーションスペースをミートアップ以外の時間でも交流できるスペースとして大々的にアナウンスされて活用されていたら違ったかもしれない。いや、嘘かも。自分から話しかけられない人間はどんな状況だったとしても変わらない。とはいえアフターパーティでは多少なりともなんとかなったはずで、それを糧に来年こそはより努力しようと思う。

その他の感想

恒例のconference-appに今年も無事コントリビュートすることができた。しかし、自分が触った以外の部分をまだほとんど見れていないのでおいおい見ていきたい。今年のアプリは表面上ViewModelを利用していないらしく? とても気になっている。

現地参加して交流をしていた他の同僚の話を聞くに、所属している企業がモバイルアプリを開発している印象がどうも薄いようで、自分も露出を増やすなどして現状を変えていく必要がありそうだと感じた。 来年のDroidKaigiでは登壇といかなくとも、プロポーザルは出していきたい所存。
このブログも書くと言いながら放置しすぎて今回久々に記事を書いたので、これを期に意識して書いていこうと思っている。 その結果自分が知られるようになって向こうから交流してくれる人が増えるかもしれないし。


脚注

1

自作アプリと言っても所属している企業の開発合宿で同僚と一緒に作ったもの。KMPで作られていてAndroid以外の端末でも動く。

テスト更新

1ヶ月更新しなかったと記事を書いてからさらに丸々10ヶ月経ってしまった。
そんなことだってある。
10ヶ月の間に環境の変化があってようやく気力が湧いてきたのでそろそろ再開していこうかなと。

タイトルのテストとはなにかという話も軽く。
これまで記事が更新された際に連動してツイートがされるようにzapierと連携していたが、10ヶ月間放置されていた結果それが止まってしまっていた。 10ヶ月の間にRSSを元にツイートするGitHub Actionsが公開されていて、再開のタイミングで利用しようと追加して動作確認のためのテスト、といった感じ。

1ヶ月更新しないときだってある

そんなときだってある。

ブログ記事を書くことに慣れていないため、更新するために使う労力が意外と大きいことが原因なのかもしれない。構成を考える手間がとても多く、さらには言い換えなどを調べながら書いているのでそこそこ時間がかかってしまう。まぁこれは書き続けていればそのうち解決するだろう。
もう一つ労力を使っていそうな原因として、記事の文章をである調で統一していることもあるのではないか。普段と異なる口調、そして堅めの文章を書くことになるので上記のような時間はさらに増えることになる。ちなみになぜである調で統一しているかというと、です/ます調だと文末がほぼ同じになってしまうのを防ぐためだ。事実、先日勤めている会社の技術ブログ記事をです/ます調で執筆した際、9割の文章で文末が「ます」になっていた。(この記事は個人が特定されない形で公開されているのでリンクしない)
これらを解決するために今後はもう少し緩い文体で記事を書いていこうと思う。

急に意識高くなってプロテイン飲みだすやついるよね

昔から筋肉が付かなければ脂肪も付かないため、どうしても痩せがちで骨張っている。体質が悪いというより食生活が悪いのだと思う。とはいえ、食生活を変えることも難しいのでとりあえず追加でプロテインを飲むことにしている。

基本的に自分はミルク風味の味が好みでないのでほとんどのプロテインが口に合わない。ではどうしているのかというと、SAVAS AQUAというスポーツドリンクに近いものを飲用している。そのまま水で溶かすと運動していない身体には少々酸っぱく感じてしまうので、代わりにオレンジジュースで溶かすと飲みやすい。タンパク質が変質してしまいそうな気もするが、どうせアミノ酸に分解されてしまうだろうから問題はないはず。
ただそこそこの量のジュースを追加で飲む羽目になるため糖分などがやや心配だ。そこでSAVAS AQUAがなくなり次第、以前飲んでいたUMATEINプロテインに変更しようと考えている。ウマいプロテインを標榜しているだけあってプロテインとしては美味しい部類であり、またフレーバーも以前より大幅に増えて選択肢が増えた。お試しセットもあるので、以前飲んでいたストロベリー味以外にオレンジ味とベリーミックス味を試すつもりだ。ちなみに、飲むヨーグルトで溶かすとスムージーのようになりとても美味である。