開発者コンソール

パフォーマンスと安定性を強化してアプリの質を高める方法

Moses Roth Mar 13, 2025
Share:
Best practices
Blog_Header_Post_Img

Amazon Fireデバイス向けのアプリの開発では、機能が正しく動作することが非常に重要です。また、優れたアプリの開発とは、ユーザーにとって使いやすい、スムーズで信頼性の高いエクスペリエンスを実現することでもあります。クラッシュや遅延が発生したり、UIの操作がわかりづらいと、アプリの評価が低くなり、場合によってはアンインストールされることもあります。そういった状況は回避したいものです。

そのため、パフォーマンスの問題に対処し、アプリの安定性を保つことは、アプリ開発者にとって重要な課題の一つとなります。アプリのパフォーマンスと安定性の確保は、ユーザーの満足度を向上させ、アプリを成功に導くための鍵となります。アプリ健全性インサイトダッシュボードなどのAmazon開発者コンソールのツールを利用すれば、簡単に問題を特定して適切に修正することができます。

本ガイドでは、このダッシュボードの概要と注目すべきデータについて説明します。アプリのパフォーマンスと安定性を改善するための様々なヒントを紹介し、あらゆるFireデバイスに対応できるアプリを開発できるようサポートします。 では始めましょう。

アプリ健全性インサイトダッシュボードについて

アプリ健全性インサイトダッシュボードは、アプリのパフォーマンスを把握できる便利なツールです。このダッシュボードで主要な指標と実用的な分析データを確認することで、最も重要な問題の修正に注力できます。

このダッシュボードにアクセスするには、公開中のアプリが必要です。手順は以下のとおりです。

1. 開発者コンソールのダッシュボードに移動します。

2. 上部のナビゲーションバーで [アプリ&サービス] をクリックします。

3. アプリの [アクション] メニューを開きます。

4. [アプリ健全性インサイト] をクリックします。

 

ダッシュボードの概要

このダッシュボードは、パフォーマンスと安定性という2つの主なセクションに分かれています。各セクションには、アプリの健全性に関する具体的なデータが表示されます。

  • パフォーマンスダッシュボード: アプリの起動時間やフレームレートなどの指標を追跡します。これらの指標から、アプリがどの程度スムーズに実行されているか、またアプリがどの程度迅速にユーザーの操作に応答しているかがわかります。
  • 安定性ダッシュボード: クラッシュ発生率やアプリ応答なし(ANR)エラーなど、ユーザーが不満を抱く可能性がある安定性に関連する問題のデータを確認できます。

これらのデータがあれば、問題の原因を明確に把握できるため、推測で探る必要がなくなり、問題に即座に対処できるようになります。

 

ダッシュボードのメリット

開発者にとって、時間は貴重なものです。このダッシュボードを利用することで、ログを細かく調べる必要がなくなり、ユーザーからの苦情を受けて問題が発覚することもなくなります。さらに、アプリの最も重要な問題が浮き彫りになるため、ユーザーエクスペリエンスに最大の影響を及ぼす問題の修正を優先できます。こうした問題に早い段階で対処できれば、批判的レビューを減らし、ユーザーの定着率を改善できます。

パフォーマンスダッシュボードの指標

パフォーマンスダッシュボードでは、アプリの効率を測定できます。いくつかの主要な指標に注目することで、ユーザーエクスペリエンスに影響を及ぼすボトルネックを特定して対処できます。

 

アプリのレイテンシの指標

アプリのレイテンシの指標は、アプリの起動に関する2つの主なデータ(アプリの起動時間と使用準備完了時間)を追跡します。

  • アプリの起動時間は、アプリを開いてから最初のフレームが表示されるまでにかかる時間を測定したものです。最初にユーザーが認識するものであるため、起動時間を短くすることは、顧客満足度を上げるのに大きく貢献します。理想的な数値は、コールドスタートの場合は2秒以内、ウォームスタートの場合は1秒以内です。
  • 使用準備完了時間は、起動後にアプリが完全に操作可能な状態になるまでにかかる時間を追跡したものです。この時間が短いと、ユーザーは要素の読み込みの完了を待たずに、即座にアプリの操作を始められます。この時間は、アプリの複雑度に応じて変化する可能性があります。Fireタブレット対応アプリの標準的なコールドスタートベンチマークでは、ゲームアプリは15秒以内、ゲーム以外のアプリでは10秒以内に準備が完了する必要があります。ウォームスタートの場合、このしきい値は一律で2秒となります。

