安卓Settings模塊淺析

一.Settings的啟動(dòng)流程

(Settings部分源碼在packages/Settings下)

1.入口

Settings入口位于Settings.java.進(jìn)入Settings會(huì)發(fā)現(xiàn):它沒有重寫任何SettingsActiviy的方法酱塔,也沒有增加任何自己的方法,唯獨(dú)增加了許多靜態(tài)內(nèi)部類咏尝,如:

public? class Settings extends SettingsActivity {

public?? static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ }

public static class WirelessSettingsActivity extends SettingsActivity { /* empty */ }

public? static class SimSettingsActivity extends SettingsActivity { /*? empty */ }

.....

這些子類是為了啟動(dòng)特定獨(dú)立的Settings選項(xiàng)而創(chuàng)建的颊乘,例如在某個(gè)應(yīng)用里需要設(shè)置無線那么只需要啟動(dòng)WirelessSettingsActivity就可以了。因此Settings模塊啟動(dòng)流程主要看SettingsActivity類.

2.SettingsActivity

這邊只看主要方法

protected? void onCreate(Bundle savedState) {

super.onCreate(savedState);

long? startTime = System.currentTimeMillis();???? //Should happen before any call to getIntent()

getMetaData();//獲得Activity的額外數(shù)據(jù)mFragmentClass,如果可以獲得這個(gè)數(shù)據(jù),那么下面會(huì)去顯示mFragmentClass對(duì)應(yīng)的Activity。直接啟動(dòng)Settings模塊不會(huì)獲得這個(gè)數(shù)據(jù)

…...

mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) || intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT,

false);//判斷是否為shortcut(桌面小部件)進(jìn)入

...

...

mIsShowingDashboard=className.equals(Settings.class.getName())||

className.equals(Settings.WirelessSettings.class.getName())||

className.equals(Settings.DeviceSettings.class.getName())||

className.equals(Settings.PersonalSettings.class.getName())

||className.equals(Settings.WirelessSettings.class.getName());//判斷是否為主界面

//This is a "Sub Settings" when:

//- this is a real SubSetting

//- or:settings:show_fragment_as_subsetting is passed to the Intent

final?? boolean isSubSettings = this instanceof SubSettings ||intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING,false);//判斷是否為子界面

//If this is a sub settings, then apply the SubSettings Theme for? the ActionBar content insets

if (isSubSettings) {

//Check also that we are not a Theme Dialog as we don't want to override them

final int themeResId = getThemeResId();

if? (themeResId != R.style.Theme_DialogWhenLarge && themeResId!= R.style.Theme_SubSettingsDialogWhenLarge) {

?????????? setTheme(R.style.Theme_SubSettings);

}

}

setContentView(mIsShowingDashboard?R.layout.settings_main_dashboard: R.layout.settings_main_prefs);

if (savedState != null) {

//We are restarting from a previous saved state; used that to initialize, instead

//of starting fresh.

mSearchMenuItemExpanded=savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);

mSearchQuery= savedState.getString(SAVE_KEY_SEARCH_QUERY);

setTitleFromIntent(intent); //從Intent設(shè)置標(biāo)題

ArrayList? categories =

savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);//設(shè)置列表項(xiàng)

if

(categories != null) {

mCategories.clear();

mCategories.addAll(categories);

setTitleFromBackStack();//從返回棧設(shè)置標(biāo)題

}

mDisplayHomeAsUpEnabled

= savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);//是否顯示返回鍵

mDisplaySearch

= savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);//是否顯示搜索鍵

}

else {

if(!mIsShowingDashboard) {mDisplaySearch= false;

//UP will be shown only if it is a sub settings

if (mIsShortcut) {mDisplayHomeAsUpEnabled= isSubSettings;標(biāo)題欄的顯示

}

else if (isSubSettings) {

子界面??? mDisplayHomeAsUpEnabled= true;

}

else {?? mDisplayHomeAsUpEnabled= false; }

setTitleFromIntent(intent);

Bundle? initialArguments =

intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);

switchToFragment(initialFragmentName, initialArguments, true, false,

mInitialTitleResId, mInitialTitle, false);//跳轉(zhuǎn)到指定fragment}

else {

// No UP affordance if we are displaying the main Dashboard

mDisplayHomeAsUpEnabled = false;

// Show Search affordance

mDisplaySearch = true;

mInitialTitleResId = R.string.dashboard_title;

switchToFragment(DashboardSummary.class.getName(),null, false, false,mInitialTitleResId,mInitialTitle, false);

}?

}



