优化Fire TV应用的性能和用户体验是客户的首要任务。需要考虑的一个关键方面是后台内存的使用,这会显著影响应用的启动时间和整体用户体验。虽然后台内存的使用没有固定阈值,但需要注意的重要一点是,当Fire OS需要释放资源时,具有较高后台内存的Fire TV应用是第一个被清理的应用。当应用“冷启动”时,所有相关进程、活动、视图和资产都必须加载到内存中。加载时间会影响用户启动和运行您的应用需要花费的时间。
在这个关于改善后台内存使用的开发者演练中,我们将从Android的导航原则开始,更好地了解这些原则如何影响Fire OS设备上的应用。
下图显示了Fire App Builder示例应用的四种不同状态:
首次启动后,应用从Fire TV主屏幕转到应用的主屏幕,而当用户选择内容时,应用会转到内容详情屏幕。如果用户决定播放内容,则最终视图为内容播放屏幕。请记住,如果用户按下遥控器的“主页”按钮,应用就会被发送到后台。
Android应用响应默认的activity生命周期事件,如“onPause”,并可能在应用转换到后台状态时释放内存资源。一个常见的例子是,用户在流媒体应用中短暂暂停视频,按下“主页”按钮,或无意中退出应用,然后想要从中断的地方快速恢复播放。然而,如果应用在后台停留较长时间,操作系统可能会关闭它,尤其是在消耗大量后台内存的情况下。
为了解决这个问题,Android在onTrimMemory上提供了一个系统回调,以允许应用在系统级别释放内存以响应内存相关事件。在Fire TV应用的环境中,onTrimMemory可用于管理后台内存的使用并确保最佳性能。以下是Fire TV应用示例中onTrimMemory的使用示例。尽管该应用仅适用于Java,但所举例子中包括Kotlin示例,也会有助于基于Kotlin的应用。
此示例简单演示了如何对内存相关的事件做出反应。根据应用的架构,您可以从各种操作中进行选择,例如从数据容器中释放缓存或卸载后台状态下不需要的图形资源。此外,如果一个应用已经进行了所有释放内存的尝试,但仍在因内存不足而进行回调,那么这时可能就该保存目前可能需要的任何应用状态,为意外关闭做好准备。
步骤1:按照以下说明下载、构建并运行Fire TV App Builder。现在我们有了一个正在运行的示例应用实例,我们将进行以下更改。
步骤2:将onTrimMemory回调添加到ModularApplication<path to sample app>/fire-app-builder-master/ModuleInterface/src/main/java/com/amazon/android/module/ModularApplication.java
打开ModularApplication.java并将onTrimMemory回调添加到此文件中。在下面的代码段中示出了参考示例。
Java
import android.content.ComponentCallbacks2;
/**
* Base class for a modular application.
*/
public abstract class ModularApplication extends Application implements ComponentCallbacks2 {
...
...
...
...
@Override
public void onCreate() {
super.onCreate();
...
...
}
...
...
...
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
//This callback notifyies your app for memory related system callbacks
switch (level) {
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
/* The app is now transitioning to a background state*/
break;
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
/* Low system memory callbacks when the application is in foreground.
In this blog we will skip this part */
break;
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
/*
Low Memory events when the application is in background.
Based on the message type you can decide what and how much memory
to release here.
*/
break;
default:
break;
}
}
}
Kotlin
import android.content.ComponentCallbacks2
/**
* Base class for a modular application.
*/
class ModularApplication : Application() , ComponentCallbacks2{
...
...
...
override fun onCreate() {
super.onCreate();
...
...
}
...
...
...
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
//This callback notifyies your app for memory related system callbacks
when (level) {
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN -> {
// The app is now transitioning to a background state
}
ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE,
ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW,
ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> {
/* Low system memory callbacks when the application is in foreground.
In this blog we will skip this part */
}
ComponentCallbacks2.TRIM_MEMORY_BACKGROUND,
ComponentCallbacks2.TRIM_MEMORY_MODERATE,
ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> {
/*
Low Memory events when the application is in background.
Based on the message type you can decide what and how much memory
to release here.
*/
}
else -> {
}
}
}
}
步骤3:在一些用例中,在应用的用户界面中导航会将一个活动堆叠在另一个活动之上,从而产生多个活动的堆叠,如上面的状态图所示。我们将把所有活动引用存储在ModularApplication中的列表变量中,并添加一个用于添加和删除条目的方法。作为这个示例实现的一部分,当应用需要在后台状态下释放内存时,我们将通过结束除主屏幕之外的所有活动来开始释放内存。随着活动被销毁,相关的内存也将被释放。
Java
public abstract class ModularApplication extends Application implements ComponentCallbacks2 {
...
...
private List<Activity> arrayActivities = new ArrayList<>();
...
...
public void addActivity(Activity activity){
arrayActivities.add(activity);
}
public void removeActivity(Activity activity){
arrayActivities.remove(activity);
}
}
Kotlin
class ModularApplication : Application() , ComponentCallbacks2{
...
...
private var arrayActivities: ArrayList<Activity> = ArrayList ();
...
...
fun addActivity(activity: Activity) {
arrayActivities.add(activity);
}
fun removeActivity(activity: Activity) {
arrayActivities.remove(activity);
}
}
步骤4:接下来,我们将对以下文件列表进行更改。
<path to sample app>/fire-app-builder-master/TVUIComponent/lib/src/main/java/com/amazon/android/tv/tenfoot/ui/activities/
ContentBrowseActivity.java
ContentDetailsActivity.java
ContentSearchActivity.java
FullContentBrowseActivity.java
SplashActivity.java
VerticalContentGridActivity.java
<path to sample app>/fire-app-builder-master/TVUIComponent/lib/src/main/java/com/amazon/android/uamp/ui/
PlaybackActivity.java
在上面列出的所有文件中,我们将修改活动的onCreate和onDestroy方法。下面是每个活动类中已完成更改的示例。
Java
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.full_content_browse_activity_layout);
...
...
((ModularApplication)getApplication()).addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
...
...
((ModularApplication)getApplication()).removeActivity(this);
}
Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.full_content_browse_activity_layout);
...
...
(application as ModularApplication).addActivity(this);
}
override fun onDestroy() {
super.onDestroy()
(application as ModularApplication).removeActivity(this);
}
步骤5:接下来,我们将在ModularApplication.onTrimMemory中放入调试日志,并打印应用的可用内存。调试日志将有助于了解调用OnTrimMemory时的特定实例,并观察每次调用期间的内存消耗和正在运行的活动。我们还可以观察到,当系统资源开始不足时,会将应用终止。
Java
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
//This callback notifyies your app for memory related system callbacks.
switch (level) {
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
/* The app is now transitioning to a background state*/
break;
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
/* Low system memory callbacks when the application is in foreground. In this blog we will skip this part */
break;
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
/*
Low Memory events when the application is in background.
Based on the message type you can decide what and how much memory
to release here.
*/
Log.e("onTrimMemory", "Total activities :" + arrayActivities.size());
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
activityManager.getMemoryInfo(mi);
double sizeInMB = mi.availMem / 0x100000L;
Log.e("onTrimMemory", "Available Memory:" + sizeInMB);
break;
default:
break;
}
}
Kotlin
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
//This callback notifyies your app for memory related system callbacks
when (level) {
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN -> {
// The app is now transitioning to a background state
}
ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE,
ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW,
ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> {
/* Low system memory callbacks when the application is in foreground.
In this blog we will skip this part */
}
ComponentCallbacks2.TRIM_MEMORY_BACKGROUND,
ComponentCallbacks2.TRIM_MEMORY_MODERATE,
ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> {
/*
Low Memory events when the application is in background.
Based on the message type you can decide what and how much memory
to release here.
*/
Log.e("onTrimMemory", "Total activities :" + arrayActivities.size)
val mi = ActivityManager.MemoryInfo()
val activityManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager
activityManager.getMemoryInfo(mi)
val sizeInMB = (mi.availMem / 0x100000L).toDouble()
Log.e("onTrimMemory", "Available Memory:$sizeInMB")
}
else -> {
}
}
}
步骤6:现在启动示例应用(通过单击主屏幕图标或通过Android Studio)并播放一些示例视频。测试时,您可以打开终端窗口(MAC上的终端和Windows上的命令控制台),并输入以下命令来监视logcat。
adb logcat | grep "OnTrimMemory"
步骤7:播放视频时,按下“主页”按钮可将应用发送到后台。现在打开另一个应用,如Prime Video,播放一些高清内容,然后打开其他一些应用,并执行同样的操作,直到您在logcat中观察到以下行。
03-24 22:21:35.587 29110 29110 E onTrimMemory: Total activities :3
03-24 22:21:42.462 29110 29110 E onTrimMemory: Available Memory :408.0
步骤8:在上面的步骤中,我们观察到当操作系统运行所需资源不足时,我们的示例应用在后台被终止。我们如何通过进一步降低资源密集型后台应用的排名,让我们的应用在后台状态下停留更长时间? 我们可以在示例应用中进行的一个简单更改是检查活动列表的大小,并销毁主屏幕(最上层的活动)以外的所有活动。通过释放这些活动,我们还将释放相关资源,并减少示例应用的总体内存消耗。下面是一个示例代码片段,该代码片段从堆栈中释放除最上层的活动(主屏幕)之外的一系列活动。
Java
public void onTrimMemory(int level) {
...
...
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
while(arrayActivities.size() > 1){
Activity activity = arrayActivities.get(arrayActivities.size() - 1);
arrayActivities.remove(activity);
activity.finish();
}
...
...
}
Kotlin
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
...
...
ComponentCallbacks2.TRIM_MEMORY_BACKGROUND,
ComponentCallbacks2.TRIM_MEMORY_MODERATE,
ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> {
while (arrayActivities.size > 1) {
val activity = arrayActivities[arrayActivities.size - 1]
arrayActivities.remove(activity)
activity.finish()
}
....
....
}
...
...
}
上面用于释放后台内存的示例实现可以让用户在短时间内返回应用时恢复视频播放。如果应用长时间处于后台,并且从操作系统接收到内存过低回调,则它会通过释放视频播放和视频详情页面的活动来释放内存。当用户返回应用并将其调至前台时,用户将看到应用主屏幕,并可以从该处继续导航。如本部分开头所述,每个应用都有不同的设计,根据设计,应用可以采取最适合减少后台内存消耗的操作,以应对系统发起的内存回调事件。
我们希望您能够为使用您产品的客户优化您的Fire TV应用。通过利用这些Fire OS功能,您应该会发现您应用的加载时间变短,效率也变得更高。