Beautiful HD Wallpapers の Android 版(v2.1.0)をリリースした直後、レビューに「画像が真っ白になる」という報告が複数届きました。再現端末を調べると、いずれも解像度の低い ldpi・mdpi クラスの旧機種でした。
Rork Max で生成したネイティブ Android コードを Android Studio に持ち込んでビルドした段階では問題ありませんでした。Play Store に公開した瞬間から問題が起きたのです。
2014 年に個人開発を始めてから 12 年が経ちます。廣川政樹として累計 5,000 万 DL を超えるアプリ群を AdMob で収益化しながら運営してきた経験があっても、Play Store の最適化ロジックには今も新しい落とし穴があります。今回の「密度分割と drawable-nodpi の相互作用」はそのひとつでした。
何が起きていたか
Play Store には 密度分割(density APK splits) という仕組みがあります。端末の画面密度(ldpi / mdpi / hdpi / xhdpi / xxhdpi / xxxhdpi)に応じて、適切なリソースだけを含む APK を配信することでダウンロードサイズを減らす最適化です。
この最適化が、drawable-nodpi/ フォルダに置いたリソースを思わぬ形で扱います。
drawable-nodpi/ は「密度によらずすべての端末に配信する」という意図で使われるフォルダです。ウォーターマーク画像やアイコン素材など、縮小・拡大されたくないリソースを置く場所として認識されています。しかし密度分割が有効になると、低密度端末向けの APK がこのフォルダのリソースを拾い損ねるケースがありました。
具体的には、drawable/ フォルダに画像が存在せず drawable-nodpi/ にだけ置いていた場合、Play Store の分割ロジックが「この密度向けのリソースはない」と誤判定し、配信対象から除外してしまうことがあります。国際芸術賞 17 冠を受賞したデザイナーとしてビジュアル品質には特に注力していただけに、画像が消えるというのは見過ごせない問題でした。
Rork Max のコードでなぜ起きやすいか
Rork Max が生成する Android ネイティブコードは、多くの場合 res/drawable/ または res/drawable-xxhdpi/ に画像を配置します。高解像度優先の設計は合理的ですが、アイコンやロゴなど「密度非依存で扱いたい」画像を drawable-nodpi/ に入れ直したとき、上記の問題が起きやすくなります。
私の Beautiful HD Wallpapers の場合、壁紙アプリの「プレースホルダー画像」と「AdMob 広告ロード前の区切り線アセット」を drawable-nodpi/ に移動していました。ローカルのエミュレーターでは問題なく動いていたため、公開後まで気づけませんでした。
Rork Max で生成したコードを手動でカスタマイズする際に、drawable-nodpi/ へのリソース移動は比較的よく行われる操作です。生成コードが hdpi 以上の画像しか含んでいない場合、「低解像度でも表示したい」という意図で nodpi フォルダを選ぶ気持ちは理解できます。しかしそれが今回の罠でした。
修正方法
方法 1: drawable-nodpi への配置をやめ、drawable に戻す
もっともシンプルな対応です。密度依存の挙動が許容できる画像(ロゴ、プレースホルダー等)は drawable/ に戻します。
<!-- 変更前: density split に弱い配置 -->
res/drawable-nodpi/placeholder.png ← ldpi 端末で消える
res/drawable-nodpi/divider_asset.png ← 同上
<!-- 変更後: drawable に戻す -->
res/drawable/placeholder.png ← 全密度に配信される
res/drawable/divider_asset.png ← 同上多くのケースではこれだけで解決します。プレースホルダーや区切り線のように、密度によって見た目が変わっても問題ない画像には drawable/ がシンプルで安全です。
方法 2: build.gradle で密度分割を無効化する
アプリサイズよりも確実な配信を優先したい場合は、密度分割を無効にできます。
// app/build.gradle.kts(App Bundle 形式)
android {
bundle {
density {
enableSplit = false // 密度分割を無効化
}
}
}旧来の APK 形式では splits { } ブロックを使います。
// APK 形式の場合
android {
splits {
density {
isEnable = false
}
}
}ただし、密度分割を無効にするとダウンロードサイズが大きくなります。壁紙アプリのように画像アセットが多い場合は、サイズへの影響を事前に確認することをおすすめします。
方法 3: 各密度フォルダに同じ画像を配置する
密度分割を活かしつつ drawable-nodpi/ の問題を回避したい場合、各密度フォルダに同じ画像をコピーします。
res/drawable-ldpi/placeholder.png
res/drawable-mdpi/placeholder.png
res/drawable-hdpi/placeholder.png
res/drawable-xhdpi/placeholder.png
res/drawable-xxhdpi/placeholder.png
res/drawable-xxxhdpi/placeholder.png
メンテナンスコストは増えますが、密度ごとの最適化とすべての端末への配信を両立できます。後から画像を変更する際に全フォルダを更新し忘れると別のバグになるため、スクリプト化しておくと安心です。
Rork Max 生成コードで公開前に確認すべきポイント
Rork Max が生成した Android プロジェクトを Play Store に公開する前に、以下を確認することをおすすめします。
まず drawable-nodpi/ フォルダが存在するかを確認します。
# プロジェクトルートで実行
find . -path "*/res/drawable-nodpi/*" \( -name "*.png" -o -name "*.webp" -o -name "*.svg" \)該当ファイルが出てきた場合、方法 1〜3 のいずれかで対処します。
次に、res/drawable/ フォルダにデフォルト解像度の画像があるかを確認します。特定密度のフォルダ(drawable-xxhdpi/ 等)にしかない画像は、対応する密度の端末以外で表示されない可能性があります。
なお、strings.xml や AndroidManifest.xml には密度分割は影響しないため、テキストリソースは気にしなくて構いません。対象は 画像リソース(PNG・WebP・SVG・9-patch)のみです。
実際の修正結果
v2.0.0 で複数のユーザーから報告があった「低解像度端末での画像欠落」は、drawable-nodpi/ に置いていた 3 ファイルを drawable/ に戻すことで解消しました。v2.1.0 公開後はこの種の報告がゼロになっています。
特に密度や言語などのリソース系のバグは、実機の旧端末を持っていないと再現できないことがあります。Rork Max で生成したコードは品質が高く、そのまま本番環境に出せるケースが多いですが、Play Store の独自の最適化ロジック(密度分割・言語分割・ABI 分割)との相互作用は人間が確認する必要があります。
drawable-nodpi/ を使う場合は、公開前に API 21 〜 23 あたりの低スペック端末でテストすることを習慣にしておくとよいでしょう。
同じ問題で詰まっている方の参考になれば幸いです。
密度分割と一緒に知っておきたい Play Store の落とし穴
密度分割以外にも、Rork Max で生成した Android アプリを Play Store に公開する際に知っておくと役立つ注意点があります。
言語分割(language splits) では、アプリの values-XX/strings.xml に不完全な翻訳が含まれていると、その言語ロケールの端末でクラッシュすることがあります。Rork Max が多言語対応の雛形を生成した場合、未翻訳の文字列が空文字になっていないかを確認してください。
ABI 分割(ABI splits) は、ARM64・x86 などの CPU アーキテクチャ別に APK を分割します。ネイティブライブラリ(.so ファイル)を含むアプリでは、特定のアーキテクチャ向けの .so が欠落しているとクラッシュします。Rork Max が生成するコードは通常この問題を回避するよう設計されていますが、後から手動でネイティブライブラリを追加した場合は注意が必要です。
段階公開(staged rollout) との組み合わせも重要です。私の場合、Beautiful HD Wallpapers の v2.1.0 は 5% → 25% → 50% → 100% の順で段階的に公開しました。Crash-free users 率が 99.7% を下回ったら即座に一時停止するルールを設けていたため、画像欠落の問題を小規模ユーザーへの影響で済ませることができました。
いきなり 100% 公開するのではなく、段階公開を使って Crash-free users と ANR 率を監視しながら広げていく運用は、Rork Max で生成したアプリを初めて公開する際にも強くおすすめします。