DashBoardSummary.class

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

List categories =

((SettingsActivity)getActivity()).getDashboardCategories();//主界面列表項(xiàng)

mSummaryLoader= new SummaryLoader(getActivity(), categories);

..

… ...

@Override

public

View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle

savedInstanceState) {

return

inflater.inflate(R.layout.dashboard,

container, false);? ? //這個(gè)xml文件主要是一個(gè)類似ListView用來顯示主界面,把category傳進(jìn)去

}

總結(jié)一下:ActionBar(標(biāo)題欄)

繼承SettingsPreferenceFragment

(各子界面).......

點(diǎn)擊”日期與時(shí)間”

進(jìn)入日期設(shè)置界面Setting.java

DashBoardSummary(主列表界面)

繼承

從Setting進(jìn)入

SettingActivity

DateTimeSettingsActivity

DateTimeSettings

WirelessSettings

WirenessActivity

.......

二.Settings的界面顯示

繼承關(guān)系:

SettingsDrawerActivity

PreferenceFragment

SettingsActivity

SettingsPreferenceFragment

Settings部分一般使用Preference組件相信大家對(duì)Perference都比較熟悉了役耕,也就是我們常說的偏好設(shè)置,首選項(xiàng)設(shè)置聪廉,可以保存一些數(shù)據(jù)瞬痘,例如我們?cè)谏弦淮问褂玫臅r(shí)候的一些內(nèi)容,希望在

下一次啟動(dòng)后依然生效板熊,而不需要再進(jìn)行配置那么麻煩框全。一般這個(gè)時(shí)候我們便會(huì)使用perference鍵值對(duì)的方式來處理,使用碎片的首選項(xiàng)配置方法干签,即使用PreferenceFragement來實(shí)現(xiàn)津辩。

public

abstract class PreferenceFragment extends Fragment

以一個(gè)列表來展示首選項(xiàng)對(duì)象的層級(jí)關(guān)系,這些首選項(xiàng)將自動(dòng)地保存為SharedPreferences容劳,數(shù)據(jù)保存到data/data/包名/shared_prefs目錄下的包名_preferences.xml中

此外喘沿,所展示的首選項(xiàng)將會(huì)遵循系統(tǒng)首選項(xiàng)的視覺風(fēng)格,通過使用XML文件來創(chuàng)建各個(gè)首選項(xiàng)的視圖層級(jí)(可以被顯示在許多頁面)會(huì)非常簡(jiǎn)單

addPreferencesFromResource(R.xml.preferences);//加載XML布局文件

常見Preference組件:

PreferenceScreen:可以用作顯示設(shè)置界面竭贩,還可以啟動(dòng)Activity

PreferenceCategory:類似于LinearLayout蚜印,用于組合一組可設(shè)置標(biāo)題的Preference,使布局更具備層次感

SwitchPreference:類似常見控件的Switch留量,一個(gè)item晒哄,右側(cè)有一個(gè)Switch控件,用于通過SharePreferences存儲(chǔ)操作的設(shè)置值

ListPreference:類似常見控件的ListView肪获,一個(gè)item,點(diǎn)擊彈出一個(gè)ListView的Dialog柒傻,用于通過SharePreferences存儲(chǔ)操作的設(shè)置值

常用監(jiān)聽方法有:onPreferencechanged()onPreferenceClick()

onPreferenceTreeClick等

具體屬性用法參考http://blog.csdn.net/yanbober/article/details/47954653

三孝赫、SettingsProvider

代碼位置

frameworks/base/packages/SettingsProvider/src/com/Android/providers/settings/DatabaseHelper.Java

frameworks/base/packages/SettingsProvider/res/values/defaults.xml

在Android啟動(dòng)之后,我們通常需要根據(jù)自己的一些需要來設(shè)置一些符合我們使用習(xí)慣的屬性红符。例如:來電鈴聲青柄、鎖屏?xí)r間、日期格式等等预侯。而這些屬性的設(shè)置通常是有Settings為入口致开,通過SettingsProvider來進(jìn)行的。在第一次啟動(dòng)Android手機(jī)的時(shí)候會(huì)在默認(rèn)的文件中讀取設(shè)定的值萎馅,比如

在frameworks/base/packages/SettingsProvider/res/values/defaults.xml中双戳,需要添加相應(yīng)的項(xiàng)

name="def_dongle_name"

translatable="false">00:00:00:00:00

600000設(shè)置關(guān)屏超時(shí)時(shí)間的默認(rèn)值

