首页 > 移动开发 > Android > Android 屏保开发 (PowerManager & DreamService)
2021
11-28

Android 屏保开发 (PowerManager & DreamService)

一、应用中使用DreamService

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<ResolveInfo> 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);
    }

二、PowerManagerService里面屏保处理

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
            }
        }
    }
最后编辑:
作者:搬运工

留下一个回复

你的email不会被公开。