让应用始终显示在 Wear 上

某些 Wear OS 应用对用户始终可见。

搭载 Android 5.1 或更高版本的 Wear OS 设备可以让应用保持在前台运行,同时节省电池电量。Wear OS 应用可以控制手表在低功耗(微光)模式下显示的内容。在微光模式和交互模式下均运行的 Wear 应用称为始终开启的应用。

有了此类应用,慢跑的用户扫一眼手表就可以知道跑了多远,跑了多长时间。一些用户会记录购物清单,在购物时可以快速查看清单上的商品。

让应用始终可见会影响电池续航时间,因此在为应用添加这项功能时,您应该仔细考虑这个影响。

重要提示:27.1.0 版的 Android 支持库提供了一种新的方式来支持微光模式,这种方式使用 AmbientModeSupport,而不是 WearableActivity 类。您可以决定是使用这种全新的首选方式来支持微光模式,还是 扩展 WearableActivity 类

注意AmbientMode 类已被弃用,取而代之的是 AmbientModeSupport 类。

请参阅以下相关资源:

配置项目

要支持微光模式,您必须更新您的 Android SDK 并配置您的开发项目。请按以下步骤做出必要的更改:

  1. 根据创建并运行穿戴式设备应用页面上的配置创建或更新您的项目。
  2. 向 Android 清单文件添加 WAKE_LOCK 权限:
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    

使用 AmbientModeSupport 类支持微光模式

如果使用 AmbientModeSupport 类支持微光模式,您可以从以下方面受益:

如需使用 AmbientModeSupport 类,您可以扩展某个 FragmentActivity 子类或 FragmentActivity 本身并实现一个提供程序接口,该接口又可用于监听微光模式更新。

注意AmbientModeSupport.attach(FragmentActivity) 方法会将一个无头 Fragment 附加到您提供的 FragmentActivity 类或子类,对 FragmentManager.getFragments() 的后续调用将返回对此 Fragment 的引用(该引用不会以任何方式使用)。

下面介绍 AmbientModeSupport 类的一般用法:

  1. 创建某个 FragmentActivity 类的子类。
  2. 实现 AmbientCallbackProvider 接口,如以下示例所示。替换 getAmbientCallback() 方法,以提供对来自 Android 系统的微光事件做出反应所需的回调。在第 4 步中,我们将创建自定义回调类。

    Kotlin

        class MainActivity : AppCompatActivity(), AmbientModeSupport.AmbientCallbackProvider {
            …
            override fun getAmbientCallback(): AmbientModeSupport.AmbientCallback = MyAmbientCallback()
            …
        }
        

    Java

        public class MainActivity extends AppCompatActivity implements AmbientModeSupport.AmbientCallbackProvider {
            …
            @Override
            public AmbientModeSupport.AmbientCallback getAmbientCallback() {
                return new MyAmbientCallback();
            }
            …
        }
        
  3. onCreate() 方法中,通过调用 AmbientModeSupport.attach(FragmentActivity) 启用微光模式。此方法会返回 AmbientModeSupport.AmbientController。您可以通过该控制器检查回调之外的微光状态,您可能需要保留对 AmbientModeSupport.AmbientController 对象的引用:

    Kotlin

        class MainActivity : AppCompatActivity(), AmbientModeSupport.AmbientCallbackProvider {
            ...
            /*
             * Declare an ambient mode controller, which will be used by
             * the activity to determine if the current mode is ambient.
             */
            private lateinit var ambientController: AmbientModeSupport.AmbientController
            ...
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
                ...
                ambientController = AmbientModeSupport.attach(this)
            }
            ...
        }
        

    Java

        public class MainActivity extends AppCompatActivity implements AmbientModeSupport.AmbientCallbackProvider {
            ...
            /*
             * Declare an ambient mode controller, which will be used by
             * the activity to determine if the current mode is ambient.
             */
            private AmbientModeSupport.AmbientController ambientController;
            ...
            @Override
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                ...
                ambientController = AmbientModeSupport.attach(this);
            }
            ...
        }
        
  4. 创建一个扩展 AmbientCallback 类的内部类,以便对微光事件进行操作。此类将成为从您在第 2 步中创建的方法返回的对象:

    Kotlin

        private class MyAmbientCallback : AmbientModeSupport.AmbientCallback() {
    
            override fun onEnterAmbient(ambientDetails: Bundle?) {
              // Handle entering ambient mode
            }
    
            override fun onExitAmbient() {
              // Handle exiting ambient mode
            }
    
            override fun onUpdateAmbient() {
              // Update the content
            }
        }
        

    Java

        private class MyAmbientCallback extends AmbientModeSupport.AmbientCallback {
            @Override
            public void onEnterAmbient(Bundle ambientDetails) {
              // Handle entering ambient mode
            }
    
            @Override
            public void onExitAmbient() {
              // Handle exiting ambient mode
             }
    
            @Override
            public void onUpdateAmbient() {
              // Update the content
            }
        }
        

