1.DreamService简介
白日梦是Android设备的新型互动屏保模式。当设备置入底座或充电闲置状态时(屏幕没有关闭),此模式自动激活。白日梦模式每次显示一个,可以是纯粹的视觉效果,在用户触摸时消失,也可以是响应用户所有输入的交互式应用。您的白日梦将运行在您应用的进程内,并可以访问所有的AndroidUI工具包,可以使用视图、布局和动画等。所以它比动态壁纸或应用窗口小部件更具表现力。
您可以由实现DreamService的子类来创建一个白日梦。DreamService的API被设计成类似Activity。在通过诸如onAttachedToWindows()之类的方法获得窗口后,就可以给setContentView()设定一个布局资源ID或View,来为您的白日梦设置UI。
android.service.dreams.DreamService子类来实现的,下面是一些关键的方法:
onAttachedToWindow():初始设置 onDreamingStarted():启动动画和计时器
onDreamingStopped():停止动画
onDetachedFromWindow():清除你在onAttachedToWindow()中构建的所有东西 可能会调用的一些重要方法:
setContentView():设置Daydream场景
setInteractive(boolean):默认情况下,用户触摸时Daydream会退出。如果你希望与用户交互,则调用setInteractive(true)
setFullscreen(boolean):隐藏状态栏
setScreenBright(boolean):默认情况下,Daydream是全亮度模式,设置为false会降低屏幕亮度
为了使您的白日梦对系统可用,您需要在manifest文件中的元素下声明您的DreamService。然后,您必须在其中加入具有”android.service.dreams.DreamService”动作的intentfilter。例如:
<service
android:name=".MyDream"
android:exported="true"
android:icon="@drawable/dream_icon"
android:label="@string/dream_label" >
<intent-filter>
<action android:name="android.service.dreams.DreamService" />
<category android:name="android.intent.category.DEFAULT" />
intent-filter>
service>
2.启动屏保
由于DreamManagerService是隐藏的,不能直接使用,需要通过IDreamManager来进行操作
private final IDreamManager mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.getService(DreamService.DREAM_SERVICE));
启动屏保时,先获取系统安装的所有屏保,可以得到我们自己的开发的屏保存
PackageManager pm = mContext.getPackageManager();
Intent dreamIntent = new Intent(DreamService.SERVICE_INTERFACE);
List resolveInfos = pm.queryIntentServices(dreamIntent,
PackageManager.GET_META_DATA);
然后再将屏保设置我们自己开发的
public void setActiveDream(ComponentName dream) {
logd("setActiveDream(%s)", dream);
if (mDreamManager == null)
return;
try {
ComponentName[] dreams = { dream };
mDreamManager.setDreamComponents(dream == null ? null : dreams);
} catch (RemoteException e) {
Log.w(TAG, "Failed to set active dream to " + dream, e);
}
}
如果没有设置,系统会有一个默认的屏保,使用以下方法可以获取默认屏保
public ComponentName getDefaultDream() {
if (mDreamManager == null)
return null;
try {
return mDreamManager.getDefaultDreamComponent();
} catch (RemoteException e) {
Log.w(TAG, "Failed to get default dream", e);
return null;
}
}
启动屏保直接调用startDream方法即可
public void startDreaming() {
logd("startDreaming()" + (mDreamManager == null));
if (mDreamManager == null)
return;
try {
mDreamManager.dream();
} catch (RemoteException e) {
Log.w(TAG, "Failed to dream", e);
}
}
3.停止屏保
用户有任何操作,屏保都会停止,实现逻辑是在DreamService里面的
// begin Window.Callback methods
/** {@inheritDoc} */
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
// TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK
if (!mInteractive) {
if (mDebug) Slog.v(TAG, "Finishing on keyEvent");
safelyFinish();
return true;
} else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (mDebug) Slog.v(TAG, "Finishing on back key");
safelyFinish();
return true;
}
return mWindow.superDispatchKeyEvent(event);
}
/** {@inheritDoc} */
@Override
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
if (!mInteractive) {
if (mDebug) Slog.v(TAG, "Finishing on keyShortcutEvent");
safelyFinish();
return true;
}
return mWindow.superDispatchKeyShortcutEvent(event);
}
/** {@inheritDoc} */
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
// TODO: create more flexible version of mInteractive that allows clicks
// but finish()es on any other kind of activity
if (!mInteractive) {
if (mDebug) Slog.v(TAG, "Finishing on touchEvent");
safelyFinish();
return true;
}
return mWindow.superDispatchTouchEvent(event);
}
/** {@inheritDoc} */
@Override
public boolean dispatchTrackballEvent(MotionEvent event) {
if (!mInteractive) {
if (mDebug) Slog.v(TAG, "Finishing on trackballEvent");
safelyFinish();
return true;
}
return mWindow.superDispatchTrackballEvent(event);
}
/** {@inheritDoc} */
@Override
public boolean dispatchGenericMotionEvent(MotionEvent event) {
if (!mInteractive) {
if (mDebug) Slog.v(TAG, "Finishing on genericMotionEvent");
safelyFinish();
return true;
}
return mWindow.superDispatchGenericMotionEvent(event);
}
1.有几个系统设置的值是否启动屏保有关:
//是否打开屏保
mDreamsEnabledSetting = (Settings.Secure.getIntForUser(resolver,
Settings.Secure.SCREENSAVER_ENABLED,
mDreamsEnabledByDefaultConfig ? 1 : 0,
UserHandle.USER_CURRENT) != 0);
//休眠的时候是否打开屏保
mDreamsActivateOnSleepSetting = (Settings.Secure.getIntForUser(resolver,
Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
mDreamsActivatedOnSleepByDefaultConfig ? 1 : 0,
UserHandle.USER_CURRENT) != 0);
mDreamsActivateOnDockSetting = (Settings.Secure.getIntForUser(resolver,
Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
mDreamsActivatedOnDockByDefaultConfig ? 1 : 0,
UserHandle.USER_CURRENT) != 0);
2.updatePowerStateLocked
PowerManagerService里面主要是处理电源相关的逻辑,所以updatePowerStateLocked()方法会时时调用,更新电源状态,然后根据不同状态进行不同处理
private void updatePowerStateLocked() {
if (!mSystemReady || mDirty == 0) {
return;
}
// Phase 0: Basic state updates.
updateIsPoweredLocked(mDirty);
updateStayOnLocked(mDirty);
// Phase 1: Update wakefulness.
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
final long now = SystemClock.uptimeMillis();
int dirtyPhase2 = 0;
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0;
updateWakeLockSummaryLocked(dirtyPhase1);
updateUserActivitySummaryLocked(now, dirtyPhase1);
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
}
// Phase 2: Update dreams and display power state.
updateDreamLocked(dirtyPhase2);
updateDisplayPowerStateLocked(dirtyPhase2);
// Phase 3: Send notifications, if needed.
if (mDisplayReady) {
sendPendingNotificationsLocked();
}
// Phase 4: Update suspend blocker.
// Because we might release the last suspend blocker here, we need to make sure
// we finished everything else first!
updateSuspendBlockerLocked();
}
在updatePowerStateLocked方法里面,会更新屏保状态,调用updateDreamLocked方法
private void updateDreamLocked(int dirty) {
if ((dirty & (DIRTY_WAKEFULNESS
| DIRTY_USER_ACTIVITY
| DIRTY_WAKE_LOCKS
| DIRTY_BOOT_COMPLETED
| DIRTY_SETTINGS
| DIRTY_IS_POWERED
| DIRTY_STAY_ON
| DIRTY_PROXIMITY_POSITIVE
| DIRTY_BATTERY_STATE)) != 0) {
scheduleSandmanLocked();
}
}
scheduleSandmanLocked方法里面会发送一个消息,
private void scheduleSandmanLocked() {
if (!mSandmanScheduled) {
mSandmanScheduled = true;
Message msg = mHandler.obtainMessage(MSG_SANDMAN);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
MSG_SANDMAN消息是在PowerManagerHandler里面处理的
private final class PowerManagerHandler extends Handler {
public PowerManagerHandler(Looper looper) {
super(looper, null, true /*async*/);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_USER_ACTIVITY_TIMEOUT:
handleUserActivityTimeout();
break;
case MSG_SANDMAN:
handleSandman();
break;
case MSG_SCREEN_ON_BLOCKER_RELEASED:
handleScreenOnBlockerReleased();
break;
case MSG_CHECK_IF_BOOT_ANIMATION_FINISHED:
checkIfBootAnimationFinished();
break;
}
}
}
主要处理逻辑是在handleSandman里面,
private void handleSandman() { // runs on handler thread
// Handle preconditions.
boolean startDreaming = false;
synchronized (mLock) {
mSandmanScheduled = false;
//首先判断是否可以启动屏保
boolean canDream = canDreamLocked();
if (DEBUG_SPEW) {
Slog.d(TAG, "handleSandman: canDream=" + canDream
+ ", mWakefulness=" + wakefulnessToString(mWakefulness));
}
//如果可以启动屏保且当前锁状态为WAKEFULNESS_NAPPING时,由表示需要启动屏保
if (canDream && mWakefulness == WAKEFULNESS_NAPPING) {
startDreaming = true;
}
}
// Start dreaming if needed.
// We only control the dream on the handler thread, so we don't need to worry about
// concurrent attempts to start or stop the dream.
boolean isDreaming = false;
if (mDreamManager != null) {
if (startDreaming) {
//启动屏保
mDreamManager.startDream();
}
isDreaming = mDreamManager.isDreaming();
}
// Update dream state.
// We might need to stop the dream again if the preconditions changed.
boolean continueDreaming = false;
synchronized (mLock) {
if (isDreaming && canDreamLocked()) {
//如果屏保正在运行且相关设置允许启动屏保,将状态设置为WAKEFULNESS_DREAMING
if (mWakefulness == WAKEFULNESS_NAPPING) {
mWakefulness = WAKEFULNESS_DREAMING;
mDirty |= DIRTY_WAKEFULNESS;
mBatteryLevelWhenDreamStarted = mBatteryLevel;
updatePowerStateLocked();
continueDreaming = true;
} else if (mWakefulness == WAKEFULNESS_DREAMING) {
if (!isBeingKeptAwakeLocked()
&& mBatteryLevel < mBatteryLevelWhenDreamStarted
3.mWakefulness状态变量与屏保启动关闭逻辑
从代码可以看出mWakefulness变量与是否启动屏保密切相关,当启动屏保时,会调用napInternal –>napNoUpdateLocked
在napNoUpdateLocked方法中,状态发生变化
private boolean napNoUpdateLocked(long eventTime) {
......
Slog.i(TAG, "Nap time...");
mDirty |= DIRTY_WAKEFULNESS;
mWakefulness = WAKEFULNESS_NAPPING; //此状态下,屏保会被启动
return true;
}
在停止屏保时,会依次调用handleDreamFinishedLocked –>wakeUpNoUpdateLocked
在wakeUpNoUpdateLocked方法里面,mWakefulness 状态发生变化
private boolean wakeUpNoUpdateLocked(long eventTime) {
..............
mLastWakeTime = eventTime;
mWakefulness = WAKEFULNESS_AWAKE; //屏保停止后,状态为WAKEFULNESS_AWAKE
mDirty |= DIRTY_WAKEFULNESS;
userActivityNoUpdateLocked(
eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
return true;
}
4.启动停止屏保还可以通过广播的形式来进行
filter = new IntentFilter();
filter.addAction(Intent.ACTION_DREAMING_STARTED);
filter.addAction(Intent.ACTION_DREAMING_STOPPED);
mContext.registerReceiver(new DreamReceiver(), filter, null, mHandler);
private final class DreamReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
scheduleSandmanLocked();
}
}
}
通过分析代码scheduleSandmanLocked方法并没有真正停止屏保,只是发送了一个消息,所以直接发ACTION_DREAMING_STOPPED广播是无法停止屏保的
可以添加如下逻辑处理
private final class DreamReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
scheduleSandmanLocked();
// Patch Begin
if(Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())){
if(mDreamManager != null){
mDreamManager.stopDream();
mScreenSaverTime = 0;
Log.v(TAG,"DreamReceiver stopDream and reset time");
}
}
//Patch end
}
}
}