1. 程式人生 > >Android FrameWork深入分析DreamManagerService實現自己的系統屏保

Android FrameWork深入分析DreamManagerService實現自己的系統屏保

Framework分析DreamService,實現自己的系統屏保

現在有一個需求是在手機一段時間不用的情況下,顯示自己的系統屏保功能
下面是涉及到的程式碼

  • /frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java 顯示屏保功能的入口
  • /frameworks/base/services/core/java/com/android/server/dreams/DreamManagerService.java 管理屏保APP
  • /frameworks/base/services/core/java/com/android/server/dreams/DreamController.java
    屏保具體的呼起
  • /packages/apps/DeskClock/src/com/android/deskclock/Screensaver.java 鬧鐘模組預設的屏保

首先需要熟悉PowerManagerService的主要邏輯

這邊個人推薦blog:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=30510400&id=5569393

接著上面的部落格介紹,大概畫了一個流程,其實我們只要在系統啟動屏保的時候繫結到自己的APP就行了

![主要過程](https://img-blog.csdn.net/20170703170434850?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3pxc2Rx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

現在我們主要分析DreamManagerService的邏輯
DreamManagerService怎麼找到屏保APP?

找到入口了,那麼DreamManagerService怎麼啟動屏保app呢?
private final class LocalService extends DreamManagerInternal {
@Override
public void startDream(boolean doze) {
startDreamInternal(doze);
}
}
private void startDreamInternal(boolean doze) {
final int userId = ActivityManager.getCurrentUser();
//獲得屏保指定的app
final ComponentName dream = chooseDreamForUser(doze, userId);
if (dream != null) {
synchronized (mLock) {
startDreamLocked(dream, false /isTest

/, doze, userId);
}
}
}

我們分析一下chooseDreamForUser()做了什麼處理
//doze是系統另外一種狀態了,系統處於節電狀態,網路什麼都會禁止掉
//我們讓PowerMangerService呼叫startDream()的時候傳入doze = false;
//可以看出這邊是找到所有的屏保app預設使用第一個
private ComponentName chooseDreamForUser(boolean doze, int userId) {
Slog.w(TAG, “Dream 0 ” + doze + userId);
if (doze) {
ComponentName dozeComponent = getDozeComponent(userId);
return validateDream(dozeComponent) ? dozeComponent : null;
}
ComponentName[] dreams = getDreamComponentsForUser(userId);
return dreams != null && dreams.length != 0 ? dreams[0] : null;
}

看一下getDreamComponentsForUser()做了什麼處理

private ComponentName[] getDreamComponentsForUser(int userId) {
//這裡是從settings.db獲得預設的屏保app中dreameService的包名
String names = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Settings.Secure.SCREENSAVER_COMPONENTS,
userId);
//我們這邊寫死定義為自己的app這
names = “com.ucon.displaydream/com.ucon.displaydream.”;
Slog.w(TAG, “names 1 : ” + names);
ComponentName[] components = componentsFromString(names);
Slog.w(TAG, “names 2 : ” + components);

    // first, ensure components point to valid services
    List<ComponentName> validComponents = new ArrayList<ComponentName>();
    if (components != null) {
        for (ComponentName component : components) {
            if (validateDream(component)) {
                validComponents.add(component);
            }
        }
    }

}

**Settings.Secure.getStringForUser(mContext.getContentResolver(),Settings.Secure.SCREENSAVER_COMPONENTS, userId);獲得預設的componentName
是 com.android.deskclock/com.android.deskclock.Screensaver**

然後開啟screensaver的程式碼
package com.android.deskclock;

import android.app.AlarmManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.service.dreams.DreamService;
import android.util.Log;
import android.view.View;
import android.widget.TextClock;

import com.android.deskclock.Utils.ScreensaverMoveSaverRunnable;

public class Screensaver extends DreamService {

public static final int ORIENTATION_CHANGE_DELAY_MS = 250;

private static final boolean DEBUG = false;
private static final String TAG = "DeskClock/Screensaver";

private View mContentView, mSaverView;
private View mAnalogClock, mDigitalClock;
private String mDateFormat;
private String mDateFormatForAccessibility;

private final Handler mHandler = new Handler();

private final ScreensaverMoveSaverRunnable mMoveSaverRunnable;

// Thread that runs every midnight and refreshes the date.
private final Runnable mMidnightUpdater = new Runnable() {
    @Override
    public void run() {
        Utils.updateDate(mDateFormat, mDateFormatForAccessibility, mContentView);
        Utils.setMidnightUpdater(mHandler, mMidnightUpdater);
    }
};

/**
 * Receiver to handle time reference changes.
 */
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (DEBUG) Log.v(TAG, "Screensaver onReceive, action: " + action);

        if (action == null) {
            return;
        }

        if (action.equals(Intent.ACTION_TIME_CHANGED)
                || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
            Utils.updateDate(mDateFormat, mDateFormatForAccessibility, mContentView);
            Utils.refreshAlarm(Screensaver.this, mContentView);
            Utils.setMidnightUpdater(mHandler, mMidnightUpdater);
        } else if (action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
            Utils.refreshAlarm(Screensaver.this, mContentView);
        }
    }
};