102設(shè)置亮度的默認(rèn)值

false設(shè)置是否允許安裝非Market應(yīng)用程序的默認(rèn)值

這些數(shù)據(jù)主要是存儲(chǔ)在數(shù)據(jù)庫中,對(duì)應(yīng)的URI為:content://settings/system和content://settings/secure糜芳,這兩個(gè)是主要的飒货,目前也只是涉及到這兩個(gè)數(shù)據(jù)庫表的使用魄衅。例如:

當(dāng)需要獲得當(dāng)前wifi狀態(tài)的值,調(diào)用已封裝的方法如下:

Settings.Secure.getInt(getContentResolver()

, Settings.Secure.WIFI_ON);

當(dāng)需要獲得當(dāng)前時(shí)間日期自動(dòng)獲取塘辅,調(diào)用如下:

Settings.System.getInt(getContentResolver()

, "auto_time");

修改調(diào)用對(duì)應(yīng)的setInt方法晃虫。

在安卓6.0之前,系統(tǒng)修改的數(shù)據(jù)最后是存儲(chǔ)在data/data/com.android.providers.settings/databases目錄下的Settings.db中扣墩,在6.0之后該目錄下存放了一個(gè)backup數(shù)據(jù)庫哲银,里面的數(shù)據(jù)不是當(dāng)前系統(tǒng)設(shè)置的全部數(shù)據(jù),只有一部分內(nèi)容呻惕,另一個(gè)是journal數(shù)據(jù)庫荆责,無數(shù)據(jù)。現(xiàn)在的數(shù)據(jù)庫真正的數(shù)據(jù)存儲(chǔ)目錄在data/system/users/userId(我們沒開啟多用戶蟆融,userid為0)草巡。

CreateShortcut

CreateShortCut.Java繼承ListActivity,以ListView的形式顯示可以創(chuàng)建桌面快捷方式的Settings子項(xiàng)型酥,主要方法有:

getTargetIntent():獲取特定Intent

Intent

targetIntent = new Intent(Intent.ACTION_MAIN, null);

targetIntent.addCategory("com.android.settings.SHORTCUT");

targetIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

OnListItemClick():列表點(diǎn)擊事件

onQueryPackageManager():queryIntentActivities()方法獲取特定Activity的ResolveInfo信息List

因此山憨,創(chuàng)建shortcut所需的intent對(duì)象將會(huì)由CreateShortcut和其父類LuancherActivity共同構(gòu)建(詳見CreateShortcut的onListItemClick),在創(chuàng)建LuancherActivity的ActivityAdapter對(duì)象時(shí)弥喉,執(zhí)行了makeListItems()方法郁竟,該方法將應(yīng)用PackageManager的queryIntentActivities來按照intent對(duì)象查詢符合條件的activity。intent是從getTargetIntent函數(shù)返回的由境。不難發(fā)明棚亩,要想在CreateShortcut中顯示,Activity在必要有

android:name="com.android.settings.SHORTCUT" />

Battery模塊

一虏杰、使用類

?BatteryStatsService在內(nèi)部創(chuàng)建BatteryStatsImpl實(shí)例讥蟆,并傳入耗電量記錄文件batterystats.bin;

?ActivityManagerService創(chuàng)建并初始化BatteryStatsService纺阔,并傳入耗電量記錄文件batterystats.bin瘸彤;

?BatteryStatsHelper

--計(jì)算所有應(yīng)用的耗電(A

helper class for retrieving the power usage information for all

applications and services.)

?BatteryStats(abstract)實(shí)際用的是BatteryStatsImpl(providing

access to battery usage statistics, including information on

wakelocks, processes, packages, and services.)

?BatteryStatsImpl:提供App各部件運(yùn)行時(shí)間。

?BatterySipper表示一個(gè)應(yīng)用或服務(wù)的耗電量信息笛钝,包括

包名质况,圖標(biāo),耗電量玻靡,使用時(shí)間结榄,cpu時(shí)間,GPS囤捻、WIFI臼朗、等時(shí)間;而此類主要的方法加載名字和圖標(biāo)loadNameAndIcon(),從PackageManager中加載icon依溯,name老厌,packageName,并發(fā)送message到mHandler以便更新列表的顯示黎炉。

PowerProfile

--實(shí)際是從xml(power_profile.xml)中讀出里各個(gè)硬件cpu枝秤,屏幕藍(lán)牙wifi之類的耗電基值(記錄每種硬件1秒鐘耗多少電)。這樣慷嗜,

