Android设置铃声分析
测试机:小米2.3.5版本代码其实没有几行,这里简单记录下学习的过程.
Android系统启动时会扫描系统与SD卡中的对媒体文件,分别存入数据库sqlite中,以contentProvider的形式对外提供服务
路径:/data/data/com.android.providers.media/databases/XXX...
可以看到有2个db文件, 一个是系统的,一个是sd卡里的
用SQLite Expert打开internal.db,部分截图如下:
这里面记录了音频audio、视频video、图片images的相关数据信息,我们以音频audio为例,蓝色部分audio_meta就是audio数据表,打开之后就可以看到详细信息了,里面列出了系统内部的所有音频文件,各个字段在android.provider.MediaStore中都定义有相应的常量,如id --- MediaStore.Audio.Media._ID.
而这里面有想说下这四个字段
含义在源码里都有说明,看了一遍数据,发现这四个字段同时有且仅有一个字段为1,也就是对于一个多媒体文件只能是这四种中的一种,默认为0,如果是某种类型,则android系统默认置为1,所以也就明白了为什么很多扫描系统通知或者来电铃声的示例代码中,都会有一个类似的条件语句:is_notification = 1.
如:
/**
* 扫描系统内部通知铃声
*/
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));
}
}
这里 is_notification != 0,效果是一样的,除非哪天google再定义个2, 3 ......
上面扯了些其他的,关于设置铃声的方法,系统提供了一个铃声管理器android.provider.RingtoneManager,其中提供了获取与设置铃声的API
如:Uri uri = RingtoneManager.getActualDefaultRingtoneUri(MediaActivity.this, RingtoneManager.TYPE_NOTIFICATION);可以获取到当前系统的通知铃声uri
第二个参数可以指定获取的铃声类型,还有其他的TYPE_RINGTONE,TYPE_ALARM, TYPE_ALL
设置铃声的API:
RingtoneManager.setActualDefaultRingtoneUri(MediaActivity.this,
RingtoneManager.TYPE_NOTIFICATION, Uri.parse(data.get(position)));
第二个参数同上,最后一个是指定一个新的Uri, 这里的data.get(position)就是在上面的扫描代码扫描出的所有通知铃声path路径中选泽一个,然后在解析成一个URI对象传入即可
那么android是如何获取指定类型的系统铃声呢?
这涉及到另一个类android.provider.Settings
相关源码如下:
public static Uri getActualDefaultRingtoneUri(Context context, int type) {
//根据指定的类型获取Settings类中对应的类型,这里RingtoneManager.TYPE_NOTIFICATION对应的为Settings.System.NOTIFICATION_SOUND,其实也就是下面所说的system表中的一个name字段名
String setting = getSettingForType(type);
if (setting == null) return null;
//调用Settings类中静态内部类System中的相应方法 www.zzzyk.com
final String uriString = Settings.System.getString(context.getContentResolver(), setting);
return uriString != null ? Uri.parse(uriString) : null;
}
public synchronized static String getString(ContentResolver resolver, String name) {
//MOVED_TO_SECURE是System类中定义的一个hashSet集合,在Android系统启动时,会初始化30(目前是30)条涉及系统安全的设置数据(如果http代理设置,wifi相关设置),并且存入数据库中,与多媒体的db不同,系统默认存放在settings.db中,路径为/data/data/com.android.providers.settings/databases,具体是存放在settings.db数据库实例的secure表中,用工具打开,可以看到此表中恰好有30条数据。说了那么多,其实这里是检查你所指定的类型也就是db中的字段在不在这个集合中,如果在,则会调用Settings类中的另一个静态内部类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);
}
//如果不在那个涉及系统安全的设置集合中,则调用Settings中定义的一个缓存类NameValueCache中的getString(...)
if (sNameValueCache == null) {
sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI,
CALL_METHOD_GET_SYSTEM);
}
return sNameValueCache.getString(resolver, name);
}
//NameValueCache中getString()方法部分代码
Cursor c = null;
try {
//mUri == "content://settings/system"在NameValueCache初始化时赋值,指定查询的是settings.db中的system表,同理上面提到的Secure类的getString(...)中调用的也是这个缓存类的同名方法,只不过mUri被指定为查询secure表(这2个表中除了id,只有name与value2个字段,分别指定设置的类型与对应的值)
c = cp.query(mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER,
new String[]{name}, null);
if (c == null) {
&nb
补充:移动开发 , Android ,