小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Android設(shè)置鈴聲分析

 lifei_szdz 2013-09-09

Android設(shè)置鈴聲分析

測試機:小米2.3.5版本

 

代碼其實沒有幾行,這里簡單記錄下學(xué)習(xí)的過程.

Android系統(tǒng)啟動時會掃描系統(tǒng)與SD卡中的對媒體文件,分別存入數(shù)據(jù)庫sqlite中,以contentProvider的形式對外提供服務(wù)

路徑:/data/data/com.android.providers.media/databases/XXX...

可以看到有2個db文件, 一個是系統(tǒng)的,一個是sd卡里的

用SQLite Expert打開internal.db,部分截圖如下:

這里面記錄了音頻audio、視頻video、圖片images的相關(guān)數(shù)據(jù)信息,我們以音頻audio為例,藍(lán)色部分audio_meta就是audio數(shù)據(jù)表,打開之后就可以看到詳細(xì)信息了,里面列出了系統(tǒng)內(nèi)部的所有音頻文件,各個字段在android.provider.MediaStore中都定義有相應(yīng)的常量,如id --- MediaStore.Audio.Media._ID.

 

而這里面有想說下這四個字段

含義在源碼里都有說明,看了一遍數(shù)據(jù),發(fā)現(xiàn)這四個字段同時有且僅有一個字段為1,也就是對于一個多媒體文件只能是這四種中的一種,默認(rèn)為0,如果是某種類型,則android系統(tǒng)默認(rèn)置為1,所以也就明白了為什么很多掃描系統(tǒng)通知或者來電鈴聲的示例代碼中,都會有一個類似的條件語句:is_notification = 1.

如:

復(fù)制代碼
/**
     * 掃描系統(tǒng)內(nèi)部通知鈴聲
     */
    private void scannerMediaFile() {
        ContentResolver cr = this.getContentResolver();
        Cursor cursor = cr.query(MediaStore.Audio.Media.INTERNAL_CONTENT_URI,
                new String[] { MediaStore.Audio.Media._ID,
                        MediaStore.Audio.Media.DATA,
                        MediaStore.Audio.Media.TITLE }, "is_notification != ?",
                new String[] { "0" }, "_id asc");

        if (cursor == null) {
            return;
        }

        while (cursor.moveToNext()) {
            data.add(cursor.getString(1));
        }
    }
復(fù)制代碼

這里 is_notification != 0,效果是一樣的,除非哪天google再定義個2, 3 ......

 

上面扯了些其他的,關(guān)于設(shè)置鈴聲的方法,系統(tǒng)提供了一個鈴聲管理器android.provider.RingtoneManager,其中提供了獲取與設(shè)置鈴聲的API

如:Uri uri = RingtoneManager.getActualDefaultRingtoneUri(MediaActivity.this, RingtoneManager.TYPE_NOTIFICATION);可以獲取到當(dāng)前系統(tǒng)的通知鈴聲uri

第二個參數(shù)可以指定獲取的鈴聲類型,還有其他的TYPE_RINGTONE,TYPE_ALARM, TYPE_ALL

設(shè)置鈴聲的API:

RingtoneManager.setActualDefaultRingtoneUri(MediaActivity.this,
                        RingtoneManager.TYPE_NOTIFICATION, Uri.parse(data.get(position)));

第二個參數(shù)同上,最后一個是指定一個新的Uri, 這里的data.get(position)就是在上面的掃描代碼掃描出的所有通知鈴聲path路徑中選澤一個,然后在解析成一個URI對象傳入即可

 

那么android是如何獲取指定類型的系統(tǒng)鈴聲呢?

這涉及到另一個類android.provider.Settings

相關(guān)源碼如下:

復(fù)制代碼
public static Uri getActualDefaultRingtoneUri(Context context, int type) {
//根據(jù)指定的類型獲取Settings類中對應(yīng)的類型,這里RingtoneManager.TYPE_NOTIFICATION對應(yīng)的為Settings.System.NOTIFICATION_SOUND,其實也就是下面所說的system表中的一個name字段名
        String setting = getSettingForType(type);
        if (setting == null) return null;
//調(diào)用Settings類中靜態(tài)內(nèi)部類System中的相應(yīng)方法
        final String uriString = Settings.System.getString(context.getContentResolver(), setting);
        return uriString != null ? Uri.parse(uriString) : null;
    }
復(fù)制代碼

 