根據(jù)各個(gè)應(yīng)用的運(yùn)行時(shí)間就可以算出耗電了淀弹。(Reports

power consumption values for various device activities. Reads values

from an XML file.)

?BatteryEntry

--對(duì)應(yīng)的包名和icon,作為UI的數(shù)據(jù)來源庆械。(Wraps

the power usage data of a BatterySipper with information about

package name and icon image)

二薇溃、界面顯示

(

packages/apps/Settings/src/com/android/settings/fuelgauge下)

我們從Settings中Battery的入口處開始看起,先看PowerUsageSummary.java類缭乘,這個(gè)類的抬頭描述是:將應(yīng)用自從上次拔下USB線(或者交流電等其它充電方式)的耗電量排成列表沐序。這個(gè)類的具體的作用是顯示當(dāng)前電量以及篩選耗電量最多的前10個(gè)應(yīng)用,并且展示在ListView列表中堕绩。n8976代碼中PowerUsageSummary繼承PowerUsageBase,但大體過程如下:

1策幼、實(shí)例化廣播mBatteryInfoReceiver:用來在收到電量變化的廣播后更新耗電量列表refreshStats()

.

private

BatteryStatsHelper mStatsHelper;

private

BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {

@Override

public

void onReceive(Context context, Intent intent) {

String

action = intent.getAction();

if

(Intent.ACTION_BATTERY_CHANGED.equals(action)

&&

updateBatteryStatus(intent)) {

if

(!mHandler.hasMessages(MSG_REFRESH_STATS)) {

mHandler.sendEmptyMessageDelayed(MSG_REFRESH_STATS,

500);

}

}

}

};

2、實(shí)例化mHandler:用來傳給mStatsHelper奴紧,

Handler

mHandler = new Handler() {

@Override

public

void handleMessage(Message msg) {

switch

(msg.what) {

case

BatteryEntry.MSG_UPDATE_NAME_ICON:

BatteryEntry

entry = (BatteryEntry) msg.obj;

…..........................

case

MSG_REFRESH_STATS:

mStatsHelper.clearStats();

refreshStats();

}

super.handleMessage(msg);

}

};

3特姐、onAttach()實(shí)例化BatteryStatsHelper,即mStatsHelper

@Override

public

void onAttach(Activity activity) {

super.onAttach(activity);

mUm

= (UserManager) activity.getSystemService(Context.USER_SERVICE);

mStatsHelper

= new BatteryStatsHelper(activity, true);

}

4黍氮、onCreate()初始化mStatsHelper唐含;

加載R.xml.power_usage_summary,并定位preference

@Override

public

void onCreate(Bundle icicle) {

super.onCreate(icicle);

mStatsHelper.create(icicle);

addPreferencesFromResource(R.xml.power_usage_summary);

mAppListGroup

= (PreferenceGroup) findPreference(KEY_APP_LIST);

setHasOptionsMenu(true);

}

5沫浆、onResume()注冊(cè)廣播mBatteryInfoReceiver捷枯;

刷新耗電量列表refreshStats()

@Override

public

void onResume() {

super.onResume();

BatteryStatsHelper.dropFile(getActivity(),

BATTERY_HISTORY_FILE);

updateBatteryStatus(getActivity().registerReceiver(mBatteryInfoReceiver,

new

IntentFilter(Intent.ACTION_BATTERY_CHANGED)));

if

(mHandler.hasMessages(MSG_REFRESH_STATS)) {

mHandler.removeMessages(MSG_REFRESH_STATS);

mStatsHelper.clearStats();

}

refreshStats();

}

6、onPause()注銷廣播mBatteryInfoReceiver专执;mStatsHelper.pause()淮捆;移除mHandler的message

@Override

public

void onPause() {

BatteryEntry.stopRequestQueue();

mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);

getActivity().unregisterReceiver(mBatteryInfoReceiver);

super.onPause();

}

7、onDestory()

mStatsHelper.destroy()

@Override

public

void onDestroy() {

super.onDestroy();

if

(getActivity().isChangingConfigurations()) {

mStatsHelper.storeState();

BatteryEntry.clearUidCache();

}

}

三.preference顯示(以8976為例)

1.電量百分比SwitchPreference:

監(jiān)聽開關(guān),若開關(guān)打開則在子線程中發(fā)送廣播:

Intent

intent = new

Intent("android.intent.action.BATTERY_SHOW_PERCENTAGE");

ActivityManagerNative.broadcastStickyIntent(intent,

null,UserHandle.USER_ALL);

//