请务必查看 AlwaysOn 示例,以了解详细信息和最佳做法。

使用 WearableActivity 类支持微光模式

对于新项目和现有项目,您可以通过更新项目配置来向 Wear 应用添加微光模式支持。

创建一个支持微光模式的 Activity

您可以使用 WearableActivity 类在 Activity 中启用微光模式:

  1. 创建扩展 WearableActivity 的 Activity。
  2. 在您的 Activity 的 onCreate() 方法中,调用 setAmbientEnabled() 方法。

按以下方式在 Activity 中启用微光模式:

Kotlin

    class MainActivity : WearableActivity() {

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

            setAmbientEnabled()
        ...
        }
    }

Java

    public class MainActivity extends WearableActivity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            setAmbientEnabled();
            ...
        }
    

处理模式之间的转换

如果用户在应用显示期间有一段时间未与应用进行交互,或者用户用手掌遮住屏幕,系统就会将 Activity 切换到微光模式。在应用切换到微光模式后,将 Activity 界面更新为更基本的布局以减少耗电量。您应该使用黑色背景和最简单的白色图形和文本。为了方便用户从交互模式转换到微光模式,请尽量在屏幕上保持相似的项目布局。要详细了解如何在微光屏幕上呈现内容,请参阅 Wear OS 表盘设计指南。

请注意,如果运行应用的设备没有硬件按钮,那么当用户用手掌遮住屏幕时,并不会使应用切换到微光模式,而会使应用退出并显示主屏幕。此行为旨在确保用户可以正常退出应用。不过,如果屏幕超时,这些设备仍会进入微光模式。

注意:在微光模式下,应停用屏幕上的所有交互元素,如按钮。如需详细了解如何为始终开启应用设计用户交互,请参阅 Wear OS 应用结构设计指南。

当 Activity 切换到微光模式时,系统会调用您的穿戴式设备 Activity 中的 onEnterAmbient() 方法。以下代码段展示了如何在系统切换到微光模式后将文本颜色更改为白色并停用抗锯齿:

Kotlin

    override fun onEnterAmbient(ambientDetails: Bundle?) {
        super.onEnterAmbient(ambientDetails)

        stateTextView.setTextColor(Color.WHITE)
        stateTextView.paint.isAntiAlias = false
    }
    

Java

    @Override
    public void onEnterAmbient(Bundle ambientDetails) {
        super.onEnterAmbient(ambientDetails);

        stateTextView.setTextColor(Color.WHITE);
        stateTextView.getPaint().setAntiAlias(false);
    }
    

当用户点按屏幕或抬起手腕时,Activity 会从微光模式切换到交互模式。系统会调用 onExitAmbient() 方法。替换此方法以更新界面布局,让您的应用在全彩色交互状态下显示。

以下代码段展示了如何在系统切换到交互模式后将文本颜色更改为绿色并启用抗锯齿:

Kotlin

    override fun onExitAmbient() {
        super.onExitAmbient()

        stateTextView.setTextColor(Color.GREEN)
        stateTextView.paint.isAntiAlias = true
    }
    

Java

    @Override
    public void onExitAmbient() {
        super.onExitAmbient();

        stateTextView.setTextColor(Color.GREEN);
        stateTextView.getPaint().setAntiAlias(true);
    }
    

在微光模式下更新内容

微光模式允许您用提供给用户的新信息更新屏幕,但您必须小心地在显示更新与电池续航时间之间取得平衡。您应认真考虑仅替换 onUpdateAmbient() 方法,以在微光模式下实现每分钟更新一次屏幕。如果您的应用需要更频繁的更新,您需要考虑电池续航时间与更新频率之间的取舍。为了节省电池电量,更新频率不应超过每 10 秒一次。但实际上,您更新应用的频率应小于该值。

每分钟更新一次

