<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
初識Android PowerManagerService省電模式對省電模式進行了一個初步認識,介紹了一些概念,以及對省電模式環境初始化的程式碼進行了簡單分析。讀者需要仔細閱讀第一篇文章,再來看這一篇文章。
開啟省電模式,有三種方式:
本文只關注如下內容:
只要掌握了上面2點內容,自動模式、動態模式,都可以自行分析。
現在以手動開啟省電模式為例,分析省電模式的開啟過程。
從初識Android PowerManagerService省電模式可知,在 Settings->Battery->Battery Saver 介面,可以手動開啟省電模式,呼叫程式碼如下
最終會呼叫 PowerManagerService 對應的方法:
public boolean setPowerSaveModeEnabled(boolean enabled) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER) != PackageManager.PERMISSION_GRANTED) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); } final long ident = Binder.clearCallingIdentity(); try { return setLowPowerModeInternal(enabled); } finally { Binder.restoreCallingIdentity(ident); } } private boolean setLowPowerModeInternal(boolean enabled) { synchronized (mLock) { // 充電狀態下,不允許開啟/關閉省電模式 if (mIsPowered) { return false; } mBatterySaverStateMachine.setBatterySaverEnabledManually(enabled); return true; } }
在 AOSP 的設計中,省電模式和充電狀態是衝突的。如果裝置處於省電模式狀態,此時插入充電器,那麼一定會關閉省電模式。如果裝置處於充電狀態,那麼是不允許開啟省電模式的。
說實話,我不是很認同這種設計。我認為省電模式是使用者的強烈個人意願,只能由使用者自己決定開啟或者關閉。
從上面程式碼可知,開啟省電模式時,通過 BatterySaverStateMachine#setBatterySaverEnabledManually() 方法,把指令傳給狀態機
public void setBatterySaverEnabledManually(boolean enabled) { synchronized (mLock) { updateStateLocked(true, enabled); } }
狀態機通過 updateStateLocked() 更新內部狀態,然後根據狀態執行相應的操作。 注意,這裡的第一個參數列示是否是使用者手動開啟省電模式,值為 true,第二個參數列示是否開啟省電模式,根據我們分析的例子,這裡的值為 true。
private void updateStateLocked(boolean manual, boolean enable) { if (!manual && !(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) { return; // Not fully initialized yet. } switch (mState) { case STATE_OFF: { if (!mIsPowered) { // 非充電模式,才允許操作省電模式 if (manual) { // 手動操作 if (!enable) { return; } // 使用者手動開啟省電模式 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, BatterySaverController.REASON_MANUAL_ON); hideStickyDisabledNotification(); // 狀態切換為 STATE_MANUAL_ON mState = STATE_MANUAL_ON; } else if (isAutomaticModeActiveLocked() && isInAutomaticLowZoneLocked()) { // ... 自動模式 } else if (isDynamicModeActiveLocked() && isInDynamicLowZoneLocked()) { // ... 動態模式 } } break; } // ... } }
狀態機裡的預設狀態是 STATE_OFF,表示省電模式預設關閉。
通過 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, BatterySaverController.REASON_MANUAL_ON);
開啟省電模式,然後把狀態切換為 STATE_MANUAL_ON。對於每一次狀態切換,我們都要注意,因此這會影響下一次狀態切換。
private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason) { enableBatterySaverLocked(enable, manual, intReason, reasonToString(intReason)); } private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason, String strReason) { final boolean wasEnabled = mBatterySaverController.isFullEnabled(); // 已經處於省電模式狀態 if (wasEnabled == enable) { return; } // 充電中,是不允許開啟省電模式的 if (enable && mIsPowered) { return; } mLastChangedIntReason = intReason; mLastChangedStrReason = strReason; mSettingBatterySaverEnabled = enable; // 1. 儲存省電模式的狀態 putGlobalSetting(Settings.Global.LOW_POWER_MODE, enable ? 1 : 0); // 2. 開啟 battery saver sticky 模式 if (manual) { // 使用者手動操作省電模式 // mBatterySaverStickyBehaviourDisabled 預設為 false,表示支援 battery saver sticky 模式 setStickyActive(!mBatterySaverStickyBehaviourDisabled && enable); } // 3. 通過 BatterySaverController 開啟省電模式 mBatterySaverController.enableBatterySaver(enable, intReason); // 動態省電模式相關 if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) { triggerDynamicModeNotification(); } else if (!enable) { hideDynamicModeNotification(); } }
在開啟省電模式之前,首先把資料庫 Settings.Global.LOW_POWER_MODE 欄位的值儲存為 1。
low power 應該翻譯為低功耗,俗稱省電模式,而 low battery 才應該翻譯為低電量,不要混淆了。 原始碼中 BatteryManagerService#getBatteryLevelLow() 表示電量是否低於自動省電模式的電量百分比,這個函數的命名非常差勁,一度讓我誤以為是低電量(電量低於15%),其實它表示是否觸發了自動省電模式。
第二步,我們要注意了,這裡涉及了 battery saver sticky 功能。根據判斷條件可知,只有在使用者手動操作省電模式的情況下,才會觸發 battery saver sticky 功能,來看下 setStickyActive()
private void setStickyActive(boolean active) { // 表示 battery saver sticky 模式已經開啟 mSettingBatterySaverEnabledSticky = active; // Settings.Global.LOW_POWER_MODE_STICKY 代表 battery saver sticky功能的狀態 putGlobalSetting(Settings.Global.LOW_POWER_MODE_STICKY, mSettingBatterySaverEnabledSticky ? 1 : 0); }
很簡單,就是儲存狀態,表示 battery saver sticky 功能已經開啟。
第三步,把開啟省電模式的實際操作,交給了省電模式控制器 BatterySaverController。
現在來看下 BatterySaverController#enableBatterySaver() 如何開啟省電模式
public void enableBatterySaver(boolean enable, int reason) { synchronized (mLock) { if (getFullEnabledLocked() == enable) { return; } // 1. 儲存省電模式的狀態 setFullEnabledLocked(enable); // 2. 更新省電模式策略 if (updatePolicyLevelLocked()) { // 3. 處理省電模式狀態的改變 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); } } } private boolean getFullEnabledLocked() { return mFullEnabledRaw; } private void setFullEnabledLocked(boolean value) { if (mFullEnabledRaw == value) { return; } // 重新整理省電模式的快取,使用者端可以通過 PowerManager 獲取省電模式狀態 PowerManager.invalidatePowerSaveModeCaches(); mFullEnabledRaw = value; }
首先使用 mFullEnabledRaw 儲存省電模式狀態。
然後,更新省電模式的策略。省電模式會影響很多模組的功能,例如,最直觀的就是影響螢幕亮度。因此開啟省電模式,必須得有一個策略,這些策略影響某些模組的功能。
最後,處理省電模式狀態的改變。其中包括切換省電模式,通知省電模式的監聽者。
mFullEnabledRaw 表示 full battery saver,其實就是使用者用到的省電模式。其實還有一種省電模式 adaptive battery saver,這種省電模式,是通過命令列設定的,應該是與測試相關,執行
adb shell power set-adaptive-power-saver-enabled true
來開啟,具體可以參考 PowerManagerShellCommand 類。
現在來看下 BatterySaverController#updatePolicyLevelLocked() 如何更新省電模式策略
private boolean updatePolicyLevelLocked() { if (getFullEnabledLocked()) { // 設定省電模式 policy level return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_FULL); } else if (getAdaptiveEnabledLocked()) { return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_ADAPTIVE); } else { return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_OFF); } }
原來是給 BatterySaverPolicy 設定了 policy level,值為 BatterySaverPolicy.POLICY_LEVEL_FULL。
boolean setPolicyLevel(@PolicyLevel int level) { synchronized (mLock) { if (mPolicyLevel == level) { return false; } if (mPolicyLevel == POLICY_LEVEL_FULL) { mFullPolicy = mDefaultFullPolicy; } switch (level) { case POLICY_LEVEL_FULL: case POLICY_LEVEL_ADAPTIVE: case POLICY_LEVEL_OFF: // 1. 儲存 level mPolicyLevel = level; break; default: Slog.wtf(TAG, "setPolicyLevel invalid level given: " + level); return false; } // 2. 根據 level,更新有效的 policy updatePolicyDependenciesLocked(); return true; } }
BatterSaverPolicy 儲存了 policy level,並且呼叫 updatePolicyDependenciesLocked() 來更新有效的 battery saver policy。
private void updatePolicyDependenciesLocked() { // 1. 根據 policy level, 獲取對應的 policy final Policy rawPolicy = getCurrentRawPolicyLocked(); // 重新整理省電模式快取 invalidatePowerSaveModeCaches(); // 車載 final int locationMode; if (mAutomotiveProjectionActive.get() && rawPolicy.locationMode != PowerManager.LOCATION_MODE_NO_CHANGE && rawPolicy.locationMode != PowerManager.LOCATION_MODE_FOREGROUND_ONLY) { // If car projection is enabled, ensure that navigation works. locationMode = PowerManager.LOCATION_MODE_FOREGROUND_ONLY; } else { locationMode = rawPolicy.locationMode; } // 2. 根據獲取的策略,來更新有效的策略 // mEffectivePolicyRaw 表示實際生效的 policy // mEffectivePolicyRaw 的資料,基本上都是從 rawPolicy 中複製過來的 // 只有幾項是需要調整的,例如 車載 或者 無障礙,這兩個特殊的情況,在使用時注意下即可 mEffectivePolicyRaw = new Policy( rawPolicy.adjustBrightnessFactor, rawPolicy.advertiseIsEnabled, rawPolicy.cpuFrequenciesForInteractive, rawPolicy.cpuFrequenciesForNoninteractive, rawPolicy.deferFullBackup, rawPolicy.deferKeyValueBackup, rawPolicy.disableAnimation, rawPolicy.disableAod, rawPolicy.disableLaunchBoost, rawPolicy.disableOptionalSensors, // Don't disable vibration when accessibility is on. rawPolicy.disableVibration && !mAccessibilityEnabled.get(), rawPolicy.enableAdjustBrightness, rawPolicy.enableDataSaver, rawPolicy.enableFirewall, // Don't force night mode when car projection is enabled. rawPolicy.enableNightMode && !mAutomotiveProjectionActive.get(), rawPolicy.enableQuickDoze, rawPolicy.forceAllAppsStandby, rawPolicy.forceBackgroundCheck, locationMode, rawPolicy.soundTriggerMode ); // ... } // 預設省電模式策略 private Policy mFullPolicy = DEFAULT_FULL_POLICY; private Policy getCurrentRawPolicyLocked() { switch (mPolicyLevel) { case POLICY_LEVEL_FULL: return mFullPolicy; case POLICY_LEVEL_ADAPTIVE: return mAdaptivePolicy; case POLICY_LEVEL_OFF: default: return OFF_POLICY; } }
getCurrentRawPolicyLocked() 會獲取預設的省電模式策略 DEFAULT_FULL_POLICY,然後根據一些情況調整省電模式策略,最後形成有效的省電模式策略 mEffectivePolicyRaw。
現在讓我們看看這個預設的省電策略 DEFAULT_FULL_POLICY 到底是何方神聖
private static final Policy DEFAULT_FULL_POLICY = new Policy( 0.5f, /* adjustBrightnessFactor */ true, /* advertiseIsEnabled */ new CpuFrequencies(), /* cpuFrequenciesForInteractive */ new CpuFrequencies(), /* cpuFrequenciesForNoninteractive */ true, /* deferFullBackup */ true, /* deferKeyValueBackup */ false, /* disableAnimation */ true, /* disableAod */ true, /* disableLaunchBoost */ true, /* disableOptionalSensors */ true, /* disableVibration */ false, /* enableAdjustBrightness */ false, /* enableDataSaver */ true, /* enableFirewall */ true, /* enableNightMode */ true, /* enableQuickDoze */ true, /* forceAllAppsStandby */ true, /* forceBackgroundCheck */ PowerManager.LOCATION_MODE_FOREGROUND_ONLY, /* locationMode */ PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY /* soundTriggerMode */ );
Policy 就是一個資料封裝類,看下它建構函式的引數,我們就能大致猜測出省電模式影響哪些模組的功能。
這裡注意下第三個和第四個引數,它表示省電模式下,需要限制頻率的 CPU 的編號以及限制的頻率值,這裡預設是空,後面會用到。
現在讓我們回到開啟省電模式的程式碼
public void enableBatterySaver(boolean enable, int reason) { synchronized (mLock) { if (getFullEnabledLocked() == enable) { return; } // 1. 儲存省電模式的狀態 setFullEnabledLocked(enable); // 2. 更新省電模式策略 if (updatePolicyLevelLocked()) { // 3. 處理省電模式狀態的改變 mHandler.postStateChanged(/*sendBroadcast=*/ true, reason); } } }
前兩步已經分析完畢,現在來看看第三步,它最終會呼叫 BatterySaverController#handleBatterySaverStateChanged() 來處理省電模式狀態改變
void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) { final LowPowerModeListener[] listeners; final boolean enabled; // 獲取裝置是否處於互動狀態 // 一般來說,如果螢幕熄滅,裝置處於非互動狀態,螢幕電量,裝置處於互動狀態 final boolean isInteractive = getPowerManager().isInteractive(); final ArrayMap<String, String> fileValues; synchronized (mLock) { // 獲取省電模式的狀態 enabled = getFullEnabledLocked() || getAdaptiveEnabledLocked(); // 儲存前一個 full battery saver狀態 mFullPreviouslyEnabled = getFullEnabledLocked(); // 儲存前一個adaptive battery saver狀態 mAdaptivePreviouslyEnabled = getAdaptiveEnabledLocked(); listeners = mListeners.toArray(new LowPowerModeListener[0]); mIsInteractive = isInteractive; if (enabled) { // 1. 開啟省電模式情況下,獲取頻率受限的CPU的編號以及受限的值 fileValues = mBatterySaverPolicy.getFileValues(isInteractive); } else { fileValues = null; } } // 2. 通過 PowerManagerService 向底層設定省電模式 final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class); if (pmi != null) { pmi.setPowerMode(Mode.LOW_POWER, isEnabled()); } // 用 BatterySavingStats 記錄資料 updateBatterySavingStats(); // 3. 根據策略,限制或恢復CPU頻率 if (ArrayUtils.isEmpty(fileValues)) { // CPU 策略為空,表示需要恢復 CPU 之前的頻率 mFileUpdater.restoreDefault(); } else { // CPU 頻率策略不為空,表示需要限制 CPU 頻率 mFileUpdater.writeFiles(fileValues); } if (sendBroadcast) { // 4. 傳送廣播 Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendBroadcastAsUser(intent, UserHandle.ALL); // 可以在 frameworks-res 的組態檔中設定一個應用的包名 // 這個應用可以在manifest.xml中註冊廣播接收器,接收省電模式狀態改變 if (getPowerSaveModeChangedListenerPackage().isPresent()) { intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED) .setPackage(getPowerSaveModeChangedListenerPackage().get()) .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendBroadcastAsUser(intent, UserHandle.ALL); } // 傳送一個內部版本的廣播,但是接收者需要許可權 intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.DEVICE_POWER); // 5. 通知監聽者 for (LowPowerModeListener listener : listeners) { final PowerSaveState result = mBatterySaverPolicy.getBatterySaverPolicy(listener.getServiceType()); listener.onLowPowerModeChanged(result); } } }
第一步和第三步,是在省電模式下限制 CPU 頻率的。根據前面分析可知,目前預設策略是沒有設定CPU頻率的,因此這兩步不分析了。我將在後面的文章中,分析如何控制省電模式策略,到時候再來分析這裡的程式碼邏輯。
第二步,通過 PowerManagerService 向底層設定省電模式,底層稱之為低功耗模式(low power mode)。
第四步,傳送省電模式狀態改變的廣播。
第五步,通知監聽者。誰會是監聽者呢?當然是那些受省電模式影響的模組。
讓我們看下返回給監聽者的資料到底是什麼?看下mBatterySaverPolicy.getBatterySaverPolicy(listener.getServiceType())
public PowerSaveState getBatterySaverPolicy(@ServiceType int type) { synchronized (mLock) { final Policy currPolicy = getCurrentPolicyLocked(); final PowerSaveState.Builder builder = new PowerSaveState.Builder() .setGlobalBatterySaverEnabled(currPolicy.advertiseIsEnabled); switch (type) { case ServiceType.LOCATION: boolean isEnabled = currPolicy.advertiseIsEnabled || currPolicy.locationMode != PowerManager.LOCATION_MODE_NO_CHANGE; return builder.setBatterySaverEnabled(isEnabled) .setLocationMode(currPolicy.locationMode) .build(); case ServiceType.ANIMATION: return builder.setBatterySaverEnabled(currPolicy.disableAnimation) .build(); // ... case ServiceType.VIBRATION: return builder.setBatterySaverEnabled(currPolicy.disableVibration) .build(); case ServiceType.FORCE_ALL_APPS_STANDBY: return builder.setBatterySaverEnabled(currPolicy.forceAllAppsStandby) .build(); case ServiceType.FORCE_BACKGROUND_CHECK: return builder.setBatterySaverEnabled(currPolicy.forceBackgroundCheck) .build(); // ... default: return builder.setBatterySaverEnabled(currPolicy.advertiseIsEnabled) .build(); } } }
原來,根據監聽者的型別,返回一個 PowerSaveState 物件,這個物件中只包含了監聽者關心的資料。
從這裡,我們應該有所領悟,如果我們自己開發了一個功能模組
現在很多專案都關注電量消耗問題,省電模式到底能讓手機待機多長時間,也是一個考核的指標。
根據前面的分析,只有在使用者手動操作省電模式的時候,才會相應的開啟或者關閉 battery saver sticky 模式。
我先總結下什麼是 battery saver sticky 模式? 當手機已經處於省電模式,插入電源,系統會預設關閉省電模式,如果此時拔掉電源或者手機重啟,當 battery saver sticky 功能已經開啟的情況下,系統會重新開啟省電模式。
現在讓我們從程式碼角度分析,繼續使用上面的例子分析,假如現在已經開啟了省電模式,此時插入了電源,來看下狀態機的切換動作 BatterySaverStateMachine#updateStateLocked()
private void updateStateLocked(boolean manual, boolean enable) { if (!manual && !(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) { return; // Not fully initialized yet. } switch (mState) { case STATE_OFF: { if (!mIsPowered) { // 充電狀態下,不允許開啟省電模式 if (manual) { // 手動模式 if (!enable) { Slog.e(TAG, "Tried to disable BS when it's already OFF"); return; } enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, BatterySaverController.REASON_MANUAL_ON); hideStickyDisabledNotification(); // 1. 使用者開啟省電模式,狀態切換為 STATE_MANUAL_ON mState = STATE_MANUAL_ON; } else if (isAutomaticModeActiveLocked() && isInAutomaticLowZoneLocked()) { // 自動模式 ... } else if (isDynamicModeActiveLocked() && isInDynamicLowZoneLocked()) { // 動態模式 ... } } break; } case STATE_MANUAL_ON: { if (manual) { // ... } else if (mIsPowered) { // 2. 插入電源 // 關閉省電模式 enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, BatterySaverController.REASON_PLUGGED_IN); // 手動開啟省電模式時,mSettingBatterySaverEnabledSticky 設定為 true // mBatterySaverStickyBehaviourDisabled 預設為 false,表示支援這個 feature if (mSettingBatterySaverEnabledSticky && !mBatterySaverStickyBehaviourDisabled) { // 插入電源,狀態切換為 STATE_PENDING_STICKY_ON mState = STATE_PENDING_STICKY_ON; } else { mState = STATE_OFF; } } break; } // ... case STATE_PENDING_STICKY_ON: { // 3. battery saver sticky 模式操作 if (manual) { return; } // mSettingBatterySaverStickyAutoDisableEnabled 對應 Battery Saver介面下的 Turn off when charging 開關 // mSettingBatterySaverStickyAutoDisableThreshold 預設值為 90 final boolean shouldTurnOffSticky = mSettingBatterySaverStickyAutoDisableEnabled && mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold; // 手動開啟省電模式,再插入電源,此時 isStickyDisabled 值為 false final boolean isStickyDisabled = mBatterySaverStickyBehaviourDisabled || !mSettingBatterySaverEnabledSticky; if (isStickyDisabled || shouldTurnOffSticky) { // 3.2 如果Turn off when charging 開關被開啟,並且電量大於90%,那麼不會重新開啟省電模式 mState = STATE_OFF; setStickyActive(false); triggerStickyDisabledNotification(); } else if (!mIsPowered) { // Re-enable BS. // 3.1 斷開電源,重新開啟省電模式 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, BatterySaverController.REASON_STICKY_RESTORE); mState = STATE_MANUAL_ON; } break; } // ... } }
首先看下第一步,它開啟了省電模式,並且狀態切換為 STATE_MANUAL_ON。
如果此時,插入電源,那麼會進入第二步, 關閉省電模式, 並把狀態切換為 STATE_PENDING_STICKY_ON。
如果關閉了設定中 Battery Saver 介面的 Turn off when Charging 開關,此時拔掉電源,那麼進入 3.1 步,又會再次開啟省電模式,這就是 sticky 的含義。
如果開啟了設定中 Battery Saver 介面的 Turn off when Charging 開關,那麼進入 3.2 步,不會再次開啟省電模式。
設定中 Battery Saver 介面的 Turn off when Charging 開關就是 battery saver sticky auto disable 功能。
通讀了整個省電模式的程式碼,給我的感覺是很多功能都非常雞肋,限於偏於原因,我只分析了核心的程式碼,那就是如何切換省電模式。剩下的其他功能,留給讀者自行分析。
到此這篇關於Android PowerManagerService 開啟省電模式的文章就介紹到這了,更多相關Android PowerManagerService 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45