`
u011721609
  • 浏览: 41379 次
社区版块
存档分类
最新评论

Android设置铃声分析

 
阅读更多

代码其实没有几行,这里简单记录下学习的过程.

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中的相应方法
        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) {
                    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集合中,当然如果这个集合中已经存在这个键值对,那么也就不会执行这段操作db的代码了
                    mValues.put(name, value);
                }

settings.db结构如下:

上面示例中指定的TYPE_NOTIFICATION的数据如下(蓝色部分):

最后返回的就是file:///..........这个String数据,再转化成URI返回给调用者

OK,那么设置铃声的API, setAc.......执行的过程也类似:

public static boolean putString(ContentResolver resolver, String name, String value) {
//这里依然是检查设置的类型是否涉及到系统预置的安全设置集合,如果是,则直接返回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;
            }
//这里执行的是另一个静态内部类NameValueTable中的方法
            return putString(resolver, CONTENT_URI, name, value);
        }

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与相应value插入db的system表中,如果表中已经存在指定的类型字段怎么办? 请看上面的源码注释...
                resolver.insert(uri, values);
                return true;
            } catch (SQLException e) {
                Log.w(TAG, "Can't set key " + name + " in " + uri, e);
                return false;
            }
        }

最后总结下,从整个过程可以看到android系统的一些设计思想

1,设置铃声之前,要先知道有哪些系统铃声,所以需要扫描,android提供了xxx.media这个contentProvider为此服务,对应的数据库为internal.db/external-xx.db

2,拿到铃声,真正需要设置的时候,提供了Setting类管理这个过程,其对应的数据库为settings.db

  2.1 首先检查是否涉及到系统的一些安全设置参数,这里定义了Secure类来管理,如果涉及到系统安全,那么又分为两种情况:

    2.1.1如果是查询,则操作secure 表查询

    2.1.2 如果是写操作,则直接return

  2.2 不涉及到系统安全,就属于正常设置,接着定义了System类管理

3,查询操作的实际操作类NameValueCache, 其中定义了

  缓存name/value键值对的集合,避免每次操作都去操作数据库

  可以由调用者指定的uri,便于根据uri决定去操作哪张表

以及写操作的NameValueTable类,因为写操作涉及到id, 所以继承了BaseColumns类


分享到:
评论

相关推荐

    Android设置铃声实现代码

    主要介绍了Android设置铃声实现代码,以实例形式分析了Android中铃声设置的相关技巧,非常简单实用,需要的朋友可以参考下

    Android源码AudioManager和RingerManager分析文档

    在Android源码的基础上分析 音频文件的的扫描,系统铃声的管理以及情景模式的切换

    Android与蓝牙耳机建立连接的分析

    Android与蓝牙耳机建立连接的一些代码,希望能起到抛砖引玉的效果,大家一起学习

    新版Android开发教程.rar

    也有分析认为,谷歌并不想做一个简单的手机终端制造商或者软件平台开发商,而意在一统传统互联网和 移 动互联网。----------------------------------- Android 编程基础 4 Android Android Android Android 手机新...

    Android开发实验---通讯录.docx

    由于界面设置繁琐,代码页过多,所以挑出其中的一部分进行说明 首先是DB数据库建库过程: Android开发实验---通讯录全文共25页,当前为第3页。package com.xample.hivian.my_contact_manager.models.db; import ...

    Android开发 闹钟项目

    需求分析:详细分析用户对闹钟应用的需求,确定功能和界面设计。 b. 技术选型:选择适合Android开发的开发工具和技术框架,如Android Studio和Java或Kotlin语言。 c. 数据库设计:设计合适的数据库结构,用于存储...

    android电话系统数据流程分析

    首先抛开Android的一切概念来研究一下电话系统的最基本的描述。我们的手机首先用来打电话的,随后是需要一个电话本,随后是PIM,随后是网络应用,随后是云计算,随后是想我们的手机无所不能,替代PC。但是作为一个...

    android开发实例大全_王东华

    实例104: 设置手机中的铃声 368 实例105: 在线播放网络中的MP3 371 实例106: 在线下载音乐作为手机铃声 379 实例107: 播放GIF格式的动画 385 实例108: 在手机中播放MP4视频 391 实例109: 在线观看3GP视频 394 ...

    android编程获取和设置系统铃声和音量大小的方法

    主要介绍了android编程获取和设置系统铃声和音量大小的方法,实例分析了Android针对音频的相关操作技巧,需要的朋友可以参考下

    Android开关机动画铃声

    系统通常默认支持开机动画,开机关机铃声服务倒是预先定义了,不过使用时需要在/sysytem/media/下添加相应的音频文件。长按Power键 弹出关键选项,关机时也只是显示进度条...下面将逐步分析如何实 现这些定制化的需求。

    基于androidx的跑步app-包含源码-文档-演示视频.zip

    本论文首先对基于AndroidX的跑步软件进行了需求分析,从系统开发环境、系统目标、设计流程、功能设计等几个方面进行系统的总体设计,使用Java语言设计了基于AndroidX的跑步软件,主要完成了注册登陆、计步功能、计时...

    内容包含:蓝牙+NFC+传感器

    内容主要包含Android系统硬件功能开发:蓝牙、NFC、传感器、GPS、铃声设置、AR技术等技术讲解,主要帮助我们进行简单的分析和了解,能够让我们初步认识有关Android和硬件相结合使用的文档说明

    我的铃声点击呼叫「Мои Звонки Click to Call」-crx插件

    您可以配置向分析人员发送哪些呼叫,指定有关事件的其他信息,当然还可以在分析中设置目标。 - 与CRM系统集成 我的电话可以从我们的列表链接到CRM系统,从而扩展其功能。您可以直接从联系人卡片拨打电话,当来电时...

    C#播放铃声最简单实现方法

    具体分析如下: 因为只是做一个软件的闹铃播放效果,到网上找的时候试了几种,哎,都失败了,而且代码挺杂的,最终一句搞定了: 代码如下:// 窗体加载事件 private void TimeCue_Load(object sender, EventArgs e)...

    ophone多媒体编程

    7.5 案例分析——铃声DIY 327 7.6 小结 329 第8章 让程序在后台运行 330 8.1 Service概述 330 8.2 Service编程实践 331 8.2.1 创建Service 331 8.2.2 启动和停止Service 333 8.2.3 通知用户 335 8.2.4 不...

Global site tag (gtag.js) - Google Analytics