Amazon developer security profiles menu image

これらは両方とも、ユーザーが認知する速度と応答性に影響を及ぼすため、重要な指標です。これらのベンチマークの数値を超えると、アプリの動作が遅く感じられ、ユーザーが不満を抱く原因となる場合があります。

アプリの起動時間と使用準備完了時間を短縮するために、次のような対策を取ることができます。

  • 遅延読み込みを実装する: その時点で実際に必要なリソースだけを読み込みます。重要性の低いライブラリの自動初期化を無効にしましょう。Android Studio Profilerを使用すると、最も読み込み時間が長いリソースを特定できます。この手法により、アプリ起動時のデータとリソースの初期オーバーヘッドを大幅に削減できます。
  • 初期UIを最適化する: アプリ起動時のUIは、迅速に描画できるシンプルで静的なものにします。ビューには複雑なレイアウトではなくプレースホルダーを使用しましょう。また、reportFullyDrawn()の呼び出しが完了するまでは、アニメーションや描画処理を過度に使用しないようにします。このアプローチにより、ユーザーへの画面表示を即座に行いながら、バックグラウンドで残りのアプリの読み込みを継続できるようになります。
  • データ管理を効率化する: 初期データの読み込みには、データベースではなくファイルシステムの処理を使用します。これにより、アプリの起動時間を大幅に短縮できます。迅速に取得できるよう、関連データを直接ファイルシステムにシリアル化することを検討してください。以下の例では、最初にユーザーに表示するデータの迅速なシリアル化と逆シリアル化を行います。

Copied to clipboard
public void serialize(Context context, String filename, 
                      Serializable object) throws Exception {
  FileOutputStream fOut = 
    context.openFileOutput(filename, Context.MODE_PRIVATE);
  ObjectOutputStream oOut = new ObjectOutputStream(fOut);
  oOut.writeObject(object);
  oOut.close();
}

public Object deserialize(Context context, 
                          String filename) throws Exception {
  FileInputStream fIn = context.openFileInput(filename);
  ObjectInputStream oIn = new ObjectInputStream(fIn);
  Object result = oIn.readObject();
  oIn.close();

  return result;
}
Copied to clipboard
private val executor = Executors.newSingleThreadExecutor()

