中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

互動(dòng)平臺(tái)羅馬復(fù)興廣州seo站內(nèi)優(yōu)化

互動(dòng)平臺(tái)羅馬復(fù)興,廣州seo站內(nèi)優(yōu)化,昆明hph網(wǎng)站建設(shè),新疆新聞網(wǎng)官網(wǎng)前言 SettingsProvider顧名思義是一個(gè)提供設(shè)置數(shù)據(jù)共享的Provider,SettingsProvider和Android系統(tǒng)其它Provider有很多不一樣的地方,如: SettingsProvider只接受int、float、string等基本類型的數(shù)據(jù);SettingsProvider由Android系…

前言

SettingsProvider顧名思義是一個(gè)提供設(shè)置數(shù)據(jù)共享的Provider,SettingsProvider和Android系統(tǒng)其它Provider有很多不一樣的地方,如:

  • SettingsProvider只接受int、float、string等基本類型的數(shù)據(jù);
  • SettingsProvider由Android系統(tǒng)framework進(jìn)行了封裝,使用更加快捷方便
  • SettingsProvider的數(shù)據(jù)由鍵值對(duì)組成

SettingsProvider有點(diǎn)類似Android的properties系統(tǒng)(Android屬性系統(tǒng)):SystemProperties。SystemProperties除具有SettingsProvider以上的三個(gè)特性,SettingsProvider和SystemProperties的不同點(diǎn)在于:

  • 數(shù)據(jù)保存方式不同:SystemProperties的數(shù)據(jù)保存屬性文件中(/system/build.prop等),開(kāi)機(jī)后會(huì)被加載到system properties store;SettingsProvider的數(shù)據(jù)保存在文件/data/system/users/0/settings_***.xml和數(shù)據(jù)庫(kù)settings.db中;
  • 作用范圍不同:SystemProperties可以實(shí)現(xiàn)跨進(jìn)程、跨層次調(diào)用,即底層的c/c++可以調(diào)用,java層也可以調(diào)用;SettingProvider只能能在java層(APP)使用;
  • 公開(kāi)程度不同:SettingProvider有部分功能上層第三方APP可以使用,SystemProperties上層第三方APP不可以使用。

用一句話概括SettingsProvider的作用,SettingsProvider包含全局性、系統(tǒng)級(jí)別的用戶編好設(shè)置。在手機(jī)中有一個(gè)Settings應(yīng)用,用戶可以在Settings里面做很多設(shè)備的設(shè)置,這些用戶偏好的設(shè)置很多就保存在SettingsProvider中。例如,飛行模式。

在Android 6.0版本時(shí),SettingsProvider被重構(gòu),Android從性能、安全等方面考慮,把SettingsProvider中原本保存在settings.db中的數(shù)據(jù),目前全部保存在XML文件中。

SettingsProvider概覽

主要源碼

SettingsProvider的代碼數(shù)量不多,主要包含如下的java文件:

frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java

frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java

frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java

frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java

frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java

frameworks/base/core/java/android/provider/Settings.java

數(shù)據(jù)分類

SettingsProvider對(duì)數(shù)據(jù)進(jìn)行了分類,分別是Global、System、Secure三種類型,它們的區(qū)別如下:

  • Global:所有的偏好設(shè)置對(duì)系統(tǒng)的所有用戶公開(kāi),第三方APP有讀沒(méi)有寫(xiě)的權(quán)限;
  • System:包含各種各樣的用戶偏好系統(tǒng)設(shè)置;
  • Secure:安全性的用戶偏好系統(tǒng)設(shè)置,第三方APP有讀沒(méi)有寫(xiě)的權(quán)限。
    AndroidManifest.xml配置
    SettingsProvider的AndroidManifest.xml文件對(duì)應(yīng)用和ContentProvider的配置如下:
<manifest ......android:sharedUserId="android.uid.system"><application android:allowClearUserData="false"android:label="@string/app_label"android:process="system"......android:directBootAware="true"><provider android:name="SettingsProvider"android:authorities="settings"android:multiprocess="false"android:exported="true"android:singleUser="true"android:initOrder="100" /></application>
</manifest>

這些代碼定義在文件frameworks/base/packages/SettingsProvider/AndroidManifest.xml中。

上面的Manifest配置由sharedUserId可知,SettingsProvider運(yùn)行在系統(tǒng)進(jìn)程中,定義的ContentProvider實(shí)現(xiàn)類是SettingsProvider,Uri憑證是settings。

SettingsProvider的啟動(dòng)過(guò)程