为了节省电池电量,大多数 Wear 应用都不应在微光模式下频繁地更新屏幕。我们建议您将应用设计为在该模式下每分钟更新一次屏幕。系统提供了一个回调方法 onUpdateAmbient(),允许您按照建议的这个频率更新屏幕。

如需更新您的应用内容,请替换您的穿戴式设备 Activity 中的 onUpdateAmbient() 方法:

Kotlin

    override fun onUpdateAmbient() {
        super.onUpdateAmbient()
        // Update the content
    }
    

Java

    @Override
    public void onUpdateAmbient() {
        super.onUpdateAmbient();
        // Update the content
    }
    

更频繁地更新

您可以按照高于每分钟一次的频率在微光模式下更新 Wear 应用,不过不建议这样做。对于需要更频繁地更新的应用,您可以使用 AlarmManager 对象唤醒处理器并更频繁地更新屏幕。

如需实现一个闹钟以在微光模式下更频繁地更新内容,请按以下步骤操作:

  1. 准备闹钟管理器。
  2. 设置更新的频率。
  3. 当 Activity 切换到微光模式或当前处于微光模式时安排下一次更新。
  4. 当 Activity 切换到交互模式或停止时取消闹钟。

注意:闹钟管理器可能会在触发时创建 Activity 的新实例。为防止出现这种情况,请确保在清单中使用 android:launchMode="singleInstance" 参数声明您的 Activity。

下面几部分将详细介绍这些步骤。

准备闹钟管理器

闹钟管理器会启动一个待定 intent,用来更新屏幕并安排下次闹钟时间。以下示例展示了如何在 Activity 的 onCreate() 方法中声明闹钟管理器和待定 intent:

Kotlin

    // Action for updating the display in ambient mode, per our custom refresh cycle.
    private const val AMBIENT_UPDATE_ACTION = "com.your.package.action.AMBIENT_UPDATE"
    ...
    private lateinit var ambientUpdateAlarmManager: AlarmManager
    private lateinit var ambientUpdatePendingIntent: PendingIntent
    private lateinit var ambientUpdateBroadcastReceiver: BroadcastReceiver

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

        setAmbientEnabled()

        ambientUpdateAlarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager

        ambientUpdatePendingIntent = Intent(AMBIENT_UPDATE_ACTION).let { ambientUpdateIntent ->
            PendingIntent.getBroadcast(this, 0, ambientUpdateIntent, PendingIntent.FLAG_UPDATE_CURRENT)
        }

        ambientUpdateBroadcastReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                refreshDisplayAndSetNextUpdate()
            }
        }
        ...
    }
    

Java

    // Action for updating the display in ambient mode, per our custom refresh cycle.
    private static final String AMBIENT_UPDATE_ACTION = "com.your.package.action.AMBIENT_UPDATE";

    private AlarmManager ambientUpdateAlarmManager;
    private PendingIntent ambientUpdatePendingIntent;
    private BroadcastReceiver ambientUpdateBroadcastReceiver;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setAmbientEnabled();

        ambientUpdateAlarmManager =
            (AlarmManager) getSystemService(Context.ALARM_SERVICE);

        Intent ambientUpdateIntent = new Intent(AMBIENT_UPDATE_ACTION);

        ambientUpdatePendingIntent = PendingIntent.getBroadcast(
            this, 0, ambientUpdateIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        ambientUpdateBroadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                refreshDisplayAndSetNextUpdate();
            }
        };
        ...
    }
    

现在,我们必须在 onResume()onPause() 中注册和取消注册广播接收器

Kotlin

    override fun onResume() {
        super.onResume()
        IntentFilter(AMBIENT_UPDATE_ACTION).also { filter ->
            registerReceiver(ambientUpdateBroadcastReceiver, filter)
        }
    }

    override fun onPause() {
        super.onPause()
        unregisterReceiver(ambientUpdateBroadcastReceiver)
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent)
    }
    

Java

    @Override
    public void onResume() {
        super.onResume();
        IntentFilter filter = new IntentFilter(AMBIENT_UPDATE_ACTION);
        registerReceiver(ambientUpdateBroadcastReceiver, filter);
            ...
    }

    @Override
    public void onPause() {
        super.onPause();
        unregisterReceiver(ambientUpdateBroadcastReceiver);
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent);
        ...
    }
    
更新屏幕并安排数据更新