public Screensaver() {
    if (DEBUG) Log.d(TAG, "Screensaver allocated");
    mMoveSaverRunnable = new ScreensaverMoveSaverRunnable(mHandler);
}

@Override
public void onCreate() {
    if (DEBUG) Log.d(TAG, "Screensaver created");
    super.onCreate();

    setTheme(R.style.DeskClockParentTheme);

    mDateFormat = getString(R.string.abbrev_wday_month_day_no_year);
    mDateFormatForAccessibility = getString(R.string.full_wday_month_day_no_year);
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    if (DEBUG) Log.d(TAG, "Screensaver configuration changed");
    super.onConfigurationChanged(newConfig);
    mHandler.removeCallbacks(mMoveSaverRunnable);
    layoutClockSaver();
    mHandler.postDelayed(mMoveSaverRunnable, ORIENTATION_CHANGE_DELAY_MS);
}

@Override
public void onAttachedToWindow() {
    if (DEBUG) Log.d(TAG, "Screensaver attached to window");
    super.onAttachedToWindow();

    // We want the screen saver to exit upon user interaction.
    setInteractive(false);

    setFullscreen(true);

    layoutClockSaver();

    // Setup handlers for time reference changes and date updates.
    IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_TIME_CHANGED);
    filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
    registerReceiver(mIntentReceiver, filter);
    Utils.setMidnightUpdater(mHandler, mMidnightUpdater);

    mHandler.post(mMoveSaverRunnable);
}

@Override
public void onDetachedFromWindow() {
    if (DEBUG) Log.d(TAG, "Screensaver detached from window");
    super.onDetachedFromWindow();

    mHandler.removeCallbacks(mMoveSaverRunnable);

    // Tear down handlers for time reference changes and date updates.
    Utils.cancelMidnightUpdater(mHandler, mMidnightUpdater);
    unregisterReceiver(mIntentReceiver);
}

private void setClockStyle() {
    Utils.setClockStyle(this, mDigitalClock, mAnalogClock,
            ScreensaverSettingsActivity.KEY_CLOCK_STYLE);
    mSaverView = findViewById(R.id.main_clock);
    boolean dimNightMode = PreferenceManager.getDefaultSharedPreferences(this)
            .getBoolean(ScreensaverSettingsActivity.KEY_NIGHT_MODE, false);
    Utils.dimClockView(dimNightMode, mSaverView);
    setScreenBright(!dimNightMode);
}

private void layoutClockSaver() {
    setContentView(R.layout.desk_clock_saver);
    mDigitalClock = findViewById(R.id.digital_clock);
    mAnalogClock = findViewById(R.id.analog_clock);
    setClockStyle();
    Utils.setTimeFormat((TextClock)mDigitalClock,
        (int)getResources().getDimension(R.dimen.main_ampm_font_size));

    mContentView = (View) mSaverView.getParent();
    mSaverView.setAlpha(0);

    mMoveSaverRunnable.registerViews(mContentView, mSaverView);

    Utils.updateDate(mDateFormat, mDateFormatForAccessibility, mContentView);
    Utils.refreshAlarm(Screensaver.this, mContentView);
}

}

我們就知道DreamService與系統屏保的關係了,但是怎麼bind啟動屏保的dreameService的呢?
DreamManagerService的實現程式碼在DreamController中實現的
public void startDream(Binder token, ComponentName name,
boolean isTest, boolean canDoze, int userId) {
stopDream(true /immediate/);

    Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream");
    try {
        // Close the notification shade. Don't need to send to all, but better to be explicit.
        mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);

        Slog.i(TAG, "Starting dream: name=" + name
                + ", isTest=" + isTest + ", canDoze=" + canDoze
                + ", userId=" + userId);

        mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId);

        try {
            mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM);
        } catch (RemoteException ex) {
            Slog.e(TAG, "Unable to add window token for dream.", ex);
            stopDream(true /*immediate*/);
            return;
        }

        Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
        intent.setComponent(name);
        intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        try {
            //這邊就是怎麼啟動屏保的service了
            if (!mContext.bindServiceAsUser(intent, mCurrentDream,
                    Context.BIND_AUTO_CREATE, new UserHandle(userId))) {
                Slog.e(TAG, "Unable to bind dream service: " + intent);
                stopDream(true /*immediate*/);
                return;
            }

}

我們看一下DreamService顯示屏保的時候怎麼退出和喚醒螢幕
@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, “Waking up on keyEvent”);
wakeUp();
return true;
} else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (mDebug) Slog.v(TAG, “Waking up on back key”);
//喚醒螢幕
wakeUp();
return true;
}
return mWindow.superDispatchKeyEvent(event);
}

/** {@inheritDoc} */
@Override
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
    if (!mInteractive) {
        if (mDebug) Slog.v(TAG, "Waking up on keyShortcutEvent");
        wakeUp();
        return true;
    }
    return mWindow.superDispatchKeyShortcutEvent(event);
}

public void onWakeUp() {
//螢幕喚醒後消滅自己
finish();
}