啟動(dòng)SettingsProvider即運(yùn)行SettingsProvider,和打開(kāi)一個(gè)Activity類似,會(huì)回調(diào)ContentProvider的生命周期方法,首先的,會(huì)調(diào)用OnCreate()方法,如下:
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

public boolean onCreate() {synchronized (mLock) {......mHandlerThread = new HandlerThread(LOG_TAG,Process.THREAD_PRIORITY_BACKGROUND);mHandlerThread.start();mSettingsRegistry = new SettingsRegistry();}registerBroadcastReceivers();startWatchingUserRestrictionChanges();return true;
}

上面的代碼首先是實(shí)例化一個(gè)HandlerThread的實(shí)例mHandlerThread,優(yōu)先級(jí)為Process.THREAD_PRIORITY_BACKGROUND,下文會(huì)用到。然后實(shí)例化SettingsRegistry的實(shí)例mSettingsRegistry,這一步很重要。接著會(huì)注冊(cè)廣播接收器,所關(guān)心的廣播包括設(shè)備用戶變化以及APP卸載的廣播,設(shè)備用戶的變化對(duì)大多數(shù)地方使用SettingProvider的影響不是很大,本文就不再闡述和用戶變化相關(guān)的內(nèi)容了。但是APP卸載這里需要關(guān)注一下,當(dāng)一個(gè)APP有數(shù)據(jù)保存在SettingsProvider時(shí),APP被卸載后,被卸載的APP設(shè)置的所有數(shù)據(jù)都會(huì)被清除?;氐絊ettingsRegistry的實(shí)例化過(guò)程,構(gòu)造方法如下:
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

    final class SettingsRegistry {private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";private static final String SETTINGS_FILE_GLOBAL = "settings_global.xml";private static final String SETTINGS_FILE_SYSTEM = "settings_system.xml";private static final String SETTINGS_FILE_SECURE = "settings_secure.xml";private static final String SETTINGS_FILE_SSAID = "settings_ssaid.xml";private static final String SETTINGS_FILE_CONFIG = "settings_config.xml";public SettingsRegistry() {.....migrateAllLegacySettingsIfNeeded();}

migrateAllLegacySettingsIfNeeded()方法,從命名上是遷移settings數(shù)據(jù),遷移什么數(shù)據(jù)呢?從哪里遷移到哪里呢?繼續(xù)往下看:
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

private void migrateAllLegacySettingsIfNeeded() {synchronized (mLock) {final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);File globalFile = getSettingsFile(key);if (globalFile.exists()) {return;}......DatabaseHelper dbHelper = new DatabaseHelper(getContext(), userId);SQLiteDatabase database = dbHelper.getWritableDatabase();migrateLegacySettingsForUserLocked(dbHelper, database, userId);// Upgrade to the latest version.UpgradeController upgrader = new UpgradeController(userId);upgrader.upgradeIfNeededLocked();......}
}

上面的代碼首先是調(diào)用了makeKey()方法,所謂makeKey()就是和上文中的數(shù)據(jù)分類小章節(jié)中提到的System、Global和Secure三種key。然后調(diào)用getSettingsFile()方法獲取到一個(gè)File對(duì)象的實(shí)例,如下:
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

private File getSettingsFile(int key) {if (isGlobalSettingsKey(key)) {final int userId = getUserIdFromKey(key);return new File(Environment.getUserSystemDirectory(userId),SETTINGS_FILE_GLOBAL);} else if (isSystemSettingsKey(key)) {final int userId = getUserIdFromKey(key);return new File(Environment.getUserSystemDirectory(userId),SETTINGS_FILE_SYSTEM);} else if (isSecureSettingsKey(key)) {final int userId = getUserIdFromKey(key);return new File(Environment.getUserSystemDirectory(userId),SETTINGS_FILE_SECURE);} else {throw new IllegalArgumentException("Invalid settings key:" + key);}
}

上面的代碼中對(duì)Global、System、Secure分別生成一個(gè)File對(duì)象實(shí)例,它們的File對(duì)象分別對(duì)應(yīng)的文件是:

/data/system/users/0/settings_global.xml
/data/system/users/0/settings_system.xml
/data/system/users/0/settings_secure.xml
那么也就是說(shuō),Global類型的數(shù)據(jù)保存在文件settings_global.xml中,System類型的數(shù)據(jù)保存在文件settings_system.xml中,Secure類型的數(shù)據(jù)保存在文件settings_secure.xml中。

回到上文中的migrateAllLegacySettingsIfNeeded()方法,實(shí)例化一個(gè)DatabaseHelper,DatabaseHelper是SQLiteOpenHelper的子類,然后調(diào)用getWritableDatabase()獲取到指向數(shù)據(jù)庫(kù)文件的SQLiteDatabase實(shí)例database。從Android SQLite的架構(gòu)可知,這個(gè)過(guò)程會(huì)調(diào)用SQLiteOpenHelper的onCreate()方法,如果讀者對(duì)這個(gè)過(guò)程迷惑的,可以閱讀Android的API指南Android SQLite API 指南。

frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java中。

public void onCreate(SQLiteDatabase db) {db.execSQL("CREATE TABLE system (" +"_id INTEGER PRIMARY KEY AUTOINCREMENT," +"name TEXT UNIQUE ON CONFLICT REPLACE," +"value TEXT" +");");db.execSQL("CREATE INDEX systemIndex1 ON system (name);");createSecureTable(db);// Only create the global table for the singleton 'owner/system' userif (mUserHandle == UserHandle.USER_SYSTEM) {createGlobalTable(db);}......// Load initial volume levels into DBloadVolumeLevels(db);// Load inital settings valuesloadSettings(db);
}

這個(gè)方法調(diào)用db.execSQL("CREATE TABLE system …、createSecureTable()、createGlobalTable()分別創(chuàng)建System、Secure、Global三個(gè)數(shù)據(jù)庫(kù)表,這個(gè)和上文中數(shù)據(jù)分類章節(jié)中的內(nèi)容一致。接著調(diào)用loadVolumeLevels(db)方法,把默認(rèn)的鈴聲音量、音樂(lè)音量、通知音量以及震動(dòng)設(shè)置等等寫(xiě)入到數(shù)據(jù)庫(kù)的System表格中。處理完后調(diào)用方法loadSettings(db),如下:
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java中。

private void loadSettings(SQLiteDatabase db) {loadSystemSettings(db);loadSecureSettings(db);// The global table only exists for the 'owner/system' userif (mUserHandle == UserHandle.USER_SYSTEM) {loadGlobalSettings(db);
}

loadSettings()這個(gè)方法和loadVolumeLevels()方法類似,都是加載很多默認(rèn)值寫(xiě)入到數(shù)據(jù)庫(kù)中,這些默認(rèn)值很大一部分被定義在文件frameworks/base/packages/SettingsProvider/res/values/defaults.xml中,也有一些來(lái)自其它地方。總之,loadVolumeLevels()和loadSettings()的作用就是在手機(jī)第一次啟動(dòng)時(shí),把手機(jī)編好設(shè)置的默認(rèn)值寫(xiě)入到數(shù)據(jù)庫(kù)settings.db中。

DatabaseHelper的onCreate()方法執(zhí)行完畢后,這里又回到migrateAllLegacySettingsIfNeeded()方法中,DatabaseHelper創(chuàng)建完畢后,繼續(xù)調(diào)用migrateLegacySettingsForUserLocked()方法,如下:
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

private void migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper,SQLiteDatabase database, int userId) {// Move over the system settings.final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);ensureSettingsStateLocked(systemKey);SettingsState systemSettings = mSettingsStates.get(systemKey);migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM);systemSettings.persistSyncLocked();......// Drop the database as now all is moved and persisted.if (DROP_DATABASE_ON_MIGRATION) {dbHelper.dropDatabase();} else {dbHelper.backupDatabase();}
}

上面的代碼中的每個(gè)方法都是那么重要,首先是ensureSettingsStateLocked(systemKey),如下:
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

private void ensureSettingsStateLocked(int key) {if (mSettingsStates.get(key) == null) {......SettingsState settingsState = new SettingsState(mLock, getSettingsFile(key), key,maxBytesPerPackage, mHandlerThread.getLooper());mSettingsStates.put(key, settingsState);}
}

上面代碼實(shí)例化一個(gè)SettingsState對(duì)象,這個(gè)對(duì)象指向文件/data/system/users/0/settings_system.xml,然后把settingsState放置在對(duì)象mSettingsStates中?;氐絤igrateLegacySettingsForUserLocked()方法,ensureSettingsStateLocked()執(zhí)行完畢后,調(diào)用migrateLegacySettingsLocked()方法,如下:
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

private void migrateLegacySettingsLocked(SettingsState settingsState,SQLiteDatabase database, String table) {SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();queryBuilder.setTables(table);Cursor cursor = queryBuilder.query(database, ALL_COLUMNS,null, null, null, null, null);try {......while (!cursor.isAfterLast()) {String name = cursor.getString(nameColumnIdx);String value = cursor.getString(valueColumnIdx);settingsState.insertSettingLocked(name, value,SettingsState.SYSTEM_PACKAGE_NAME);cursor.moveToNext();}} finally {cursor.close();}
}

上面這個(gè)方法,查詢數(shù)據(jù)庫(kù)中System所有的設(shè)置,然后在while循環(huán)中把每個(gè)值的信息作為insertSettingLocked()的參數(shù),insertSettingLocked()方法如下:

public boolean insertSettingLocked(String name, String value, String packageName) {Setting oldState = mSettings.get(name);String oldValue = (oldState != null) ? oldState.value : null;if (oldState != null) {......} else {Setting state = new Setting(name, value, packageName);mSettings.put(name, state);}......
}

這個(gè)方法定義在文件frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java中。

上面的方法把每個(gè)設(shè)置項(xiàng)封裝到對(duì)象Setting中,接著有把state放置到ArrayMap<String, Setting>的實(shí)例mSettings中。

那么,從方法ensureSettingsStateLocked()到insertSettingLocked()方法,這個(gè)過(guò)程表明,有一個(gè)對(duì)象SettingsState,指向文件/data/system/users/0/settings_system.xml,持有變量mSettings,而mSettings持有封裝了設(shè)置項(xiàng)的name, value, packageName的對(duì)象Setting,換句話說(shuō),settings_system.xml文件中的所有的設(shè)置項(xiàng)間接被SettingsState持有。

又回到migrateLegacySettingsForUserLocked()方法,migrateLegacySettingsLocked()方法執(zhí)行完畢后,調(diào)用systemSettings.persistSyncLocked(),systemSettings是SettingsState的實(shí)例,代表的是settings_system.xml所有的設(shè)置項(xiàng),persistSyncLocked()方法就是把systemSettings持有的所有的設(shè)置項(xiàng)從內(nèi)存中固化到文件settings_system.xml中,這個(gè)過(guò)程的代碼就不貼出來(lái)了。migrateLegacySettingsForUserLocked()方法中省略的代碼,就是和上文的這幾個(gè)方法一樣,把settings_global.xml、settings_secure.xml兩個(gè)文件中的所有設(shè)置項(xiàng)封裝到Setting中,被SettingsState持有。

也就是說(shuō),settings_global.xml、settings_secure.xml、settings_system.xml三個(gè)文件的所有設(shè)置項(xiàng)間接被SettingsState持有,而SettingsState又被封裝到類SettingsProvider.java的變量mSettingsStates中,mSettingsStates是SparseArray的實(shí)例。它們的層次關(guān)系如下圖:

20170301143940227.png

再次回到方法migrateLegacySettingsForUserLocked(),在把數(shù)據(jù)中的數(shù)據(jù)轉(zhuǎn)移到xml文件后,執(zhí)行下面這段代碼:

// Drop the database as now all is moved and persisted.
if (DROP_DATABASE_ON_MIGRATION) {dbHelper.dropDatabase();
} else {dbHelper.backupDatabase();
}

如果是工程版本的系統(tǒng),把數(shù)據(jù)庫(kù)settings.db重命名為settings.db-backup,如果是非工程版本的系統(tǒng),把數(shù)據(jù)庫(kù)文件刪除,也會(huì)刪除日志settings.db-journal。

SettnigsProvider啟動(dòng)時(shí)會(huì)創(chuàng)建settings.db數(shù)據(jù)庫(kù),然后把所有的默認(rèn)設(shè)置項(xiàng)寫(xiě)入到數(shù)據(jù)庫(kù),接著會(huì)把數(shù)據(jù)庫(kù)中所有的設(shè)置項(xiàng)從數(shù)據(jù)庫(kù)轉(zhuǎn)移到xml文件中,隨后便會(huì)對(duì)數(shù)據(jù)庫(kù)執(zhí)行刪除操作。為什么會(huì)有這么一個(gè)過(guò)程,這些過(guò)程是否可以移除創(chuàng)建數(shù)據(jù)庫(kù)這一步?其實(shí)這個(gè)過(guò)程是為了兼容之前的版本而設(shè)計(jì),在SettingsProvider被Android重構(gòu)后,SettingsProvider中數(shù)據(jù)庫(kù)相關(guān)的代碼Android已經(jīng)停止更新。

封裝SettingsProvider接口
由章節(jié)前言中的描述,SettingsProvider是向整個(gè)Android系統(tǒng)提供用戶編好設(shè)置的提供程序,所保存的數(shù)據(jù)類型和方式上也有一定約束和規(guī)定,且要求使用SettingsProvider是方便的,代碼量少的。因此,需要對(duì)ContentProvider的一些接口進(jìn)行封裝,以保證在整個(gè)Android的java層任何一個(gè)地方都能方便、快捷的使用SettingsProvider進(jìn)行數(shù)據(jù)查詢,數(shù)據(jù)更新和數(shù)據(jù)插入。所以,理所當(dāng)然地,framework有一個(gè)類Settings.java對(duì)使用SettingsProvider進(jìn)行了封裝。如下:

public final class Settings {public static final String AUTHORITY = "settings";public static final class Global extends NameValueTable {public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/global");......}public static final class Secure extends NameValueTable {public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/secure");......}public static final class System extends NameValueTable {public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/system");......}private static class NameValueCache {private final Uri mUri;private final HashMap<String, String> mValues = new HashMap<String, String>();public String getStringForUser(ContentResolver cr, String name, final int userHandle) {......}public boolean putStringForUser(ContentResolver cr, String name, String value,final int userHandle) {......}private IContentProvider lazyGetProvider(ContentResolver cr) {IContentProvider cp = null;synchronized (NameValueCache.this) {cp = mContentProvider;if (cp == null) {cp = mContentProvider = cr.acquireProvider(mUri.getAuthority());}}return cp;}
}

這個(gè)類定義在文件frameworks/base/core/java/android/provider/Settings.java中。

上面的代碼中,分別聲明了Global、Secure、System三個(gè)靜態(tài)內(nèi)部類,分別對(duì)應(yīng)SettingsProvider中的Global、Secure、System三種數(shù)據(jù)類型。Global、Secure、System三個(gè)靜態(tài)內(nèi)部類會(huì)分別持有自己NameValueCache的實(shí)例變量,每個(gè)NameValueCache持有指向SettingsProvider中的SettingsProvider.java的AIDL遠(yuǎn)程調(diào)用IContentProvider,讀者可以閱讀《Android System Server大綱之ContentService和ContentProvider原理剖析》了解ConatentProvider的這個(gè)過(guò)程。因此,查詢數(shù)據(jù)需要經(jīng)過(guò)NameValueCache的getStringForUser()方法,插入數(shù)據(jù)需要經(jīng)過(guò)putStringForUser()方法。同時(shí),NameValueCache還持有一個(gè)變量mValues,用于保存查詢過(guò)的設(shè)置項(xiàng),以便下下次再次發(fā)起查詢時(shí),能夠快速返回。

操作SettingsProvider
由于Settings.java對(duì)使用SettingsProvider進(jìn)行了封裝,所以,使用起來(lái)相當(dāng)簡(jiǎn)單簡(jiǎn)潔。由于Global、Secure、System三種數(shù)據(jù)類型的使用是幾乎相同,所以本文就只以Global為例對(duì)查詢插入數(shù)據(jù)的過(guò)程進(jìn)行分析。

查詢數(shù)據(jù)
從SettingsProvider的Global中查詢數(shù)據(jù),查詢是否是飛行模式使用方法如下:

String globalValue = Settings.Global.getString(getContentResolver(), Settings.Global.AIRPLANE_MODE_ON);
上面的代碼,用起來(lái)代碼量很少,只需要一行代碼即可查詢到所需要的值。深入Settings.java看getString()方法:

public static String getString(ContentResolver resolver, String name) {return getStringForUser(resolver, name, UserHandle.myUserId());
}/** @hide */
public static String getStringForUser(ContentResolver resolver, String name,int userHandle) {if (MOVED_TO_SECURE.contains(name)) {Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Global"+ " to android.provider.Settings.Secure, returning read-only value.");return Secure.getStringForUser(resolver, name, userHandle);}return sNameValueCache.getStringForUser(resolver, name, userHandle);
}

這些方法定義在文件frameworks/base/core/java/android/provider/Settings.java中。

getString()直接調(diào)用了getStringForUser(),getStringForUser()首先有做一個(gè)判斷MOVED_TO_SECURE.contains(name),做這個(gè)判斷是因?yàn)樵贏ndroid系統(tǒng)的更新中,保存在Global、Secure、System三種類型的數(shù)據(jù)的存放位置有變化,所以需要加這個(gè)判斷兼容老版本的使用方法。在章節(jié)“封裝SettingsProvider接口”中提到,查詢必須經(jīng)過(guò)NameValueCache.getStringForUser()方法,如下:

public String getStringForUser(ContentResolver cr, String name, final int userHandle) {final boolean isSelf = (userHandle == UserHandle.myUserId());if (isSelf) {......} else if (mValues.containsKey(name)) {return mValues.get(name);......IContentProvider cp = lazyGetProvider(cr);// Try the fast path first, not using query().  If this// fails (alternate Settings provider that doesn't support// this interface?) then we fall back to the query/table// interface.if (mCallGetCommand != null) {try {......Bundle b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);if (b != null) {String value = b.getString(Settings.NameValueTable.VALUE);......mValues.put(name, value);}return value;}} catch (RemoteException e) {// Not supported by the remote side?  Fall through// to query().}}Cursor c = null;try {c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER,new String[]{name}, null, null);String value = c.moveToNext() ? c.getString(0) : null;synchronized (NameValueCache.this) {mValues.put(name, value);}return value;......if (c != null) c.close();}
}
}

這個(gè)方法定義在文件frameworks/base/core/java/android/provider/Settings.java中。

首先從緩存mValues變量中去找,如果沒(méi)有查詢到,就調(diào)用SettingsProvider的call()接口,如果call()接口也沒(méi)有查詢到,再調(diào)用query()接口。這里用的是call()接口,下文就以call()接口往下分析。cp.call()會(huì)調(diào)用到SettingsProvider的call()方法,讀者可以閱讀《Android System Server大綱之ContentService和ContentProvider原理剖析》了解ConatentProvider的這個(gè)過(guò)程。注意參數(shù)mCallGetCommand。

public Bundle call(String method, String name, Bundle args) {final int requestingUserId = getRequestingUserId(args);switch (method) {case Settings.CALL_METHOD_GET_GLOBAL: {Setting setting = getGlobalSetting(name);return packageValueForCallResult(setting, isTrackingGeneration(args));}case Settings.CALL_METHOD_GET_SECURE: {Setting setting = getSecureSetting(name, requestingUserId);return packageValueForCallResult(setting, isTrackingGeneration(args));}......}return null;
}

這個(gè)方法定義在文件frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

上層傳過(guò)來(lái)的參數(shù)的command是mCallGetCommand,即Settings.CALL_METHOD_GET_GLOBAL,所以調(diào)用getGlobalSetting(),方法,在章節(jié)“SettingsProvider的啟動(dòng)過(guò)程”中可知,getGlobalSetting()返回的Setting setting是封裝了設(shè)置項(xiàng)name、value等信息的,查看getGlobalSetting()方法:

private Setting getGlobalSetting(String name) {// Get the value.synchronized (mLock) {return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_GLOBAL,UserHandle.USER_SYSTEM, name);}
}

這個(gè)方法定義在文件frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

通過(guò)mSettingsRegistry.getSettingLocked()繼續(xù)尋找:

public Setting getSettingLocked(int type, int userId, String name) {final int key = makeKey(type, userId);SettingsState settingsState = peekSettingsStateLocked(key);return settingsState.getSettingLocked(name);
}

這個(gè)方法定義在文件frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

通過(guò)peekSettingsStateLocked(key)尋找SettingsState:

private SettingsState peekSettingsStateLocked(int key) {SettingsState settingsState = mSettingsStates.get(key);if (settingsState != null) {return settingsState;}ensureSettingsForUserLocked(getUserIdFromKey(key));return mSettingsStates.get(key);
}

這個(gè)方法定義在文件frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

獲取到SettingsState settingsState,回到getSettingLocked(),調(diào)用settingsState.getSettingLocked(name):

public Setting getSettingLocked(String name) {if (TextUtils.isEmpty(name)) {return mNullSetting;}Setting setting = mSettings.get(name);if (setting != null) {return new Setting(setting);}return mNullSetting;
}

這個(gè)方法定義在文件frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java中。

到getSettingLocked()最終獲取到Setting setting,其實(shí)這個(gè)過(guò)程就是章節(jié)“SettingsProvider的啟動(dòng)過(guò)程”中的層次關(guān)系圖的反映,讀者可以查看這個(gè)圖更好理解這個(gè)過(guò)程。

插入數(shù)據(jù)
從SettingsProvider的Global中插入數(shù)據(jù),插入飛行模式的使用方法如下:

boolean isSuccess = Settings.System.putInt(getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
和查詢一樣,代碼非常簡(jiǎn)潔,這個(gè)過(guò)程和查詢幾乎類似,將快速游覽這個(gè)過(guò)程。往下跟蹤:

public static boolean putInt(ContentResolver cr, String name, int value) {return putString(cr, name, Integer.toString(value));
}
public static boolean putString(ContentResolver resolver,String name, String value) {return putStringForUser(resolver, name, value, UserHandle.myUserId());
}
public static boolean putStringForUser(ContentResolver resolver,String name, String value, int userHandle) {......return sNameValueCache.putStringForUser(resolver, name, value, userHandle);
}

這個(gè)方法定義在文件frameworks/base/core/java/android/provider/Settings.java中。

和查詢一樣,繼續(xù)看putStringForUser():

public boolean putStringForUser(ContentResolver cr, String name, String value,final int userHandle) {try {Bundle arg = new Bundle();arg.putString(Settings.NameValueTable.VALUE, value);arg.putInt(CALL_METHOD_USER_KEY, userHandle);IContentProvider cp = lazyGetProvider(cr);cp.call(cr.getPackageName(), mCallSetCommand, name, arg);} catch (RemoteException e) {Log.w(TAG, "Can't set key " + name + " in " + mUri, e);return false;}return true;
}

這個(gè)方法定義在文件frameworks/base/core/java/android/provider/Settings.java中。

直接調(diào)用SettingsProvider的call()接口:

public Bundle call(String method, String name, Bundle args) {final int requestingUserId = getRequestingUserId(args);switch (method) {......case Settings.CALL_METHOD_PUT_GLOBAL: {String value = getSettingValue(args);insertGlobalSetting(name, value, requestingUserId, false);break;}case Settings.CALL_METHOD_PUT_SECURE: {String value = getSettingValue(args);insertSecureSetting(name, value, requestingUserId, false);break;}......}return null;
}

這個(gè)方法定義在文件frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

首先調(diào)用getSettingValue(args)獲取對(duì)應(yīng)的設(shè)置項(xiàng),接著insertGlobalSetting()方法:

private boolean insertGlobalSetting(String name, String value, int requestingUserId,boolean forceNotify) {return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT,forceNotify);
}

這個(gè)方法定義在文件frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

直接調(diào)用了mutateGlobalSetting()方法:

private boolean mutateGlobalSetting(String name, String value, int requestingUserId,int operation, boolean forceNotify) {// Make sure the caller can change the settings - treated as secure.enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);// If this is a setting that is currently restricted for this user, do not allow// unrestricting changes.if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId, value)) {return false;}// Perform the mutation.synchronized (mLock) {switch (operation) {case MUTATION_OPERATION_INSERT: {return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM,name, value, getCallingPackage(), forceNotify);}......}}return false;
}

這個(gè)方法定義在文件frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

首先對(duì)使用的進(jìn)行權(quán)限檢查,然后調(diào)用mSettingsRegistry.insertSettingLocked()方法:

public boolean insertSettingLocked(int type, int userId, String name, String value,String packageName, boolean forceNotify) {final int key = makeKey(type, userId);SettingsState settingsState = peekSettingsStateLocked(key);final boolean success = settingsState.insertSettingLocked(name, value, packageName);if (forceNotify || success) {notifyForSettingsChange(key, name);}return success;
}

這個(gè)方法定義在文件frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中。

到這里,就不往下分析了,其實(shí)這個(gè)即是查詢的反過(guò)程,結(jié)合章節(jié)“SettingsProvider的啟動(dòng)過(guò)程”中的層次關(guān)系圖,能夠很好理解這個(gè)過(guò)程。

第三方APP使用SettingsProvider
第三方APP可以通過(guò)framework的Settings.java查詢SettingsProvider中的設(shè)置項(xiàng),使用方法查閱章節(jié)“查詢數(shù)據(jù)”。第三APP是否可以修改SettingsProvider的設(shè)置項(xiàng)?Android系統(tǒng)不允許第三方APP修改SettingsProvider中的設(shè)置項(xiàng)。

權(quán)限問(wèn)題
查閱SettingsProvider的設(shè)置項(xiàng)不需要聲明任何權(quán)限。

修改SettingsProvider需要權(quán)限:

android.permission.WRITE_SETTINGS,Protection level: signature
Secure數(shù)據(jù):android.permission.WRITE_SECURE_SETTINGS,Not for use by third-party applications.
對(duì)已Global和Secure模塊,還需要關(guān)心上文中的isGlobalOrSecureSettingRestrictedForUser()方法設(shè)置到的限制。

總結(jié)
本文從SettingsProvider的啟動(dòng)過(guò)程到使用SettingsProvider查詢插入數(shù)據(jù)進(jìn)行了詳細(xì)的過(guò)程描述,在SettingsProvider的啟動(dòng)過(guò)程中,需要?jiǎng)?chuàng)建數(shù)據(jù)庫(kù),把默認(rèn)設(shè)置項(xiàng)值寫(xiě)入到數(shù)據(jù)庫(kù),把數(shù)據(jù)中的所有數(shù)據(jù),遷移到xml文件中。SettingsProvider的查詢和插入結(jié)合章節(jié)“SettingsProvider的啟動(dòng)過(guò)程”中的層次關(guān)系圖,一目了然。對(duì)于第三方APP只有讀沒(méi)有寫(xiě)的能力。由于SettingsProvider的特性,雖然SettingsProvider是跨進(jìn)程通信,但是由于從多個(gè)層次都做了緩存,且SettingsProvider中的同步協(xié)作機(jī)制,只要時(shí)間都是花費(fèi)在Binder通信上面,但是Binder通信是一種快速的跨進(jìn)程通信的過(guò)程,所以在主線程(UI線程)中可以直接使用SettingsProvider查閱插入數(shù)據(jù)而不會(huì)導(dǎo)致UI阻塞導(dǎo)致ANR(應(yīng)用程序無(wú)響應(yīng))。另外,由于SettingsProvider的特性和限制,SettingsProvider不予寫(xiě)入過(guò)多的數(shù)據(jù),最好只是系統(tǒng)設(shè)置相關(guān)的設(shè)置項(xiàng)才保存到SettingsProvider中,同時(shí)也不適合寫(xiě)入過(guò)大的數(shù)據(jù),否則將會(huì)嚴(yán)重影響SettingsProvider的性能。

