標籤:android style io ar color sp java 檔案 on
需求功能說明: 該定製需求在各個國家民族差異化為背景下產生的,要求在系統中增加一個新的分區如myimage,用以實現存放定製資源,以符合不同國家民族的使用者的體驗。例如在myimage下建立media檔案夾用於存放定製的音頻資源,如果系統myimage分區下,media目錄下存在相關目錄或檔案,則系統摒棄系統重名預設資源,優先選擇該分區下的定製資源。
注!
本篇博文結合從系統啟動完成之後系統對多媒體檔案的掃描流程進行分析和修改。修改思路為:優先掃描定製分區西下定製音頻資源並將掃描結果插入相對應的資料庫中。若定製分區下不存在資來源目錄或檔案,則將系統預設資源的掃描結果插入相對應的資料庫,若定製資源檔名與系統資源檔名相同,則優先插入定製資源。
參考代碼:Android4.1.2_r21.當系統啟動完成,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)