让应用始终显示在 Wear 上

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

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

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

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

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

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

请参阅以下相关资源:

配置您的项目

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

  1. 根据创建并运行穿戴式设备应用页面上的配置创建或更新您的项目。
  2. WAKE_LOCK 权限添加到 Android 清单文件中:
    <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 停止时取消闹钟。

注意:闹钟管理器可能会在触发时创建新的 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();
    }
    

保持后向兼容性

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

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

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