原作者:FamilyYuan
鏈接:https://www.jianshu.com/p/d48977f220ee

http://www.risenshineclean.com/news/22779.html

相關(guān)文章:

  • 公司做網(wǎng)站需要準(zhǔn)備什么東西整合營(yíng)銷策略有哪些
  • 珠海移動(dòng)網(wǎng)站建設(shè)報(bào)價(jià)網(wǎng)站制作公司怎么找
  • 網(wǎng)站開(kāi)發(fā)線框四年級(jí)下冊(cè)數(shù)學(xué)優(yōu)化設(shè)計(jì)答案
  • 阿拉丁建站系統(tǒng)谷歌網(wǎng)站優(yōu)化推廣
  • 愛(ài)做片視頻網(wǎng)站百度收錄入口
  • 做網(wǎng)站上傳照片的尺寸搜索排名優(yōu)化公司
  • 網(wǎng)站開(kāi)發(fā)框架圖欽州seo
  • 哪有做網(wǎng)站的 優(yōu)幫云seo超級(jí)外鏈發(fā)布
  • 西寧網(wǎng)站制作哪家公司好網(wǎng)站設(shè)計(jì)模板網(wǎng)站
  • 韶關(guān)網(wǎng)站建設(shè)廣告聯(lián)盟平臺(tái)掛機(jī)賺錢
  • 采購(gòu)網(wǎng)站平臺(tái)遼陽(yáng)網(wǎng)站seo
  • 建站行業(yè)現(xiàn)狀探討今日頭條權(quán)重查詢
  • 網(wǎng)站搭建費(fèi)用明細(xì)樂(lè)天seo培訓(xùn)
  • 蒙山縣網(wǎng)站建設(shè)鞍山seo外包
  • 個(gè)人證書(shū)查詢網(wǎng)全國(guó)聯(lián)網(wǎng)南寧seo優(yōu)化公司排名
  • 網(wǎng)站設(shè)計(jì)主要包含3個(gè)方面市場(chǎng)調(diào)研報(bào)告萬(wàn)能模板
  • 如何做網(wǎng)站拉動(dòng)條黑帽seo是什么
  • 網(wǎng)頁(yè)設(shè)計(jì)怎樣設(shè)置圖片大小公司seo排名優(yōu)化
  • 網(wǎng)站建設(shè)開(kāi)發(fā)軟件有哪些關(guān)鍵詞推廣優(yōu)化排名品牌
  • asp 網(wǎng)站開(kāi)發(fā) 軟件怎么做網(wǎng)絡(luò)平臺(tái)
  • 如何做好網(wǎng)站管理工作深圳網(wǎng)絡(luò)推廣代運(yùn)營(yíng)
  • 即墨城鄉(xiāng)建設(shè)局網(wǎng)站2345瀏覽器官網(wǎng)
  • 如何找做網(wǎng)站的公司網(wǎng)絡(luò)廣告的形式
  • 網(wǎng)站充值 下模板aso排名優(yōu)化
  • 天津建設(shè)工程新希望seo權(quán)重是什么意思
  • 外貿(mào)公司的網(wǎng)站建設(shè)模板下載百度競(jìng)價(jià)廣告怎么收費(fèi)
  • 網(wǎng)站icp備案地網(wǎng)絡(luò)廣告有哪些形式
  • 鄭州網(wǎng)絡(luò)優(yōu)化實(shí)力樂(lè)云seo百度seo快速排名優(yōu)化軟件
  • 長(zhǎng)沙部分風(fēng)險(xiǎn)區(qū)域調(diào)整廈門seo
  • 新網(wǎng)站如何被快速收錄域名注冊(cè)萬(wàn)網(wǎng)