UserHandle.USER_ALL指對(duì)所有用戶

2.歷史用量BatteryHistoryPreference:

主要通過historyPref.setStats(mStatsHelper)將BatteryStatsHelper實(shí)例傳入他炊,BatteryStatsHelper可以獲取所有應(yīng)用的耗電信息;

3.耗電列表AppListGroup:

主要在refreshStats()方法里已艰,先清空mAppListGroup列表痊末,并設(shè)置其排序不按照添加順序顯示,接著添加耗電量的總信息mHistPref哩掺,通過mStatsHelper獲取耗電量的列表List

usageList凿叠,最后依次遍歷usageList,生成對(duì)應(yīng)的preference(在8976中是PowerGaugePreference,把每個(gè)BatteryEntry實(shí)例傳入),添加到mAppListGroup中盒件;在遍歷過程中可以通過

contine蹬碧,篩選出符合特定條件的BatterySipper,例如:

if

(((int) (percentOfTotal + .5)) < 1) {

continue;

}

//電量低于0.5%就不顯示

四、耗電量的計(jì)算

耗電量的計(jì)算在BatteryStatsHelper.java類中炒刁,計(jì)算耗電量的是processAppUsage方法恩沽。計(jì)算了手機(jī)上的每種硬件沒秒鐘耗費(fèi)了多少電量,每個(gè)應(yīng)用運(yùn)行時(shí)使用了哪幾種硬件翔始,每個(gè)硬件使用了所長時(shí)間罗心。

private

void processAppUsage(SparseArray? asUsers) {

//代表一個(gè)用戶對(duì)象,可以理解為這個(gè)類里面存儲(chǔ)了用戶的相關(guān)信息.

final

boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);

//判斷該次計(jì)算是否針對(duì)所有用戶城瞎,通過UserHandle的USER_ALL值來判斷渤闷,該值為-1

mStatsPeriod

= mTypeBatteryRealtimeUs;//此次統(tǒng)計(jì)電量的時(shí)間間隔.

BatterySipper

osSipper = null;

final

SparseArray uidStats = mStats.getUidStats();

final

int NU = uidStats.size();? ? ? // osSipper里面可以存儲(chǔ)一些后續(xù)我們要計(jì)算的值,然后通過BatteryStats類對(duì)象mStats來得到一個(gè)包含Uid的對(duì)象的SparseArray組數(shù),然后計(jì)算了一下這個(gè)數(shù)組的大小脖镀。

/**

*計(jì)算每個(gè)Uid代表的App的耗電量飒箭,因?yàn)锽atterySipper可計(jì)算的類型有三種:應(yīng)用,系統(tǒng)服務(wù),硬件類型,所以這個(gè)地方傳入的是DrainType.APP.還有其他類型如下:(定義在BatterySipper.java中)

/*public

enum DrainType {

IDLE,

CELL,

PHONE,

WIFI,

BLUETOOTH,

FLASHLIGHT,

SCREEN,

APP,

USER,

UNACCOUNTED,

OVERCOUNTED,

CAMERA

}*/

*/

for? (int iu = 0; iu < NU; iu++) {

final Uid u = uidStats.valueAt(iu);

final? BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP,u, 0);

/*

*6.0的對(duì)各個(gè)模塊的消耗都交給了單獨(dú)的類去計(jì)算,這些類都繼承于PowerCalculator抽象類:

*藍(lán)牙耗電:BluetoothPowerCalculator.java

*攝像頭耗電:CameraPowerCalculator.java

*CPU耗電:mCpuPowerCalculator.java

*手電筒耗電: FlashlightPowerCalculator.java

*無線電耗電: MobileRadioPowerCalculator.java

*傳感器耗電: SensorPowerCalculatormSensorPowerCalculator.java

*Wakelock耗電: WakelockPowerCalculator.java

*Wifi耗電: WifiPowerCalculator.java

*

*其中mStatsType的值為BatteryStats.STATS_SINCE_CHARGED,代表了我們的計(jì)算規(guī)則是從上次充滿電后數(shù)據(jù)蜒灰,還有一種規(guī)則是*STATS_SINCE_UNPLUGGED是拔掉USB線后的數(shù)據(jù)弦蹂。而mRawRealtimUs是當(dāng)前時(shí)間,mRawUptimeUs是運(yùn)行時(shí)間卷员。

*

*/

mCpuPowerCalculator.calculateApp(app,u, mRawRealtimeUs, mRawUptimeUs,mStatsType);

mWakelockPowerCalculator.calculateApp(app,u, mRawRealtimeUs, mRawUptimeUs, mStatsType);

