Amazon Fireデバイス向けのアプリの開発では、機能が正しく動作することが非常に重要です。また、優れたアプリの開発とは、ユーザーにとって使いやすい、スムーズで信頼性の高いエクスペリエンスを実現することでもあります。クラッシュや遅延が発生したり、UIの操作がわかりづらいと、アプリの評価が低くなり、場合によってはアンインストールされることもあります。そういった状況は回避したいものです。
そのため、パフォーマンスの問題に対処し、アプリの安定性を保つことは、アプリ開発者にとって重要な課題の一つとなります。アプリのパフォーマンスと安定性の確保は、ユーザーの満足度を向上させ、アプリを成功に導くための鍵となります。アプリ健全性インサイトダッシュボードなどのAmazon開発者コンソールのツールを利用すれば、簡単に問題を特定して適切に修正することができます。
本ガイドでは、このダッシュボードの概要と注目すべきデータについて説明します。アプリのパフォーマンスと安定性を改善するための様々なヒントを紹介し、あらゆるFireデバイスに対応できるアプリを開発できるようサポートします。 では始めましょう。
アプリ健全性インサイトダッシュボードは、アプリのパフォーマンスを把握できる便利なツールです。このダッシュボードで主要な指標と実用的な分析データを確認することで、最も重要な問題の修正に注力できます。
このダッシュボードにアクセスするには、公開中のアプリが必要です。手順は以下のとおりです。
1. 開発者コンソールのダッシュボードに移動します。
2. 上部のナビゲーションバーで [アプリ&サービス] をクリックします。
3. アプリの [アクション] メニューを開きます。
4. [アプリ健全性インサイト] をクリックします。
このダッシュボードは、パフォーマンスと安定性という2つの主なセクションに分かれています。各セクションには、アプリの健全性に関する具体的なデータが表示されます。
これらのデータがあれば、問題の原因を明確に把握できるため、推測で探る必要がなくなり、問題に即座に対処できるようになります。
開発者にとって、時間は貴重なものです。このダッシュボードを利用することで、ログを細かく調べる必要がなくなり、ユーザーからの苦情を受けて問題が発覚することもなくなります。さらに、アプリの最も重要な問題が浮き彫りになるため、ユーザーエクスペリエンスに最大の影響を及ぼす問題の修正を優先できます。こうした問題に早い段階で対処できれば、批判的レビューを減らし、ユーザーの定着率を改善できます。
パフォーマンスダッシュボードでは、アプリの効率を測定できます。いくつかの主要な指標に注目することで、ユーザーエクスペリエンスに影響を及ぼすボトルネックを特定して対処できます。
アプリのレイテンシの指標は、アプリの起動に関する2つの主なデータ(アプリの起動時間と使用準備完了時間)を追跡します。
これらは両方とも、ユーザーが認知する速度と応答性に影響を及ぼすため、重要な指標です。これらのベンチマークの数値を超えると、アプリの動作が遅く感じられ、ユーザーが不満を抱く原因となる場合があります。
アプリの起動時間と使用準備完了時間を短縮するために、次のような対策を取ることができます。
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;
}
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(英語のみ)を使用するメモリ効率の高い柔軟なアプローチもあります。
これらの推奨事項の詳細とコード例については、Fire OSでアプリの起動時間を測定し改善する方法を参照してください。
フォアグラウンドのローメモリキラーイベント(LME)指標は、デバイスのアプリを強制終了するフォアグラウンドのローメモリイベントの1日の平均発生件数を追跡します。フォアグラウンドのLMEは、非永続的なバックグラウンドアプリやサービスをすべて強制終了した後でもシステムのメモリが大幅に不足している場合に発生します。この指標により、アプリの強制終了につながる深刻なメモリ制限がどの程度の頻度で発生しているかを把握できます。
これらのイベントを最小限に抑えるには、アプリのメモリ使用量が健全である必要があります。もちろん、これはアプリの複雑度にも依存します。ただし、基本的なベンチマークとして、ゲームアプリの場合はフォアグラウンドのメモリ消費量を1,000MB未満、ゲーム以外のアプリの場合は600MB未満に制限する必要があります。過度にメモリを使用すると頻繁にLMEが発生し、特にリソースに制限のあるデバイスで、アプリが不安定になったりクラッシュしたりすることがあります。
アプリのメモリ管理に関する包括的なガイドについては、アプリのメモリ使用量を減らす方法に関するAndroid Developersのページを参照してください。このリソースの重要ポイントを取り入れた以下の戦略を実施することで、メモリ使用量を減らし、アプリのパフォーマンスを改善できます。
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.
}
}
}
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)
}
}
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;
}
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アプリストアにおけるアプリ申請時のテスト基準には、次のように記載されています。
スムーズなエクスペリエンスを実現するには、アプリのフレームドロップ率を5%未満に抑えることを目指します。ドロップ率が10%を超えると、ユーザーエクスペリエンスが大幅に低下する可能性があり、アプリの反応が遅く質が低いと感じられます。
滑らかさを改善するには、複雑なアニメーションを簡素化し、同時にレンダリングするオブジェクトの数を減らします。効率的にGPUを使用し、遷移時の不要な計算を最小限に抑えることで、レンダリングタスクを最適化できます。Android StudioのPerformance Profilerにあるジャンク検出(英語のみ)などのツールや、Profile GPU Renderingツールを使用すると、レンダリングに関する問題を効果的に特定して解決できます。
安定性ダッシュボードを使用すると、ユーザーに不満を抱かせるアプリのクラッシュや停止などの原因となり得る問題を特定し、解決できます。いくつかの主要な指標をモニタリングすることで、先を見越してアプリの信頼性とユーザーエクスペリエンスを改善できます。
クラッシュ発生率は、アプリのクラッシュの頻度をユーザーセッション数に照らして測定したものです。頻繁なクラッシュは、アプリの低評価とアンインストールに直結するため、この指標は重要です。
健全なアプリの場合、クラッシュ発生率は1%未満(英語のみ)です。クラッシュ発生率がこのベンチマークを超えるアプリには、ユーザーから敬遠され、信頼を失うリスクがあります。クラッシュ発生率を低下させるには、以下のAndroid開発者に推奨されている戦略を実施する必要があります。
適切なロギングを実装し、詳細なエラー情報を取り込む。
これらの領域に計画的に取り組むことで、開発者はFireタブレットにおけるクラッシュ発生率を大幅に低下させ、アプリの安定性を全体的に改善できます。
ANRが発生するのは、アプリのメインスレッドから長時間応答がなく、システムからユーザーにアプリが動作していないことが通知される場合です。多くの場合、ユーザーはアプリを強制終了します。
健全なアプリが目指すべきANR率は0.47%未満です。ANR率がこれより高い場合、アプリにパフォーマンスのボトルネックがあるか、メインスレッドで処理がブロックされている可能性があります。
ANRに対処するには、以下の推奨されている修正を行います。
// 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();
}
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)
}
}
パフォーマンスダッシュボードと安定性ダッシュボード以外にも、アプリのエクスペリエンスを可能な限り高めるために留意すべき点があります。
Fireデバイス向けの質の高いアプリを開発するには、パフォーマンスと安定性に注意を払わなければなりません。アプリ健全性インサイトダッシュボードを使用すると、開発者は主要な指標に焦点を合わせることができ、リピート率が高く、スムーズで信頼性の高いエクスペリエンスを実現するアプリを提供できるようになります。
さらに詳しい情報を知りたい場合は、 以下のリソースを参照してください。アプリの品質を継続的に改善するためのヒントをご確認いただけます。