1. 程式人生 > >如何獲取手機上所有的音樂檔案

如何獲取手機上所有的音樂檔案

(1)如何獲取手機裡所有歌曲的資訊?

    如果要解決這個問題,那麼我們首先要知道在Android系統中,是如何對歌曲資訊進行管理的。

    在Android中,系統為多媒體型別的檔案(比如圖片、音訊、視訊等)建立了資料庫(sqlite資料庫),從而完成多媒體資料的維護工作。我們當然可以不用這些系統的資料庫,比如說,如果我們想獲取所有歌曲,我們可以掃描sd上所有的資料夾中的檔案,然後根據檔案的字尾名,就可以取到我們想要的mp3、wma檔案等。但是,這樣的操作是非常效率低下的,所以是行不通的。

    Android系統為我們建立起多媒體資料庫之後,便把多媒體常用的資訊,比如歌曲名、檔案大小、播放時長、專輯、歌手等常用資訊儲存在了資料庫裡,那我們可以直接用多媒體庫中的資料,完成這個需求。雖然我們需要用多媒體庫,但是我們不能直接操作。Android為這些常用的需要共享的資料(多媒體和聯絡人等),建立了ContentProvider,因此,如果我們想獲取到這些資訊,我們就需要用ContentProvider。

    在開始介紹之前,先給出需要用到的歌曲的實體類


MediaStore中定義了一系列的資料表格,通過Android ContentResolver提供的查詢介面,我們可以得到各種需要的資訊。下面我們重點介紹查詢SD卡上的音樂檔案資訊。

先來了解一下ContentResolver的查詢介面:

Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

Uri:指明要查詢的資料庫名稱加上表的名稱,從MediaStore中我們可以找到相應資訊的引數。
Projection: 指定查詢資料庫表中的哪幾列,返回的遊標中將包括相應的資訊。Null則返回所有資訊。
selection: 指定查詢條件
selectionArgs:引數selection裡有 ?這個符號是,這裡可以以實際值代替這個問號。如果selection這個沒有?的話,那麼這個String陣列可以為null。
SortOrder:指定查詢結果的排列順序

下面的命令將返回所有在外部儲存卡上的音樂檔案的資訊:

Cursor cursor = query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);

得到cursor後,我們可以呼叫Cursor的相關方法具體的音樂資訊:

歌曲ID:MediaStore.Audio.Media._ID
Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID));

歌曲的名稱:MediaStore.Audio.Media.TITLE
String tilte = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));

歌曲的專輯名:MediaStore.Audio.Media.ALBUM
String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
 
歌曲的歌手名:MediaStore.Audio.Media.ARTIST
String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
 
歌曲檔案的路徑:MediaStore.Audio.Media.DATA
String url = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));

歌曲的總播放時長:MediaStore.Audio.Media.DURATION
Int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));

歌曲檔案的大小:MediaStore.Audio.Media.SIZE
Int size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE));

/**
 * 
 * @ClassName: com.example.mediastore.Song
 * @Description: 歌曲實體類
 * @author zhaokaiqiang
 * @date 2014-12-4 上午11:49:59
 * 
 */
public class Song {

	private String fileName;
	private String title;
	private int duration;
	private String singer;
	private String album;
	private String year;
	private String type;
	private String size;
	private String fileUrl;

	public String getFileName() {
		return fileName;
	}