mMobileRadioPowerCalculator.calculateApp(app,u, mRawRealtimeUs, mRawUptimeUs, mStatsType);

mWifiPowerCalculator.calculateApp(app,u, mRawRealtimeUs, mRawUptimeUs, mStatsType);

mBluetoothPowerCalculator.calculateApp(app,u, mRawRealtimeUs, mRawUptimeUs, mStatsType);

mSensorPowerCalculator.calculateApp(app,u, mRawRealtimeUs, mRawUptimeUs, mStatsType);

mCameraPowerCalculator.calculateApp(app,u, mRawRealtimeUs, mRawUptimeUs, mStatsType);

mFlashlightPowerCalculator.calculateApp(app,u, mRawRealtimeUs, mRawUptimeUs, mStatsType);

final? double totalPower = app.sumPower();// sumPower()計(jì)算總耗電量

if (DEBUG && totalPower != 0) {

Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),makemAh(totalPower)));

}

//? Add the app to the list if it is consuming power.

//添加進(jìn)mUsageList

if? (totalPower != 0 || u.getUid() == 0) {

//

// Add the app to the app list, WiFi, Bluetooth, etc, or into "Other

Users" list.

//

final int uid = app.getUid();

final? int userId = UserHandle.getUserId(uid);

//如果是wifi和藍(lán)牙就添加到mWifiSippers和mBluetoothSippers

if (uid == Process.WIFI_UID) {

? mWifiSippers.add(app);

}

else if (uid == Process.BLUETOOTH_UID) {

mBluetoothSippers.add(app);

}

else if (!forAllUsers && asUsers.get(userId) == null

&&

UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {

//

We are told to just report this user's apps as one large entry.

//如果我們的系統(tǒng)是單用戶系統(tǒng)盈匾,且當(dāng)前的userId號(hào)不在我們的統(tǒng)計(jì)范圍內(nèi),且其進(jìn)程id號(hào)是大于Process.FIRST_APPLICATION_UID(10000,系統(tǒng)分配給普通應(yīng)用的其實(shí)id號(hào)),我們就要將其存放到mUserSippers數(shù)組中毕骡,

List? list = mUserSippers.get(userId);

if (list == null) {

list = new ArrayList<>();

mUserSippers.put(userId, list);

}

list.add(app);

} else {

mUsageList.add(app);

}

if? (uid == 0) {

??? osSipper = app;//存入

? }

}

}? if (osSipper != null) {

// The device has probably been awake for longer than the screen on

// time and application wake lock time would account for.? Assign

// this remainder to the OS, if possible.

mWakelockPowerCalculator.calculateRemaining(osSipper,mStats, mRawRealtimeUs,

mRawUptimeUs,mStatsType);

osSipper.sumPower();//最終電量

}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末削饵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子未巫,更是在濱河造成了極大的恐慌窿撬,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叙凡,死亡現(xiàn)場(chǎng)離奇詭異劈伴,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)握爷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門跛璧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人新啼,你說我怎么就攤上這事追城。” “怎么了燥撞?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵座柱,是天一觀的道長迷帜。 經(jīng)常有香客問我,道長色洞,這世上最難降的妖魔是什么戏锹? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮火诸,結(jié)果婚禮上锦针,老公的妹妹穿的比我還像新娘。我一直安慰自己惭蹂,他們只是感情好伞插,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盾碗,像睡著了一般媚污。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上廷雅,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天耗美,我揣著相機(jī)與錄音,去河邊找鬼航缀。 笑死商架,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的芥玉。 我是一名探鬼主播蛇摸,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼灿巧!你這毒婦竟也來了赶袄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤抠藕,失蹤者是張志新(化名)和其女友劉穎饿肺,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盾似,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡敬辣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了零院。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溉跃。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖告抄,靈堂內(nèi)的尸體忽然破棺而出撰茎,到底是詐尸還是另有隱情,我是刑警寧澤玄妈,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布乾吻,位于F島的核電站,受9級(jí)特大地震影響拟蜻,放射性物質(zhì)發(fā)生泄漏绎签。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一酝锅、第九天 我趴在偏房一處隱蔽的房頂上張望诡必。 院中可真熱鬧,春花似錦搔扁、人聲如沸爸舒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扭勉。三九已至,卻和暖如春苛聘,著一層夾襖步出監(jiān)牢的瞬間涂炎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國打工设哗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唱捣,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓网梢,卻偏偏與公主長得像震缭,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子战虏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容