Android APP與媒體儲存服務的互動_Android

來源:互聯網
上載者:User

簡介:
本文介紹如何在 Android 中,開發人員的 APP 如何使用媒體儲存服務(包含MediaScanner、MediaProvider以及媒體資訊解析等部分),包括如何把 APP 新增或修改的檔案更新到媒體資料庫、如何在多媒體應用中隱藏 APP 產生的檔案、如何監聽媒體資料庫的變化等等。
Android 原生有一套媒體儲存服務,進程名是 android.process.media,主要負責把磁碟中的檔案資訊儲存到資料庫當中,供其他 APP 使用以及 MTP 模式使用。因此 APP 可以隨時快速查詢到機器上有多少音樂,音樂的時間長度、標題、藝術家、專輯封面都可以擷取到。下面就介紹我們開發的 APP 如何與這個媒體儲存服務打交道。
Note:MTP 模式是 Android 3.0 開始引入的,其資料來源於媒體儲存服務。
隱藏多媒體檔案
應用情境:APP 產生了圖片/音樂/視頻類檔案,不想讓它顯示在圖庫/音樂播放器。市面上有不少遊戲,它的圖片和音效檔案沒有做隱藏,出現在使用者的圖庫/音樂播放器當中,引起使用者反感。如果使用者把它刪除了,又可能會影響 APP 正常運行。

方法一:把檔案設為隱藏。Linux 裡檔案前加點就是隱藏,例如“檔案A”改成“.檔案A”。或者把副檔名去掉,這樣媒體儲存服務掃描時就不會將其當作多媒體檔案。
方法二:在檔案夾下產生一個名為“.nomedia”的空白檔案。這樣同一個檔案夾下的所有檔案都不會被當作多媒體檔案。

添加/修改多媒體檔案
應用情境:APP 建立了一個新的多媒體檔案,或者修改了一個已有多媒體檔案。例如 APP 下載了一個音樂檔案,需要通知媒體儲存服務,使用者就能在音樂播放器中看到這個檔案。否則,只有下次媒體儲存服務開始掃描整個磁碟,才會發現 APP 產生的新檔案。

方法一
如果只有一個檔案,並且不需要得到結果返回,直接發 Intent 通知媒體儲存服務即可。

複製代碼 代碼如下:

import java.io.File;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;

private static void requestScanFile(Context context, File file) {
    Intent i = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    i.setData(Uri.fromFile(file));
    context.sendBroadcast(i);
}

private static void requestScanFile(Context context, String file) {
    Intent i = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    i.setData(Uri.parse("file://" + Uri.encode(file)));
    context.sendBroadcast(i);
}


Note:如果使用 Uri.parse() 從檔案名稱產生 Uri,檔案名稱必須要先 Uri.encode(),作用是把保留字元轉義。例如檔案名稱若包含“?”,不經過 Uri.encode 轉義的話會被當作是查詢參數,這樣 uri.getPath() 擷取的檔案路徑會丟失“?”之後的部分。

方法二
如果只有一個檔案,並且需要檔案 uri 結果返回,則使用回呼函數。

複製代碼 代碼如下:

import android.media.MediaScannerConnection;
import android.net.Uri;

private void requestScanFile(Context context, String file) {
    MediaScannerConnection.scanFile(context, new String[] {file},
        null, // mime types,可不指定
        mListener);
}

MediaScannerConnection.OnScanCompletedListener mListener =
        new MediaScannerConnection.OnScanCompletedListener() {
    public void onScanCompleted(String path, Uri uri) {
        // TODO: 擷取到該檔案在多媒體資料庫中的 uri,進行下一步動作
    }
};


Note:還有一種方法,可以先往多媒體資料庫插入一條包含檔案路徑的記錄,插入後可以得到其 uri;然後再使用方法一通知媒體儲存服務掃描此檔案,把檔案資訊(如專輯名)補充完整。但不推薦使用這種方法,因為獲得 uri 時檔案資訊並不完整。

方法三
如果檔案較多,則發 Intent 通知媒體儲存服務掃描整個磁碟。這種做法不是特別好,但又沒發現其他更好的介面。第三方檔案管理如“ES 檔案管理工具”就是使用這種方法的。

複製代碼 代碼如下:

import android.content.Context;
import android.content.Intent;
import android.net.Uri;

