在 Wear 上讓應用程式持續顯示

當使用者不再使用手錶時,Wear OS 會自動將應用程式改為低功率模式。這就是所謂的「系統微光模式」。如果使用者在特定時間範圍內再次與手錶互動,Wear OS 會將使用者帶回先前離開的應用程式。

對於特定用途,例如,如果使用者想在跑步期間查看心率和配速,也可以控制低耗電模式 (微光模式) 所顯示的內容。在微光模式和互動模式中執行的 Wear OS 應用程式,稱為「一律啟用的應用程式」。

持續顯示應用程式會影響到電池續航力,因此在應用程式新增這項功能時,請注意這項影響。

設定專案

如要支援微光模式,請按照下列步驟操作:

  1. 根據「建立及執行穿戴式應用程式」頁面上的設定來建立或更新專案。
  2. WAKE_LOCK 權限新增至 Android 資訊清單檔案:
<uses-permission android:name="android.permission.WAKE_LOCK" />

使用 AmbientModeSupport 類別的微光模式

如需使用 AmbientModeSupport 類別,請執行以下操作:

  1. 建立 FragmentActivity 的子類別或其中一個子類別。
  2. 導入 AmbientCallbackProvider 介面,如以下範例所示。覆寫 getAmbientCallback() 方法,以提供回應 Android 系統微光事件所需的回呼 (在後續步驟中,您將建立自訂回呼類別。)

    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
        }
    }
    

如需詳細資訊和最佳做法,請參閱 GitHub 上的「AlwaysOnKotlin 範例」。

使用 WearableActivity 類別的微光模式

對於新專案和現有專案,您可以透過更新專案設定,新增支援 Wear 應用程式的微光模式。

建立支援微光模式的活動

您可以在活動中使用 WearableActivity 類別啟用微光模式,如下所示:

  1. 建立可擴充 WearableActivity 的活動。
  2. 在活動中的 onCreate() 方法中呼叫 setAmbientEnabled() 方法。

下列程式碼片段說明如何在活動中啟用微光模式:

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();
        ...
    }

處理模式之間的轉換

如果使用者在應用程式顯示期間已有一段時間未與應用程式互動,或是螢幕被覆蓋,系統會把活動切換為微光模式。

應用程式切換至微光模式後,請將活動使用者介面更新為更基本的版面配置以降低耗電量。使用含有少量白色圖像和文字的黑色背景。

如要簡化從互動模式到微光模式的轉換作業,建議您在螢幕上保留類似的項目位置。

注意:在微光模式下,請停用螢幕上的任何互動元素,例如按鈕。

當活動切換至微光模式時,系統會呼叫微光回呼的 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);
}

當使用者輕觸螢幕或抬起手腕時,活動就會從微光模式切換回互動模式。系統會呼叫 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() 方法:

Kotlin

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

Java

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

我們不建議在微光模式下每分鐘更新 Wear OS 應用程式超過一次。如果是需要頻繁更新的應用程式,請使用 AlarmManager 物件以喚醒處理器,並增加螢幕更新的頻率。

如要執行在微光模式下更頻繁地更新內容的鬧鐘,請按照下列步驟操作:

  1. 準備鬧鐘管理員。
  2. 設定更新的頻率。
  3. 檢查裝置目前是否處於微光模式,並在活動切換至微光模式時,安排下次更新作業。
  4. 在活動切換至互動模式或活動停止時取消鬧鐘。

注意:鬧鐘管理員可在觸發時建立新的活動執行個體。為避免這種行為,請使用資訊清單中的 android:launchMode="singleInstance" 參數來宣告活動。

以下各節將詳細說明這些步驟。

準備鬧鐘管理員

鬧鐘管理員會啟動 PendingIntent 來更新螢幕並安排下一個鬧鐘。以下範例說明如何在活動的 onCreate() 方法中宣告鬧鐘管理員和待處理意圖:

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);
    ...
}
更新畫面並安排資料更新時程

在這個範例活動中,鬧鐘管理員在微光模式下每 20 秒觸發一次。當計時器運作時,鬧鐘會觸發意圖以更新畫面,然後設定下一次更新的延遲時間。

以下範例說明如何更新畫面上的資訊,並設定下一次更新的鬧鐘:

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() 方法,設定鬧鐘以更新畫面,如下列範例所示:

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() 方法。如需更多呼叫這個方法的情境範例,請參閱 GitHub 上的「AlwaysOnKotlin 範例」。

取消鬧鐘

當裝置切換至互動模式時,請在 onExitAmbient() 方法中取消鬧鐘:

Kotlin

override fun onExitAmbient() {
    super.onExitAmbient()

    ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent)
}

Java

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

使用者離開或停止活動時,請在活動的 onDestroy() 方法中取消鬧鐘:

Kotlin

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

Java

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