在此示例 Activity 中,闹钟管理器在微光模式下每 20 秒触发一次。当计时器开始计时的时候,闹钟就会触发 intent 来更新屏幕,然后设置下次更新的延迟。

以下示例展示了如何更新屏幕上的信息并为下次更新设置闹钟:

Kotlin

    // Milliseconds between waking processor/screen for updates
    private val AMBIENT_INTERVAL_MS: Long = TimeUnit.SECONDS.toMillis(20)
    ...
    private fun refreshDisplayAndSetNextUpdate() {
        if (isAmbient) {
            // Implement data retrieval and update the screen for ambient mode
        } else {
            // Implement data retrieval and update the screen for interactive mode
        }
        val timeMs: Long = System.currentTimeMillis()
        // Schedule a new alarm
        if (isAmbient) {
            // Calculate the next trigger time
            val delayMs: Long = AMBIENT_INTERVAL_MS - timeMs % AMBIENT_INTERVAL_MS
            val triggerTimeMs: Long = timeMs + delayMs
            ambientUpdateAlarmManager.setExact(
                    AlarmManager.RTC_WAKEUP,
                    triggerTimeMs,
                    ambientUpdatePendingIntent)
        } else {
            // Calculate the next trigger time for interactive mode
        }
    }
    

Java

    // Milliseconds between waking processor/screen for updates
    private static final long AMBIENT_INTERVAL_MS = TimeUnit.SECONDS.toMillis(20);
    private void refreshDisplayAndSetNextUpdate() {
        if (isAmbient()) {
            // Implement data retrieval and update the screen for ambient mode
        } else {
            // Implement data retrieval and update the screen for interactive mode
        }
        long timeMs = System.currentTimeMillis();
        // Schedule a new alarm
        if (isAmbient()) {
            // Calculate the next trigger time
            long delayMs = AMBIENT_INTERVAL_MS - (timeMs % AMBIENT_INTERVAL_MS);
            long triggerTimeMs = timeMs + delayMs;
            ambientUpdateAlarmManager.setExact(
                AlarmManager.RTC_WAKEUP,
                triggerTimeMs,
                ambientUpdatePendingIntent);
        } else {
            // Calculate the next trigger time for interactive mode
        }
    }
    
安排下次闹钟时间

通过替换 onEnterAmbient() 方法和 onUpdateAmbient() 方法,安排闹钟在 Activity 进入微光模式或 Activity 已处于微光模式时更新屏幕:

Kotlin

    override fun onEnterAmbient(ambientDetails: Bundle?) {
        super.onEnterAmbient(ambientDetails)

        refreshDisplayAndSetNextUpdate()
    }

    override fun onUpdateAmbient() {
        super.onUpdateAmbient()
        refreshDisplayAndSetNextUpdate()
    }
    

Java

    @Override
    public void onEnterAmbient(Bundle ambientDetails) {
        super.onEnterAmbient(ambientDetails);
        refreshDisplayAndSetNextUpdate();
    }

    @Override
    public void onUpdateAmbient() {
        super.onUpdateAmbient();
        refreshDisplayAndSetNextUpdate();
    }
    

注意:在本例中,每当需要更新屏幕时,就会调用 refreshDisplayAndSetNextUpdate() 方法。如需查看关于何时调用此方法的更多示例,请参阅 AlwaysOn 示例

取消闹钟

当设备切换到交互模式时,在 onExitAmbient() 方法中取消闹钟:

Kotlin

    override fun onExitAmbient() {
        super.onExitAmbient()

        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent)
    }
    

Java

    @Override
    public void onExitAmbient() {
        super.onExitAmbient();
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent);
    }
    

当用户退出或停止 Activity 时,在 Activity 的 onDestroy() 方法中取消闹钟:

Kotlin

    override fun onDestroy() {
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent)
        super.onDestroy()
    }
    

Java

    @Override
    public void onDestroy() {
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent);
        super.onDestroy();
    }
    

保持向后兼容性

在搭载低于 5.1(API 级别 22)版的 Android 系统的 Wear 设备上,支持微光模式的 Activity 会自动回退为普通 Activity。不需要特殊的应用代码来支持搭载这些 Android 版本的设备。当设备切换到微光模式时,设备会返回主屏幕并退出您的 Activity。

如果您的应用不应在搭载版本低于 5.1 的 Android 设备上安装或更新,请使用以下代码更新您的清单:

    <uses-library android:name="com.google.android.wearable" android:required="true" />