private static void requestScanDisk(Context context) {
    Intent i = new Intent(Intent.ACTION_MEDIA_MOUNTED);
    String path = Environment.getExternalStorageDirectory().getPath();
    i.setData(Uri.parse("file://" + Uri.encode(path)));
    context.sendBroadcast(i);
}

監聽資料變化
應用情境:多媒體資料庫有變化,需要重新整理 APP 顯示介面。比較好理解,磁碟中的多媒體檔案有新增、刪除或者修改,APP 介面上要即時反應這些變化,重新整理顯示介面。

方法一
監聽媒體儲存相關 Intent。接受到 Intent 後,重新查詢一次資料庫。我們需要注意的 Intent 主要有以下幾個:
1、Intent.ACTION_MEDIA_SCANNER_FINISHED: 媒體儲存服務掃描整個磁碟完成後會發這個 Intent。可能有新增較多檔案或者被刪除。
2、Intent.ACTION_MEDIA_SCANNER_SCAN_FILE: 媒體儲存服務掃描單個檔案。

複製代碼 代碼如下:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;

private void registerReceiver(Context context) {
    IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
    filter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
    filter.addAction(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    filter.addDataScheme("file");
    context.registerReceiver(mReceiver, filter);
}

private BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Uri uri = intent.getData();
        if (uri != null && uri.getScheme().equals("file")) {
            Log.v("Receiver", "BroadcastReceiver action = " + action + ", uri = " + uri);
            if (Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)) {
                String filePath = uri.getPath();
                // TODO: filePath 檔案已改變,APP 重新整理介面
            } else if (Intent.ACTION_MEDIA_SCANNER_FINISHED.equals(action)) {
                // TODO: 整個磁碟掃描完成,APP 重新整理介面
            }
        }
    }
};


另外,在 Intent.MEDIA_SCANNER_STARTED 和 Intent.ACTION_MEDIA_SCANNER_FINISHED 之間的時間裡,媒體儲存服務正在掃描檔案,資料庫會有變化,所以只有在收到 Intent.ACTION_MEDIA_SCANNER_FINISHED 之後查詢的結果才是準確的。如果要檢測媒體儲存服務是否在掃描可以用以下方法:
複製代碼 代碼如下:

import android.content.ContentResolver;
import android.database.Cursor;
import android.os.Environment;
import android.provider.MediaStore;

private static boolean isMediaScannerScanning(ContentResolver cr) {
    Cursor cursor = null;
    try {
        cursor = cr.query(MediaStore.getMediaScannerUri(), new String[] {
            MediaStore.MEDIA_SCANNER_VOLUME}, null, null, null);

        if (cursor != null && cursor.getCount() > 0) {
            cursor.moveToFirst();
            return "external".equals(cursor.getString(0));
        }
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }

    return false;
}


Note:APP 可能還需要監聽存放裝置的變化,例如 SD 記憶卡拔出、磁碟掛載出去(USB 大型存放區模式)等等,這些情況下可能要把檔案顯示介面清空,或者退出程式。各個手機對於每個 Intent 定義可能略有不同,但基本上是以下幾個:
1、Intent.ACTION_MEDIA_EJECT: 存放裝置正常移除,例如在設定裡卸載儲存空間。
2、Intent.ACTION_MEDIA_UNMOUNTED: 存放裝置正常卸載,通常與 EJECT 先後出現。
3、Intent.ACTION_MEDIA_BAD_REMOVAL: 非正常移除存放裝置,例如硬插拔 SD 記憶卡。

方法二
監聽資料庫變化。如果需要在資料庫發生變化時能即時接收到通知,可以使用 ContentObserver。

複製代碼 代碼如下:

import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;

private ContentObserver mContentObserver = new ContentObserver(null) {
    @Override
    public void onChange(boolean selfChange) { // 往後相容
        onChange(selfChange, null);
    }

    public void onChange(boolean selfChange, Uri uri) {
        // TODO: 資料已改變,APP 重新查詢資料庫並重新整理介面
    }
};

private void setupCursor(Context context, Cursor c) {
    c.unregisterContentObserver(mContentObserver); // c 為需要顯示的資料
}


此外,使用 CursorAdapter 和顯示的 ListView 綁定,也可以達到同樣目的。當 Cursor 內容發現變化,ListView 也會相應自動重新整理。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.