	public void setFileName(String fileName) {
		this.fileName = fileName;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public int getDuration() {
		return duration;
	}

	public void setDuration(int duration) {
		this.duration = duration;
	}

	public String getSinger() {
		return singer;
	}

	public void setSinger(String singer) {
		this.singer = singer;
	}

	public String getAlbum() {
		return album;
	}

	public void setAlbum(String album) {
		this.album = album;
	}

	public String getYear() {
		return year;
	}

	public void setYear(String year) {
		this.year = year;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getSize() {
		return size;
	}

	public void setSize(String size) {
		this.size = size;
	}

	public String getFileUrl() {
		return fileUrl;
	}

	public void setFileUrl(String fileUrl) {
		this.fileUrl = fileUrl;
	}

	public Song() {
		super();
	}

	public Song(String fileName, String title, int duration, String singer,
			String album, String year, String type, String size, String fileUrl) {
		super();
		this.fileName = fileName;
		this.title = title;
		this.duration = duration;
		this.singer = singer;
		this.album = album;
		this.year = year;
		this.type = type;
		this.size = size;
		this.fileUrl = fileUrl;
	}

	@Override
	public String toString() {
		return "Song [fileName=" + fileName + ", title=" + title
				+ ", duration=" + duration + ", singer=" + singer + ", album="
				+ album + ", year=" + year + ", type=" + type + ", size="
				+ size + ", fileUrl=" + fileUrl + "]";
	}

}

有了上面的這些資訊,我們完全可以做一個播放器了!

  有了實體類之後,我封裝了一個類,專門用來獲取歌曲資訊,下面是實現的程式碼

/**
 * 
 * @ClassName: com.example.mediastore.AudioUtils
 * @Description: 音訊檔案幫助類
 * @author zhaokaiqiang
 * @date 2014-12-4 上午11:39:45
 * 
 */
public class AudioUtils {

	/**
	 * 獲取sd卡所有的音樂檔案
	 * 
	 * @return
	 * @throws Exception
	 */
	public static ArrayList<Song> getAllSongs(Context context) {

		ArrayList<Song> songs = null;

		Cursor cursor = context.getContentResolver().query(
				MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
				new String[] { MediaStore.Audio.Media._ID,
						MediaStore.Audio.Media.DISPLAY_NAME,
						MediaStore.Audio.Media.TITLE,
						MediaStore.Audio.Media.DURATION,
						MediaStore.Audio.Media.ARTIST,
						MediaStore.Audio.Media.ALBUM,
						MediaStore.Audio.Media.YEAR,
						MediaStore.Audio.Media.MIME_TYPE,
						MediaStore.Audio.Media.SIZE,
						MediaStore.Audio.Media.DATA },
				MediaStore.Audio.Media.MIME_TYPE + "=? or "
						+ MediaStore.Audio.Media.MIME_TYPE + "=?",
				new String[] { "audio/mpeg", "audio/x-ms-wma" }, null);

		songs = new ArrayList<Song>();

		if (cursor.moveToFirst()) {

			Song song = null;

			do {
				song = new Song();
				// 檔名
				song.setFileName(cursor.getString(1));
				// 歌曲名
				song.setTitle(cursor.getString(2));
				// 時長
				song.setDuration(cursor.getInt(3));
				// 歌手名
				song.setSinger(cursor.getString(4));
				// 專輯名
				song.setAlbum(cursor.getString(5));
				// 年代
				if (cursor.getString(6) != null) {
					song.setYear(cursor.getString(6));
				} else {
					song.setYear("未知");
				}
				// 歌曲格式
				if ("audio/mpeg".equals(cursor.getString(7).trim())) {
					song.setType("mp3");
				} else if ("audio/x-ms-wma".equals(cursor.getString(7).trim())) {
					song.setType("wma");
				}
				// 檔案大小
				if (cursor.getString(8) != null) {
					float size = cursor.getInt(8) / 1024f / 1024f;
					song.setSize((size + "").substring(0, 4) + "M");
				} else {
					song.setSize("未知");
				}
				// 檔案路徑
				if (cursor.getString(9) != null) {
					song.setFileUrl(cursor.getString(9));
				}
				songs.add(song);
			} while (cursor.moveToNext());

			cursor.close();

		}
		return songs;
	}

}

 程式碼的思路很簡單,我們需要根據ContentResover獲取到一個Cursor,然後根據這個遊標,遍歷所有的歌曲的資訊。在上面的程式碼中,我們查詢出了包括歌名、路徑、檔案大小等在內的共10項資料,對於一般的應用這些足夠了。查詢出來之後,我們把資訊轉換成了實體類,這樣操作起來更加方便。

    如果要使用這個工具類,記得新增許可權 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

(2)如何及時更新媒體庫

    Android系統重新整理媒體庫的資料的時機,是在開機的時候,即手機一開機,系統便重新掃描一下sd卡,並將多媒體資料庫更新一下。如果使用者刪除了某一個音訊檔案,不重新開機的話,資料庫中的資料是不會更新的。那麼,如果我們想使用者一開啟軟體,就強制的更新多媒體資料庫,應該怎麼做呢?

    在4.4版本之前,我們可以使用傳送廣播的方式,強制重新整理多媒體庫

IntentFilter intentFilter = new IntentFilter(
				Intent.ACTION_MEDIA_SCANNER_STARTED);
		intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
		intentFilter.addDataScheme("file");
		sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
				Uri.parse("file://"
						+ Environment.getExternalStorageDirectory()
								.getAbsolutePath())));
傳送廣播之後,還需要註冊一個廣播接受者,來接受並處理掃描開始和結束事件
private class ScanReceiver extends BroadcastReceiver {
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			// 當系統開始掃描sd卡時,為了使用者體驗,可以加上一個等待框
			if (Intent.ACTION_MEDIA_SCANNER_STARTED.equals(action)) {
			}
			// 當系統掃描完畢時,停止顯示等待框,並重新查詢ContentProvider
			if (Intent.ACTION_MEDIA_SCANNER_FINISHED.equals(action)) {
			}
		}
	}
 通過這種方式,我們便可以強制更新媒體庫。

    但是,在4.4之後,Android對一些操作的許可權提高,如果在4.4的系統上使用這種方式,便會出現下面的錯誤

Caused by: java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.MEDIA_MOUNTED from pid=22360, uid=10163
這是因為在4.4之後,這個廣播只有系統應用才能發出,因此,我們不能使用這種方式了,我們可以使用下面的程式碼實現相同的功能:
MediaScannerConnection.scanFile(this, new String[] { Environment
				.getExternalStorageDirectory().getAbsolutePath() }, null, null);

  使用MediaScannerConnection的scanFile方法,就可以強制掃描我們需要更新的檔案路徑,之後媒體資料庫也會同步更新,這樣,就不會出現檔案刪除,在媒體庫中卻能搜尋到的情況了,也能解決這位朋友提出的新增加歌曲的資訊獲取問題了。
 參考