首页 > 代码库 > 实现手机来电铃声,通知铃声、警告铃声等音频定制化功能(一,添加扫描分区myimage)

实现手机来电铃声,通知铃声、警告铃声等音频定制化功能(一,添加扫描分区myimage)

需求功能说明:

    该定制需求在各个国家民族差异化为背景下产生的,要求在系统中增加一个新的分区如myimage,用以实现存放定制资源,以符合不同国家民族的用户的体验。例如在myimage下新建media文件夹用于存放定制的音频资源,如果系统myimage分区下,media目录下存在相关目录或文件,则系统摒弃系统重名默认资源,优先选择该分区下的定制资源。
注!
    本篇博文结合从系统启动完成之后系统对多媒体文件的扫描流程进行分析和修改。修改思路为:优先扫描定制分区西下定制音频资源并将扫描结果插入相对应的数据库中。若定制分区下不存在资源目录或文件,则将系统默认资源的扫描结果插入相对应的数据库,若定制资源文件名与系统资源文件名相同,则优先插入定制资源。
参考代码:Android4.1.2_r2

1.当系统启动完成,MediaScannerReceiver接收到系统启动完成的广播,并调用scan()方法对内置存储设备进行扫描。

相关类:MediaScannerReceiver.java是一个BroadcastReceiver,接收广播,进行媒体扫描,是MediaScanner提供的接口,主要负责接收广播并启动MediaScannerService具体执行扫描工作。
方法调用流程:onReceiver()-->scan()-->scanDirectories()
具体代码如下:
public voidonReceive(Context context, Intent intent) {
......
   if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {//接收到系统启动完成的广播
        // 扫描内部存储设备,INTERNAL_VOLUME的值为"internal"
        scan(context, MediaProvider.INTERNAL_VOLUME);
    }
......
}
private voidscan(Context context, String volume) {
         Bundle args = new Bundle();
         args.putString("volume", volume);
        //通过startService()启动服务开始内部Media文件扫描,这里的参数volume的值为”internal”。
         context.startService(new Intent(context, MediaScannerService.class).putExtras(args));
}

2.MediaScannerService服务被启动,主要负责媒体扫描。详细来说MediaScannerService用于接收系统重启后、插拔SD卡、文件复制与拷贝等消息,收到消息后会启动一个handler去处理消息并扫描,最后会将扫描结果放到MediaProvider提供的数据库中去。这里我们先从onCreate生命周期函数开始分析。

主要方法如下:handleMessage()-->scan()

public voidonCreate(){
     PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
    //扫描期间保持屏幕常亮
    mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
    ......
    //启动一个工作线程来处理大量的计算任务,由于MediaScannerService实现了Runnable,因此大量计算的任务都是在Runnable的run()方法中来执行的。
    Thread thr = new Thread(null, this, "MediaScannerService");
     thr.start();
    ......
}
//在Runnable.run()中执行消息循环,把通过Handler发送过来的消息在工作线程中执行。
public voidrun(){
         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_LESS_FAVORABLE);
         Looper.prepare();
         mServiceLooper = Looper.myLooper();
         mServiceHandler = new ServiceHandler();
         Looper.loop();
     }
//Service对象在被启动的时候,onStartCommand()会被调用。
public intonStartCommand(Intent intent, int flags, int startId){
        ....
         Message msg = mServiceHandler.obtainMessage();
         msg.arg1 = startId;
         msg.obj = intent.getExtras();
         mServiceHandler.sendMessage(msg);
         return Service.START_REDELIVER_INTENT;
}
private final classServiceHandlerextends Handler
     {
         @Override
         public void handleMessage(Message msg){
            ......
             Bundle arguments = (Bundle) msg.obj;
             String filePath = arguments.getString("filepath");
                     String volume = arguments.getString("volume");
                     String[] directories = null;
                    //根据前文我们知道这里的形参volume的值是“internal”,因此这里会默认扫描"system/media"路径。
                    if (MediaProvider.INTERNAL_VOLUME.equals(volume)) {
                         directories = new String[] {
                                 Environment.getRootDirectory() + "/media",
                                 //注:根据需求这里我们新增加了一个新的分区如myimage用来存放我们需要定制的资源,因此在这里我们在对内部进行扫描的时候增加一个扫描路径,也就是“myimage/media”。
                                 "myimage/media"
                        };
                     }
                    if (directories != null) {
                        ......
                         scan(directories, volume);
                         ......
                     }
        ......
}
//从 getContentResolver获得一个ContentResover,然后直接插入根据AIDL,这个ContentResover的另一端是MediaProvider。这里主要获得了一个扫描URI。
private voidscan(String[] directories, String volumeName) {
         ContentValues values = new ContentValues();
         values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName);
         Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values);
        //这里不做任何修改以保持原有的数据库结构不发生变化。
         Uri uri = Uri.parse("file://" + directories[0]);
        //发送开始扫描的通知
         sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
         try {
             if (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) {
                 openDatabase(volumeName);
             }
             MediaScanner scanner = createMediaScanner();
            //扫描目录
             scanner.scanDirectories(directories, volumeName);
         } catch (Exception e) {
             Log.e(TAG, "exception in MediaScanner.scan()", e);
         }
         //删除扫描路径,以示扫描结束(这里由于时间问题不做验证,大概也就是会有地方捕捉该删除动作,并发出广播,以通知扫描结束)
         getContentResolver().delete(scanUri, null, null);
         sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
         mWakeLock.release();
}

实现手机来电铃声,通知铃声、警告铃声等音频定制化功能(一,添加扫描分区myimage)