【android】音乐播放器之数据存储总结
? ? ? ??功能介紹如下:?? ?
? ? ? ??1、獲取本地歌曲列表,實現歌曲播放功能。?
? ? ? ? 2、利用jsoup解析網頁數據,從網絡獲取歌曲列表,同時實現歌曲和歌詞下載到手機本地的功能。?
? ? ? ? 3、通知欄提醒,實現仿QQ音樂播放器的通知欄功能.?? ? ?
? ? ???涉及的技術有:?
? ? ? ?1、jsoup解析網絡網頁,從而獲取需要的數據?
? ? ? ?2、android中訪問網絡,獲取文件到本地的網絡請求技術,以及下載文件到本地實現斷點下載?
? ? ? ?3、線程池?
? ? ? ?4、圖片緩存?
? ? ? ?5、service一直在后臺運行?
? ? ? ?6、Activity與Fragment間的切換以及通信?
? ? ? ?7、notification通知欄設計?
? ? ? ?8、自定義廣播?
? ? ? ?9、android系統文件管
? ? ? ?音樂播放器思路及源碼下載見:【android】音樂播放器之設計思路
? ? ? 這篇文章主要來談談android數據存儲以及音樂播放器用到的一些存儲技術和緩存技術。當然小編也還是會順帶擴展下音樂播放器用到的其他方方面面的知識如果可能的話~!~!!!哈哈哈。先來總結寫目前android目前主要的5中數據存儲方式吧。
數據存儲方式
Android 的數據存儲有5種方式:
1. SharedPreferences存儲數據?
SharedPreferences數據存儲,也叫作xml存儲。這是將數據存儲“data/data/程序包名/share_prefs”路徑下的到xml文件中。 ??
2. 文件存儲數據?
分為內部儲存和外部存儲。內部存儲是應用程序使用Android為自己分配的內存空間,數據存儲到“/data/data/程序包名/files”路徑下的相應文件中。外部存儲是使用手機sdcard的內存(這個sdcard并不是我們經常說的那個可以拆卸替換的SD卡,那個SD卡我們稱之為擴展卡),使用這部分內存要聲明相應的權限。?
3. SQLite數據庫存儲數據?
使用數據庫進行存儲,這個一般數據量比較大的時候。 ??
4. 使用ContentProvider存儲數據?
這個比較眼熟,ContentProvider也是Android的四大組件之一。ContentProvider一般是第三方提供的數據存儲方式,向我們手機中的通訊錄聯系人,照片,音樂等……?
5. 網絡存儲數據?
這個是將數據上傳到網絡上進行存儲。?
? ? ?下面就具體的看看每種存儲方式,先看看sharepreferences存儲技術:
SharedPreferences存儲數據
SharedPreferences是一種輕型的數據存儲方式,實際上是基于XML文件存儲的“key-value”鍵值對數據。通常用來存儲程序的一些配置信息。其存儲在“data/data/程序包名/shared_prefs“目錄下。?
?
使用SharedPreferences來存儲數據首相我們要獲得SharedPreferences的對象。
獲得SharedPreferences的對象
獲得SharedPreferences對象的方法有三種:?
(1)通過Context的getSharedPrerences(key, [模式])方法獲取SharedPreferences對象;方法的第一個參數是用于指定SharedPreferences文件的名稱,第二個參數是指定操作模式,主要有兩種模式進行選擇:MODE_PRIVATE,?MODE_MULTI_PROCESS。默認操作是MODE_PRIVATE。除此之外還有MODE_EORLD_READEABLE,MODE_WORLD_WRITEABLE兩種。?
(2)通過Activity類提供的getPrerences(key)方法獲取到SharedPreferences對象;該方法會創建一個以當前活動類名作為SharedPreferences文件名的文件。?
(3)通過PreferencesManager類中的getDefaultPreferernces()方法獲得;這是一個靜態的方法,他接受一個Context參數,并將當前應用程序的包名作為SharedPreferences文件名。
下面我們來看一下如何使用SharedPreferences讀寫數據……
寫數據
步驟:
(1)根據Context的getSharedPrerences(key, [模式])方法獲取SharedPreferences對象;?
(2)利用SharedPreferences的editor()方法獲取Editor對象;?
(3)通過Editor的putXXX()方法,將鍵值對存儲數據;?
(4)通過Editor的commit()方法將數據提交到SharedPreferences內。
讀數據
步驟:
(1)根據Context的getSharedPrerences(key, [模式])方法獲取SharedPreference對象;?
(2)通過SharedPreference對象的getXXX方法獲得數據。例如:getString(String s, String s1)方法有兩個位參數,第一個s為要獲得的值的鍵,第二個s1為如果鍵值不存在返回的默認的String類型的值。
文件存儲數據
文件存儲是Android中最基本的一種存儲方式,他不對存儲的內容進行任何格式化的處理,所有的數據都是原封不動的保存在文件中的,因此他比較適合于存儲一些簡單的二進制數據或文本數據。?
文件存儲也分兩種:內部存儲和外部存儲。?
內部存儲
指Android為應用程序分配的內存。?
通過Context類中封裝好的輸入流和輸出流的獲取方法獲得數據/data/data//files目錄下存儲的數據。
files目錄下寫數據
通過Context的封裝好的方法?openFileOutput(String filename, int mode)獲得數據流:?
? ? ? ?String filename參數:在/data/data//files目錄下存儲時的文件名。?
? ? ? ?int mode參數:文件的操作模式,主要有兩種模式可選擇:MODE_PRIVATE,?MODE_APPEND。默認為MODE_PRIVATE,當指定相同文件名進行讀寫的時候,新的內容會覆蓋原有內容;MODE_APPEND模式會在已存在的文件的最后追加新的內容。(還得多嘮叨嘮叨~!~)除此之外有兩種模式:MODE_WORLD_READABLE,MODE_WORLD_WRITEABLE,這兩種模式表示允許其他應用程序對我們的程序文件進行讀寫操作。
files目錄下讀數據
通過Context的封裝好的方法?openFileInput(String filename)獲得數據流:?
? ? ? ? ? ?String filename參數:在/data/data//files目錄下讀取時的文件名。
外部存儲
? ? ? ? ? 對于多數情況下每個用戶都有自己的努力存儲空間,應用僅對當前用戶的外部存儲空間有訪問權限:WRITE_EXTERNAL_STORAGE為對外部存儲空間的寫權限;READ_EXTERNAL_STORAGE為對外部存儲空間的寫權限;這些都需要在AndroidManifest.fex文件夾中聲明。
? ? ? ? ?READ_EXTERNAL_STORAGE:用于獲取主要的外部存儲目錄,當然這個目錄當前很有可能不能訪問,這就需要通過狀態判斷getExternalStorageState?
? ? ? ? ?getExternalStorageState:可以被用于獲取當前的狀態;
? ? ? ? ?getExternalFileDir:返回的是應用所在的目錄,當應用被卸載后系統清理的就是這個目錄
? ? ? ? ?getExternalCacheDir:用于返回應用外部的緩存目錄
SQLite數據庫存儲數據
? ? ? ?SQLite是一個輕量級關系型數據庫,既然是關系型數據庫,那操作起來其實跟mysql、sql server差不多的。?
? ? ? ?SQLite 和其他數據庫最大的不同就是對數據類型的支持,創建一個表時,可以在 CREATE TABLE 語句中指定某列的數據類型,但是你可以把任何數據類型放入任何列中。當某個值插入數據庫時,SQLite 將檢查它的類型。如果該類型與關聯的列不匹配,則 SQLite 會嘗試將該值轉換成該列的類型。如果不能轉換,則該值將作為其本身具有的類型存儲。比如可以把一個字符串(String)放入 INTEGER 列。SQLite 稱這為“弱類型”(manifest typing.)。?
對數據庫SQLite進行操作,我們要借助于SQLiteOpenHelper類進行操作。對數據庫的操作也就是”增、刪、改、查“。在學習數據庫的操作之前我們首先要學會如何創建數據庫……
創建數據庫
數據庫的操作借助于SQLiteOpenHelper,SQLiteOpenHelper是一個抽象類,我們我們在使用SQLiteOpenHelper時要先創建一個MySQLiteOpenHelper繼承SQLiteOpenHelper類。?
SQLiteOpenHelper有兩個非常重要的方法:getReadableDatabase()方法返回數據庫是一個只讀的;getWriteableDatabase()方法獲得是一個可讀寫的數據庫對象。這里我們使用getWriteableDatabase()方法獲得數據庫Database對象。?
添加數據
使用SQLiteDatabase的insert(String table, String nullColumnHack, ContentValues values)方法插入數據。這個方法包含三個參數:?
我們先列舉一條SQLite中的插入語句:INSERT INTO user (name, passwords)] VALUES ("張三", "123456");?
String table:操作的數據表的名稱。?
String nullColumnHack:用于我們在未指定添加數據的情況下,為數據表中可以添加null值的數據填入null值。一般這個參數我們傳入null。?
ContentValues values:用于傳遞數據,通常我們通過ContentValues 類的對象的putXXX()方法封裝數據,然后將數據添加進數據庫。?
ContentValues 類,類似于java中的Map,以鍵值對的方式保存數據。
刪除數據
使用SQLiteDatabase的delete(String table, String whereClause, String[] whereArgs)方法刪除數據。這個方法包含三個參數:?
我們先列舉一條SQLite中的刪除語句:DELETE FROM user WHERE name="張三"。?
String table:操作的數據表的名稱。?
String whereClause:約束刪除行的條件。相當于SQLite語句中“where name=?“內容。?
String[] whereArgs:與前一個參數對應約束刪除行的條件。相當于”where name=”張三““中的”張三“。?
注意:如果參數String whereClause和參數String[] whereArgs都傳null的話,就是刪除所有行。
修改數據
使用SQLiteDatabase的 update (String table, ContentValues values, String whereClause, String[] whereArgs)方法刪除數據。這個方法包含四個參數:?
我們先列舉一條SQLite中的修改語句:UPDATE user SET name= "李四", passwords= "123" WHERE name="張三"。?
String table:操作的數據表的名稱。?
ContentValues values:用于傳遞數據,通常我們通過ContentValues 類的對象的putXXX()方法封裝數據,然后將數據添加進數據庫。?
String whereClause:約束修改行的條件。相當于SQLite語句中“where name=?“內容。?
String[] whereArgs:與前一個參數對應約束刪除行的條件。相當于”where name=”張三““中的”張三“。
查詢數據
對于”查“操作,SQLiteDatabase提供了多種方法。?
我們先列舉一條SQLite中的修改語句:SELECT passwords="123" FROM user。?
(1)使用SQL語句進行查詢。這里SQLiteDatabase提供了方法:
- rawQuery (String sql, String[] selectionArgs):該方法返回 Cursor類的對象,用于操作查詢的結果。
(2)使用SQLiteDatabase內定方法查詢:
- query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit):這個方法有N多個參數啊……String table是操作的數據表的名稱;String selection是篩選的字段選項;String[] selectionArgs是字段選項對應的值;String groupBy是篩選結果的分組依據;String having是在由groupBy子句創建的分組上設置條件;String orderBy是結果的排序方式,String limit是篩選結果的顯示限制,例如“2, 3”是指從篩選結果的第2個開始顯示3個。
ContentProvider存儲數據
? ? ? ? ContentProvider內容提供器,主要用于在不同應用程序之間實現數據的共享功能。舉例來說,我們開發一個應用程序,我們不可能只使用自己的數據,也會用到其他應用的數據,像手機中的通訊錄聯系人,圖片,音樂等是使用到最多的。我們使用的SharedPreferences,文件存儲以及數據庫SQLite都是從存儲的應用內部的數據,實現不同應用間的數據共享就要使用到ContentProvider。?
ContentProvider使用方法有兩種:一種是使用現有的內容提供器來讀取和操作相應程序中的數據;另一種是創建自己的內容提供器給我們的應用提供外部訪問接口。?
?
這里我們只講解使用已有的內容提供器來讀取和操作相應程序的數據。
ContentResolver的使用
想要訪問內容提供器中的內容我們需要借助ContentResolver類。?
ContentResolver為我們提供了”增“insert(Uri url, ContentValues values),“刪”delete(Uri url, String where, String[] selectionArgs),“改”update(Uri uri, ContentValues values, String where, String[] selectionArgs),“查”query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)方法對內容提供器中的內容進行操作,是不是很眼熟,對啊,和SQLiteDatabase中的增刪改查操作是一樣的,在此我們不再具體描述。
ContentResolver的使用
想要訪問內容提供器中的內容我們需要借助ContentResolver類。?
ContentResolver為我們提供了”增“insert(Uri url, ContentValues values),“刪”delete(Uri url, String where, String[] selectionArgs),“改”update(Uri uri, ContentValues values, String where, String[] selectionArgs),“查”query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)方法對內容提供器中的內容進行操作,是不是很眼熟,對啊,和SQLiteDatabase中的增刪改查操作是一樣的,在此我們不再具體描述。
Uri的使用
在SQLiteDatabase中,對數據庫的操作是通過接受數據庫的表名進行操作的,而在ContentResolver中是不接受表名的,而接受Uri對象。通過Uri指定操作的內容“位置”。?
Uri由兩部分組成:權限和路徑。權限是對于不同的應用程序進行區分的,一般用應用程序的包名;路徑是對于同一應用程序的不同表進行區分的。例如,我們有一個應用程序的包名是“com.example.ontentproviderdemo”,應用程序中有一個表“table”,則Uri為:“content://com.example.ontentproviderdemo/table”。
查詢通訊錄
我們以查詢通訊錄中的內容為例,看一下ContentProvider的使用。通過Android提供的ContentProvider內容提供器訪問通訊錄中的聯系人數據。?
1. 讀取聯系人是需要權限的,首先在AndroidManifext中加入權限
3. 創建一個Uri對象,指定訪問通訊錄。這里我們使用Android已經解析好的Uri即可:ContactsContract.CommonDataKinds.Phone.CONTENT_URI ?
4. 查找聯系人內容使用ContentResolver的query()方法,返回一個Cursor對象。
? ? ? 音樂播放器主要用到了文件存儲技術、sharepreferences存儲技術以及LruCache緩存技術,當然如果你感興趣也可以自己玩玩數據庫啥的~!~
? ? 在PlayService啟動階段,會初始化本地音樂列表(見 【android】音樂播放器之service服務設計)通過MusicUtils訪問外部存儲設備sdcard讀取本地音樂:
public class MusicUtils {// 存放歌曲列表public static ArrayList<Music> sMusicList = new ArrayList<Music>();public static void initMusicList() {// 獲取歌曲列表sMusicList.clear();sMusicList.addAll(LocalMusicUtils.queryMusic(getBaseDir()));}/*** 獲取內存卡根* @return*/public static String getBaseDir() {String dir = null;if (!Environment.getExternalStorageState().equals(Environment.MEDIA_UNMOUNTED)) {dir = Environment.getExternalStorageDirectory() + File.separator;} else {dir = App.sContext.getFilesDir() + File.separator;}return dir;}/*** 獲取應用程序使用的本地目錄* @return*/public static String getAppLocalDir() {String dir = null;if (!Environment.getExternalStorageState().equals(Environment.MEDIA_UNMOUNTED)) {dir = Environment.getExternalStorageDirectory() + File.separator+ "Music" + File.separator;} else {dir = App.sContext.getFilesDir() + File.separator + "Music" + File.separator;}return mkdir(dir);}/*** 獲取音樂存放目錄* @return*/public static String getMusicDir() {String musicDir = getAppLocalDir();return mkdir(musicDir);}/*** 獲取歌詞存放目錄* * @return*/public static String getLrcDir() {String lrcDir = getAppLocalDir();return mkdir(lrcDir);}/*** 創建文件夾* @param dir* @return*/public static String mkdir(String dir) {File f = new File(dir);if (!f.exists()) {for (int i = 0; i < 5; i++) {if(f.mkdirs()) return dir;}return null;}return dir;} /*** 獲取目錄下的歌曲* @param dirName*/public static ArrayList<Music> queryMusic(String dirName) {ArrayList<Music> results = new ArrayList<Music>();Cursor cursor = App.sContext.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null,MediaStore.Audio.Media.DATA + " like ?",new String[] { dirName + "Music/" + "%" },MediaStore.Audio.Media.DEFAULT_SORT_ORDER);if(cursor == null) return results;// id title singer data time imageMusic music;for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {// 如果不是音樂String isMusic = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.IS_MUSIC));if (isMusic != null && isMusic.equals("")) continue;String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));if(isRepeat(title, artist)) continue;music = new Music();music.setId(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)));music.setTitle(title.toString());music.setArtist(artist.toString());music.setUri(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)));music.setLength(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)));music.setImage(getAlbumImage(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID))));results.add(music);}cursor.close();return results;}? ? ?代碼非常的簡單,逐一的訪問sdcard讀取.mp3/.lrc文件,解析出歌名、著作填充到適配器中去。對于SharePreferences,我們知識將一些共享數據存儲在其中,比如每次播放歌曲在本地列表中的position等,部分代碼如下:
/*** 播放* @param position 音樂列表的位置* @return 當前播放的位置*/public int play(int position) {if(position < 0) position = 0;if(position >= MusicUtils.sMusicList.size()) position = MusicUtils.sMusicList.size() - 1;try {mPlayer.reset();mPlayer.setDataSource(MusicUtils.sMusicList.get(position).getUri());mPlayer.prepare();start();if(mListener != null) mListener.onChange(position);} catch (Exception e) {e.printStackTrace();}mPlayingPosition = position;SpUtils.put(Constants.PLAY_POS, mPlayingPosition);if(!readyNotification){startNotification();}else{setRemoteViews();}return mPlayingPosition;} public class SpUtils {public static void put(final String key, final Object value) {SharedPreferences sp = App.sContext.getSharedPreferences(Constants.SP_NAME, Context.MODE_PRIVATE);SharedPreferences.Editor editor = sp.edit();if(value instanceof Integer) {editor.putInt(key, (Integer) value);}else if(value instanceof Float) {editor.putFloat(key, (Float) value);}else if(value instanceof Boolean) {editor.putBoolean(key, (Boolean) value);}else if(value instanceof Long) {editor.putLong(key, (Long) value);}else {editor.putString(key, (String) value);}editor.commit();}public static Object get(Context context, String key, Object defaultObject) {SharedPreferences sp = App.sContext.getSharedPreferences(Constants.SP_NAME,Context.MODE_PRIVATE);if (defaultObject instanceof String) {return sp.getString(key, (String) defaultObject);} else if (defaultObject instanceof Integer) {return sp.getInt(key, (Integer) defaultObject);} else if (defaultObject instanceof Boolean) {return sp.getBoolean(key, (Boolean) defaultObject);} else if (defaultObject instanceof Float) {return sp.getFloat(key, (Float) defaultObject);} else if (defaultObject instanceof Long) {return sp.getLong(key, (Long) defaultObject);}return defaultObject;}/*** 移除某個key值已經對應的值* @param context* @param key*/public static void remove(Context context, String key) {SharedPreferences sp = context.getSharedPreferences(Constants.SP_NAME,Context.MODE_PRIVATE);sp.edit().remove(key).commit();}/*** 清除所有數據* @param context*/public static void clear(Context context) {SharedPreferences sp = context.getSharedPreferences(Constants.SP_NAME,Context.MODE_PRIVATE);sp.edit().clear().commit();} }? ? ?讀取本地音樂時,其中圖片顯示部分用到了圖片緩存技術MusicIconLoader類的實現,在MusicIconLoader類中除調用 BitmapFactory提供的方法對圖片大小進行處理外(見Android高效加載大圖、多圖解決方案,有效避免程序OOM)還使用了LruCache緩存技術,上面這篇文章講的很清楚,就直接看代碼吧:
? ? ?
public class MusicIconLoader { private static MusicIconLoader sInstance;private LruCache<String, Bitmap> mCache;// 獲取MusicIconLoader的實例public synchronized static MusicIconLoader getInstance() {if(sInstance == null) sInstance = new MusicIconLoader();return sInstance;}// 構造方法, 初始化LruCacheprivate MusicIconLoader() {int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);mCache = new LruCache<String, Bitmap>(maxSize) {protected int sizeOf(String key, Bitmap value) { // return value.getByteCount();return value.getRowBytes() * value.getHeight();}};}// 根據路徑獲取圖片public Bitmap load(final String uri) {if(uri == null) return null;final String key = Encrypt.md5(uri);Bitmap bmp = getFromCache(key);if(bmp != null) return bmp;bmp = BitmapFactory.decodeFile(uri);addToCache(key, bmp);return bmp;}// 從內存中獲取圖片private Bitmap getFromCache(final String key) {return mCache.get(key);}// 將圖片緩存到內存中private void addToCache(final String key, final Bitmap bmp) {if(getFromCache(key) == null && key != null && bmp != null) mCache.put(key, bmp);} }
總結
以上是生活随笔為你收集整理的【android】音乐播放器之数据存储总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BeanShell遍历JSON双重嵌入数
- 下一篇: 举手无措......该怎么给视频配上好听