fun <T : Serializable> serialize(context: Context, filename: String, data: T) {
    executor.execute {
        try {
            context.openFileOutput(filename, Context.MODE_PRIVATE).use { fileOut ->
                ObjectOutputStream(fileOut).use { objOut ->
                    objOut.writeObject(data)
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}

fun <T : Serializable> deserialize(context: Context, filename: String, callback: (T?) -> Unit) {
    executor.execute {
        val result: T? = try {
            context.openFileInput(filename).use { fileIn ->
                ObjectInputStream(fileIn).use { objIn ->
                    @Suppress("UNCHECKED_CAST")
                    objIn.readObject() as? T
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
        callback(result)
    }
}

上に示した従来のシリアル化メソッドに代えて、Flatbuffers(英語のみ)を使用するメモリ効率の高い柔軟なアプローチもあります。

  • バックグラウンドスレッドを使用する: バックグラウンドスレッドを利用すると、アプリの読み込み中にタスクを実行できます。これにより、メモリを多く使用する処理をメインのスレッドから外し、アプリが使用可能になるまでのユーザーの待機時間を短縮できます。
  • 測定を繰り返し行う: Androidのパフォーマンスツールを使用すると、マクロベンチマーク指標を取り込むことができます。測定を複数回行った後に平均起動時間を計算することで、アプリのパフォーマンスを把握し、改善が必要な領域を特定できます。

これらの推奨事項の詳細とコード例については、Fire OSでアプリの起動時間を測定し改善する方法を参照してください。

 
メモリ使用量に関するフォアグラウンドのローメモリキラーイベント(LME)

フォアグラウンドのローメモリキラーイベント(LME)指標は、デバイスのアプリを強制終了するフォアグラウンドのローメモリイベントの1日の平均発生件数を追跡します。フォアグラウンドのLMEは、非永続的なバックグラウンドアプリやサービスをすべて強制終了した後でもシステムのメモリが大幅に不足している場合に発生します。この指標により、アプリの強制終了につながる深刻なメモリ制限がどの程度の頻度で発生しているかを把握できます。

Amazon developer security profiles menu image

これらのイベントを最小限に抑えるには、アプリのメモリ使用量が健全である必要があります。もちろん、これはアプリの複雑度にも依存します。ただし、基本的なベンチマークとして、ゲームアプリの場合はフォアグラウンドのメモリ消費量を1,000MB未満、ゲーム以外のアプリの場合は600MB未満に制限する必要があります。過度にメモリを使用すると頻繁にLMEが発生し、特にリソースに制限のあるデバイスで、アプリが不安定になったりクラッシュしたりすることがあります。

アプリのメモリ管理に関する包括的なガイドについては、アプリのメモリ使用量を減らす方法に関するAndroid Developersのページを参照してください。このリソースの重要ポイントを取り入れた以下の戦略を実施することで、メモリ使用量を減らし、アプリのパフォーマンスを改善できます。

  • メモリのモニタリング機能の実装Android StudioのMemory Profilerを使用すると、アプリによるメモリの割り当て状況と使用状況を追跡できます。このツールにより、メモリを過度に使用するコードセグメントを特定し、アプリのメモリ消費パターンを一定期間にわたって把握できます。
  • メモリのコールバックの最適ComponentCallbacks2(英語のみ)インターフェイスを実装すると、メモリ使用量を予防的に管理できます。これにより、システムのメモリが少なくなると、アプリがインテリジェントに応答できるようになります。以下に例を示します。

Copied to clipboard
public class MainActivity extends AppCompatActivity
  implements ComponentCallbacks2 {

  // Other activity code.

  /**
   * Release memory when the UI becomes hidden or when system
   * resources become low.
   * @param level the memory-related event that is raised.
   */
  public void onTrimMemory(int level) {

    if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
      // Release memory related to UI elements, such as bitmap caches.
    }
    if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
      // Release memory related to background processing, such as by
      // closing a database connection.
    }
  }
}
Copied to clipboard
import android.app.Activity
import android.content.ComponentCallbacks2
import android.content.res.Configuration
import android.os.Bundle
import android.util.Log

class MainActivity : Activity(), ComponentCallbacks2 {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        registerComponentCallbacks(this)
    }

    override fun onTrimMemory(level: Int) {
        when {
            level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN -> {
                // Release memory related to UI elements, such as bitmap caches
                Log.d("MemoryTrim", "UI is hidden. Freeing up UI-related resources.")
            }
            level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND -> {
                // Release memory related to background processing, such as closing a database connection
                Log.d("MemoryTrim", "App in background. Releasing background resources.")
            }
        }
    }

    override fun onLowMemory() {
        // Handle extreme low-memory situations
        Log.w("MemoryTrim", "System is running low on memory!")
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        // Required override but not used for memory management
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterComponentCallbacks(this)
    }
}

  • メモリ効率の高いコーディング: パフォーマンスが重要な領域には不要なオブジェクトを作成しないようにします。オブジェクトプールを使用すると、割り当てのオーバーヘッドを削減できます。また、内部ループでのオブジェクトの作成や描画メソッドには特に注意を払ってください。
  • リソースとライブラリの管理: 不要なライブラリを削除し、APKサイズを小さくします。コード圧縮ツールを使用すると、使用されていないコードを取り除き、アプリのメモリフットプリントを最適化できます。
  • サービス管理: サービスの使用は控えめにし、タスクが完了したら即座にサービスを停止します。メモリが消費されるため、バックグラウンドで不要なサービスを実行したままにしないでください。
  • 予防的なメモリチェック: 多くのメモリを使用する処理を実行する前に、デバイスのメモリステータスを確認します。これにより、システムのリソースが制限されている場合は、アプリが処理を実行しなくなります。

Copied to clipboard
public void doSomethingMemoryIntensive() {

  // Before doing something that requires a lot of memory,
  // check whether the device is in a low memory state.
  ActivityManager.MemoryInfo memoryInfo = getAvailableMemory();
    if (!memoryInfo.lowMemory) {
      // Do memory intensive work.
  }
}

// Get a MemoryInfo object for the device's current memory status.
private ActivityManager.MemoryInfo getAvailableMemory() {
  ActivityManager activityManager =
    (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
  ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
  activityManager.getMemoryInfo(memoryInfo);
  return memoryInfo;
}
Copied to clipboard
import android.content.Context

fun Context.isMemoryLow(): Boolean {
    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    val memoryInfo = ActivityManager.MemoryInfo()
    activityManager.getMemoryInfo(memoryInfo)
    return memoryInfo.lowMemory
}

fun doSomethingMemoryIntensive(context: Context) {
    if (!context.isMemoryLow()) {
        // Perform memory-intensive operation
    }
}

  • 依存性注入: オブジェクト作成とライフサイクルの管理を効率化する依存性注入フレームワークの使用を検討します。このフレームワークでは、より規律的にオブジェクトの依存性を作成して管理できるため、メモリのオーバーヘッドが減少します。

 

滑らかさ(フレームドロップ率)

フレームドロップ率を追跡することで、アニメーションと遷移の滑らかさを測定できます。この指標から、レンダリング中にフレームがスキップされる頻度がわかります。フレームがスキップされると、アプリが途切れ途切れになり、応答性が低下したように感じられます。

Amazonアプリストアにおけるアプリ申請時のテスト基準には、次のように記載されています。

一定時間以上にわたって25fps以下になることがなく、高いフレームレートを維持する必要があります.… 高速で移動するグラフィックオブジェクトが含まれるアプリで推奨される平均フレームレートは55~60fpsです。

スムーズなエクスペリエンスを実現するには、アプリのフレームドロップ率を5%未満に抑えることを目指します。ドロップ率が10%を超えると、ユーザーエクスペリエンスが大幅に低下する可能性があり、アプリの反応が遅く質が低いと感じられます。

滑らかさを改善するには、複雑なアニメーションを簡素化し、同時にレンダリングするオブジェクトの数を減らします。効率的にGPUを使用し、遷移時の不要な計算を最小限に抑えることで、レンダリングタスクを最適化できます。Android StudioのPerformance Profilerにあるジャンク検出(英語のみ)などのツールや、Profile GPU Renderingツールを使用すると、レンダリングに関する問題を効果的に特定して解決できます。

 

安定性ダッシュボードの指標

安定性ダッシュボードを使用すると、ユーザーに不満を抱かせるアプリのクラッシュや停止などの原因となり得る問題を特定し、解決できます。いくつかの主要な指標をモニタリングすることで、先を見越してアプリの信頼性とユーザーエクスペリエンスを改善できます。

Amazon developer security profiles menu image

クラッシュ発生率

クラッシュ発生率は、アプリのクラッシュの頻度をユーザーセッション数に照らして測定したものです。頻繁なクラッシュは、アプリの低評価とアンインストールに直結するため、この指標は重要です。

健全なアプリの場合、クラッシュ発生率は1%未満(英語のみ)です。クラッシュ発生率がこのベンチマークを超えるアプリには、ユーザーから敬遠され、信頼を失うリスクがあります。クラッシュ発生率を低下させるには、以下のAndroid開発者に推奨されている戦略を実施する必要があります。

  • 徹底的なnullチェックを実行し、アプリのクラッシュの典型的な原因であるNullPointerExceptionの発生を防ぐ。
  • メソッドのパラメーターに注釈を付け、@Nullableと@NonNullを含む値を返し、コンパイル時に発生し得るnull関連の問題を把握する。
  • 汎用の例外処理を行うのではなく、明確にcatchブロックを使用して慎重に例外処理を行う。
  • さまざまなFireタブレットモデルで包括的なテストを実施する。

適切なロギングを実装し、詳細なエラー情報を取り込む。

これらの領域に計画的に取り組むことで、開発者はFireタブレットにおけるクラッシュ発生率を大幅に低下させ、アプリの安定性を全体的に改善できます。

 

アプリ応答なし(ANR)エラー

ANRが発生するのは、アプリのメインスレッドから長時間応答がなく、システムからユーザーにアプリが動作していないことが通知される場合です。多くの場合、ユーザーはアプリを強制終了します。

健全なアプリが目指すべきANR率は0.47%未満です。ANR率がこれより高い場合、アプリにパフォーマンスのボトルネックがあるか、メインスレッドで処理がブロックされている可能性があります。

ANRに対処するには、以下の推奨されている修正を行います。

Copied to clipboard
// Bad example: Performing time-consuming task on the main thread
public void onClick(View v) {
  // This will block the UI and potentially cause an ANR
  Bitmap bitmap = processBitMap("image.png");
  imageView.setImageBitmap(bitmap);
}

// Good example: Moving time-consuming task to a background thread
public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
      // A potentially time-consuming task performed off the main thread
      final Bitmap bitmap = processBitMap("image.png");


      // Use post() to update UI safely from the main thread
      imageView.post(new Runnable() {
        public void run() {
          imageView.setImageBitmap(bitmap);
        }
      });
    }
  }).start();
}
Copied to clipboard
import android.graphics.Bitmap
import android.os.Handler
import android.os.Looper
import android.view.View
import android.widget.ImageView
import java.util.concurrent.Executors

class MyActivity {

    private val executor = Executors.newSingleThreadExecutor()
    private val mainHandler = Handler(Looper.getMainLooper())

    fun onClick(view: View, imageView: ImageView) {
        executor.execute {
            // Perform time-consuming task in background thread
            val bitmap = processBitmap("image.png")

            // Update UI safely on the main thread
            mainHandler.post {
                imageView.setImageBitmap(bitmap)
            }
        }
    }

    private fun processBitmap(filename: String): Bitmap {
        // Simulate bitmap processing (e.g., decoding an image)
        Thread.sleep(2000) // Simulate delay
        return Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
    }
}

  • メインスレッドで同期I/O処理を行わない。
  • 長時間かかる計算を管理しやすい小さなチャンクに分割する。
  • メインスレッドとワーカースレッドの間のロック競合を回避する。
  • 開発中にStrictModeを使用して偶発的なメインスレッドのI/Oを検出する。
  • ブロードキャストレシーバーの場合は、長時間実行される処理にIntentServiceまたはgoAsync()を使用する。
  • Android Studio CPU Profiler(英語のみ)などのツールを使用して定期的にアプリのプロファイリングを実行し、パフォーマンスのボトルネックを特定する。

 

その他の留意事項


パフォーマンスダッシュボードと安定性ダッシュボード以外にも、アプリのエクスペリエンスを可能な限り高めるために留意すべき点があります。

  • バッテリー効率: バックグラウンドのプロセスを最適化し、リソースを多く消費するタスクを減らすことで、バッテリーの消耗を最小限に抑えます。
  • ネットワークの使用状況: 必要に応じてデータをキャッシュし、API呼び出しを最適化することで、過剰なデータ使用を制限します。
  • デバイスの互換性: さまざまなFireタブレットモデルでアプリをテストし、一貫したパフォーマンスと機能を確保します。
  • ユーザビリティ: 直感的なデザインとシンプルな操作性を重視し、ユーザーの満足度を向上させます。
  • コンテンツの質: Fireタブレットのディスプレイ向けに、ユーザーの興味を引き、的確で充実したコンテンツを提供します。
  • ユーザー補助機能: スクリーンリーダーのサポート、高コントラストモード、タッチしやすいインターフェイスなどのユーザー補助機能を組み込むことで、すべての人が利用しやすいアプリを開発します。

まとめ

Fireデバイス向けの質の高いアプリを開発するには、パフォーマンスと安定性に注意を払わなければなりません。アプリ健全性インサイトダッシュボードを使用すると、開発者は主要な指標に焦点を合わせることができ、リピート率が高く、スムーズで信頼性の高いエクスペリエンスを実現するアプリを提供できるようになります。

さらに詳しい情報を知りたい場合は、 以下のリソースを参照してください。アプリの品質を継続的に改善するためのヒントをご確認いただけます。

関連記事

ニュースレターを購読してみませんか?

最新のAmazon開発者向けニュース、業界の動向、ブログの記事をお届けします。