復(fù)制代碼
public synchronized static String getString(ContentResolver resolver, String name) {
//MOVED_TO_SECURE是System類中定義的一個hashSet集合,在Android系統(tǒng)啟動時,會初始化30(目前是30)條涉及系統(tǒng)安全的設(shè)置數(shù)據(jù)(如果http代理設(shè)置,wifi相關(guān)設(shè)置),并且存入數(shù)據(jù)庫中,與多媒體的db不同,系統(tǒng)默認(rèn)存放在settings.db中,路徑為/data/data/com.android.providers.settings/databases,具體是存放在settings.db數(shù)據(jù)庫實例的secure表中,用工具打開,可以看到此表中恰好有30條數(shù)據(jù)。說了那么多,其實這里是檢查你所指定的類型也就是db中的字段在不在這個集合中,如果在,則會調(diào)用Settings類中的另一個靜態(tài)內(nèi)部類Secure中的getString(...)方法
            if (MOVED_TO_SECURE.contains(name)) {
                Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
                        + " to android.provider.Settings.Secure, returning read-only value.");
                return Secure.getString(resolver, name);
            }
//如果不在那個涉及系統(tǒng)安全的設(shè)置集合中,則調(diào)用Settings中定義的一個緩存類NameValueCache中的getString(...)
            if (sNameValueCache == null) {
                sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI,
                                                     CALL_METHOD_GET_SYSTEM);
            }
            return sNameValueCache.getString(resolver, name);
        }
復(fù)制代碼
復(fù)制代碼
//NameValueCache中g(shù)etString()方法部分代碼
Cursor c = null;
            try {
//mUri == "content://settings/system"在NameValueCache初始化時賦值,指定查詢的是settings.db中的system表,同理上面提到的Secure類的getString(...)中調(diào)用的也是這個緩存類的同名方法,只不過mUri被指定為查詢secure表(這2個表中除了id,只有name與value2個字段,分別指定設(shè)置的類型與對應(yīng)的值)
                c = cp.query(mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER,
                             new String[]{name}, null);
                if (c == null) {
                    Log.w(TAG, "Can't get key " + name + " from " + mUri);
                    return null;
                }

                String value = c.moveToNext() ? c.getString(0) : null;
                synchronized (this) {
//查詢完講name/value鍵值對放入mValues集合中,當(dāng)然如果這個集合中已經(jīng)存在這個鍵值對,那么也就不會執(zhí)行這段操作db的代碼了
                    mValues.put(name, value);
                }
復(fù)制代碼

settings.db結(jié)構(gòu)如下:

上面示例中指定的TYPE_NOTIFICATION的數(shù)據(jù)如下(藍(lán)色部分):

最后返回的就是file:///..........這個String數(shù)據(jù),再轉(zhuǎn)化成URI返回給調(diào)用者

 

OK,那么設(shè)置鈴聲的API, setAc.......執(zhí)行的過程也類似:

復(fù)制代碼
public static boolean putString(ContentResolver resolver, String name, String value) {
//這里依然是檢查設(shè)置的類型是否涉及到系統(tǒng)預(yù)置的安全設(shè)置集合,如果是,則直接返回false
            if (MOVED_TO_SECURE.contains(name)) {
                Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
                        + " to android.provider.Settings.Secure, value is unchanged.");
                return false;
            }
//這里執(zhí)行的是另一個靜態(tài)內(nèi)部類NameValueTable中的方法
            return putString(resolver, CONTENT_URI, name, value);
        }
復(fù)制代碼

 

復(fù)制代碼
protected static boolean putString(ContentResolver resolver, Uri uri,
                String name, String value) {
            // The database will take care of replacing duplicates.
            try {
                ContentValues values = new ContentValues();
                values.put(NAME, name);
                values.put(VALUE, value);
//指定類型name與相應(yīng)value插入db的system表中,如果表中已經(jīng)存在指定的類型字段怎么辦? 請看上面的源碼注釋...
                resolver.insert(uri, values);
                return true;
            } catch (SQLException e) {
                Log.w(TAG, "Can't set key " + name + " in " + uri, e);
                return false;
            }
        }
復(fù)制代碼

 

最后總結(jié)下,從整個過程可以看到android系統(tǒng)的一些設(shè)計思想

1,設(shè)置鈴聲之前,要先知道有哪些系統(tǒng)鈴聲,所以需要掃描,android提供了xxx.media這個contentProvider為此服務(wù),對應(yīng)的數(shù)據(jù)庫為internal.db/external-xx.db

2,拿到鈴聲,真正需要設(shè)置的時候,提供了Setting類管理這個過程,其對應(yīng)的數(shù)據(jù)庫為settings.db

  2.1 首先檢查是否涉及到系統(tǒng)的一些安全設(shè)置參數(shù),這里定義了Secure類來管理,如果涉及到系統(tǒng)安全,那么又分為兩種情況:

    2.1.1 如果是查詢,則操作secure 表查詢

    2.1.2 如果是寫操作,則直接return

  2.2 不涉及到系統(tǒng)安全,就屬于正常設(shè)置,接著定義了System類管理

3,查詢操作的實際操作類NameValueCache, 其中定義了

  緩存name/value鍵值對的集合,避免每次操作都去操作數(shù)據(jù)庫

  可以由調(diào)用者指定的uri,便于根據(jù)uri決定去操作哪張表

以及寫操作的NameValueTable類,因為寫操作涉及到id, 所以繼承了BaseColumns類

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多