5.1.4 用于音频的MediaStore

    本书前面讨论了如何将MediaStore用于图像。我们所学过的大多数只是都可用来存储和检索其他类型的媒体,包括音频。为了提供可靠的机制用于浏览和搜索音频,Android包括一个MediaStore.Audio程序包,其中定义了标准的内容提供器。

    1.通过MediaStore访问音频

    使用MediaStore提供器访问存储的音频文件与之前使用MediaStore的方式相似。在当前情况下,将使用android.provider.MediaStore.Audio程序包。

    演示活动将MediaStore用于音频的最简单方式是给出一个示例应用程序。以下代码创建一个活动,其查询MediaStore以获得任何音频文件,并简单的播放返回的第一个文件。

 1 package com.nthm.androidtest;
 2 
 3 import java.io.File;
 4 import android.app.Activity;
 5 import android.content.Intent;
 6 import android.database.Cursor;
 7 import android.net.Uri;
 8 import android.os.Bundle;
 9 import android.os.Environment;
10 import android.provider.MediaStore;
11 import android.view.View;
12 import android.view.View.OnClickListener;
13 import android.widget.Button;
14 
15 public class AudioPlayer extends Activity implements OnClickListener {
16     private Button playButton;
17     private String []columns={MediaStore.Audio.Media.DATA,
18             MediaStore.Audio.Media._ID,
19             MediaStore.Audio.Media.TITLE,
20             MediaStore.Audio.Media.DISPLAY_NAME,
21             MediaStore.Audio.Media.MIME_TYPE,
22             MediaStore.Audio.Media.ARTIST,
23             MediaStore.Audio.Media.ALBUM,
24             MediaStore.Audio.Media.IS_RINGTONE,
25             MediaStore.Audio.Media.IS_ALARM,
26             MediaStore.Audio.Media.IS_MUSIC,
27             MediaStore.Audio.Media.IS_NOTIFICATION};
28     @Override
29     protected void onCreate(Bundle savedInstanceState) {
30         super.onCreate(savedInstanceState);
31         setContentView(R.layout.audioplayer);

     为了使用MediaStore,需要指定想要返回的数据。可以通过使用在android.provider.MediaStore.Audio.Media类中定义的常量创建一个字符串数组来实现该操作。这些常量都是保存在MediaStore中以用于音频的标准字段。

    在当前情况下,正在请求DATA列,其中包含了实际的音频文件路径。还将请求内部ID、标题(Title)、显示名称(Display Name)、MIME类型(MIME-Type)、艺术家(Artist)、唱片集(Album)以及它是哪种类型的音频文件(包括警报、音乐、铃声或通知类型)。

   其他列(如添加时间(DATA_ADDED)、修改日期(DATE_MODIFIED)、文件大小(SIZE)等)也同样可用。

   通过调用活动中的managedQuery方法来查询MediaStore。managedQuery方法接受内容提供器的Uri作为参数。在当前情况下,该内容提供器是音频MediaStore,对应的Uri是android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI。这个Uri指定我们想要存储在SD卡上的音频。如果想要存储在内存中的音频文件,那么将使用android.provider.MediaStore.Audio.Media.INXTERNAL_CONTENT_URI。

   除了指向MediaStore的Uri,managedQuery方法还接受想返回的列数组,一条SQL WHERE子句,用于WHERE子句的值以及一条SQL ORDER BY子句。

   这个示例不会使用WHERE和ORDER BY子句,所以对于这些参数会传入null。

1    Cursor cursor=managedQuery(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, columns, null, null, null);

     managedQuery方法返回一个Cursor对象。Cursor类允许我们与从数据库查询返回的数据集交互。

     要做的第一件事实创建几个变量,以保存一些想要从结果中访问的列的编号。这不是绝对必须的,但是获得索引将会非常的方便,从而不必在每次需要他们的时候调用Cursor对象上的方法。获得他们的方法是将想要的类的常量值传递给Cursor对象上的getColumnIndex方法。

1 int fileColumn=cursor.getColumnIndex(MediaStore.Audio.Media.DATA);

   第一个索引是包含指向实际音频文件的路径的列索引。通过传入表示该列的常量android.provider.MediaStore.Audio.Media.DATA,可以获得上述索引。

   接下来将获得几个其他的索引,他们并非都是实际正在使用的列,这里仅仅是为了演示目的而获得他们。

1         int titleColumn=cursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
2         int displayColumn=cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME);
3         int mimeTypeColumn=cursor.getColumnIndex(MediaStore.Audio.Media.MIME_TYPE);

   由MediaStore返回的数据在Cursor对象中可用,且通过行以及列的方式组织起来。通过调用moveToFirst方法和检索它的结果,可以获得返回的第一个结果。如果没有返回任何行,那么该方法将返回一个布尔值false,所以可以将它包含在一条if语句中以确保存在数据。

1 if(cursor.moveToFirst()){

    为了获得实际的数据,可以调用Cursor上的“get”方法之一,并传入希望检索的类索引。如果数据预期是一个字符串,那么就调用getStirng。如果数据预期是一个整数,那么调用getInt。对于所有的基本数据类型,都有一个合适的“get”方法。

1             String audioFilePath=cursor.getString(fileColumn);
2             String mimeType=cursor.getString(mimeTypeColumn);

    一旦获得了文件的路径和MIME类型,就可以使用它们构造意图,以启动内置的音频播放器的应用程序,并播放文件(或者可以使用之前演示的MediaPlayer类直接播放音频文件)。为了将音频文件的路径转换成可以传递到意图的Uri,可以构造一个File对象并用Uri.fromFile方法来获取Uri。还有其他方法可用来完成同样的事情,但这可能是最简单的方法。

1             Intent intent=new Intent(android.content.Intent.ACTION_VIEW);
2             File newFile=new File(audioFilePath);
3             intent.setDataAndType(Uri.fromFile(newFile), mimeType);
4             startActivity(intent);
5         }
6        }
7     }

    以上就完成了如何将MediaStore用于音频的基本说明。

   现在让我们更深入一步,创建一个应用程序,从中可以缩小返回的结果,并对他们进行浏览,从而允许用户选择音频文件进行播放。

   2.浏览MediaStore中的音频

   音频文件(特别是音乐文件)可以按照唱片集、艺术家和流派(Genre)来查找,也可以直接在MediaStore中查找。每个音频文件都有一个Uri,可以使用managedQuery来搜索。

   唱片集:android.provider.MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI

   艺术家:android.provider.MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI

   流派:android.provider.MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI

   一下代码演示了如何使用唱片集Uri来查询设备上的所有唱片集:

1         String [] columns={android.provider.MediaStore.Audio.Albums._ID,
2                 android.provider.MediaStore.Audio.Albums.ALBUM};
3         Cursor cursor=managedQuery(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, columns, null, null, null);
4         if(cursor!=null){
5             while(cursor.moveToNext()){
6                 cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM));
7             }
8         }

    在上述代码中,可以看到其请MediaStore返回_IDALBUM列。ALBUM常量表明我们希望返回该唱片集的名称。其他可用的列在android.provider.MediaStore.Audio.Albums类中列出,且都继承自android.provider.BaseColumns和android.provider.MediaStore.Audio.AlbumColumns。

   仅仅给定Uri和列的类表来调用managedQuery方法,而使其他的参数为null。这将返回设备上所有可用的唱片集。

   最后输出唱片集列表。为了对位于Cursor对象内部的返回列表进行遍历,首先检查该Cursor对象是否包含结果(cursor!=null),然后使用cursor.moveToNext()方法。

   3.唱片集浏览应用程序示例

   下面是一个示例,其使用上述内容作为一个起点,允许用户查看所有唱片集的名称。用户可以指定他或她想查看哪个唱片集上的歌曲。然后,它将列出歌曲列表,如果用户选择了其中一首,那么它将播放这首歌曲。

 1 package com.nthm.androidtest;
 2 
 3 import java.io.File;
 4 import android.app.ListActivity;
 5 import android.content.Intent;
 6 import android.database.Cursor;
 7 import android.net.Uri;
 8 import android.os.Bundle;
 9 import android.provider.MediaStore;
10 import android.view.View;
11 import android.widget.ListView;
12 import android.widget.SimpleCursorAdapter;

     没有扩展一个通用的活动,我们将扩展ListActivity,从而能够展现和管理一个基本的ListView对象。

1 public class AudioBrowser extends ListActivity {
2     private Cursor cursor;

     接下来将创建几个常量,他们将帮助跟踪用户在应用程序中的位置,而且在用户执行动作时做出适当的响应。以下代码将在currentState变量中跟踪用户,该变量起始位置设置为STATE_SELECT_ALBUM。

1     public static int STATE_SELECT_ALBUM=0;
2     public static int STATE_SELECT_SONG=1;
3     private int currentState=STATE_SELECT_ALBUM;

    就像普通的活动一样,其中有一个onCreate方法用来执行初始命令。

1     @Override
2     protected void onCreate(Bundle savedInstanceState) {
3         super.onCreate(savedInstanceState);
4         setContentView(R.layout.audiobrowser);

    在设置完布局之后(通过audiobrowser.xml布局XML文件),创建一个字符串数组,标志当运行查询时将从MediaStore返回的列。在这种情况下,它与上述代码片段相同——我们想要返回_ID和唱片集的名称,即ALBUM。他们都是在MediaStore.Audio.Albums类中列出的常量。

1         String[] columns={android.provider.MediaStore.Audio.Albums._ID,
2                           android.provider.MediaStore.Audio.Albums.ALBUM};

    仅仅用表示唱片集的Uri以及列的列表来调用managedQuery方法,其他参数都设为null,这应该会返回一个所有可用唱片集的列表。

1         cursor=managedQuery(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, columns, null, null, null);

    一旦这么操作,就会返回一个包含查询结果的Cursor对象。

    由于使用了ListActivity,因此能够使它自动管理数据列表。可以使用setListAdapter犯法将Cursor对象绑定到ListView对象。

    首先创建一个字符串数组,他们表示想要显示的Cursor对象中的列名。在当前的情况下,我们只想要唱片集、的名称。即MediaStore.Audio.Albums.ALBUM使对应的常量。

    接下来,列出将用来显示来自这些列的数据的View对象。由于现在只有一列,因此,只需要一个View对象,即android.R.id.text1。这个View对象是可用的,因为他是下一步将要使用的android.R.layout.simple_list_item_1布局的一部分。

   最后调用setListAdapter方法,并传入一个内部创建的SimpleCursorAdapter。SimpleCursorAdapter是一个简单的适配器,它将Cursor对象包含的数据转换给ListActivity。在创建SimpleCursorAdapter时,传入作为上下文的活动(this)、一个已经定义的标准ListView布局(android.R.layout.simple_list_item_1)、包含数据的Cursor对象以及刚刚定义的两个数组。

1         String []displayFields=new String[]{MediaStore.Audio.Albums.ALBUM};
2         int [] displayerViews=new int[]{android.R.id.text1};
3         setListAdapter(new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, cursor, displayFields, displayerViews));
4     }

    如果运行这段代码,将获得设备上可用的唱片集的一个简单的类表。不过我们打算更深一步,允许用户选择一个唱片集。

    为了允许用户实际的选择一个唱片集,需要重写由父类ListActivity提供的默认方法onListItemClick。

1     @Override
2     protected void onListItemClick(ListView l, View v, int position, long id) {
3         super.onListItemClick(l, v, position, id);
4         if(currentState==STATE_SELECT_ALBUM){

    当选择类表中的的唱片集时将调用该方法。由于currentState变量开始时是STATE_SELECT_ALBUM,因此第一次调用该方法时,它应该为true。

    传入在列表中选定的唱片集位置,同时Cursor对象将利用该位置,通过调用moveToPosition方法获知它是哪个唱片集。

1            if(cursor.moveToPosition(position)){

    假定moveToPosition方法成功返回,我们将重新开始查询MediaStore。然而,由于这一次想要访问单个媒体文件,因此是在MediaStore.Audio.Media.EXTERNAL_CONTENT_URI上运行managedQuery方法。

    首先选择想要返回的列。

1                 String [] columns={MediaStore.Audio.Media.DATA,
2                         MediaStore.Audio.Media._ID,
3                         MediaStore.Audio.Media.TITLE,
4                         MediaStore.Audio.Media.DISPLAY_NAME,
5                         MediaStore.Audio.Media.MIME_TYPE};

    接下来需要为查询构造SQL WHERE 子句。因为我们只想选择属于特定唱片集的媒体文件,所以应在WHERE子句中指明这一点。

    普通SQL中的WHERE子句如下表示:

    WHERE album=‘album name’

    由于正在使用managedQuery,因此不需要WHERE关键字,同时也不需要传入它的等价物。相反,我们采用一个“?”符号进行替代。因此对于上述版本,该字符串将如下所示:

    album=?

    由于正在使用常量,并不知道列的实际名称,因此将使用该常量来构造WHERE子句。

1          String where=android.provider.MediaStore.Audio.Media.ALBUM+"=?";

为了完成WHERE子句,需要数据来替换WHERE子句中的“?”符号。这将是一个字符串数组,其中的每个字符串对应一个使用的“?”符号。在当前情况下,我们需要使用选择的唱片集名称。因为Cursor对象位于正确的位置,所以只需要对正确的列调用Cursor对象上的getString方法,可以通过对列名调用Cursor对象上的getColumnIndex方法来获得正确的列。

1         String whereVal[]={cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM))};

    然后可以指定我们希望以特定列的值对结果进行排序。为此,需要创建一个变量,其包含了希望结果按其顺序排列的列名。

1        String orderBy=android.provider.MediaStore.Audio.Media.TITLE;

    最后,运行managedQuery方法,传入Uri、列、WHERE子句的变量、WHERE子句的数据以及ORDER BY 子句的变量。

1        cursor=managedQuery(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, columns, where, whereVal, orderBy);

    同样,将使用ListActivity的各种方法来管理Cursor对象和展示列表中的结果。

1                 String []displayFields=new String[]{MediaStore.Audio.Media.DISPLAY_NAME};
2                 int [] displayerViews=new int[]{android.R.id.text1};
3                 setListAdapter(new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, cursor, displayFields, displayerViews));

    最后要做的事情是将currentState变量改为STATE_SELECT_SONG,从而当下一次调用该方法时,我们将跳过所有这些步骤,因为用户将选定一首歌而不是唱片集。

1                currentState=STATE_SELECT_SONG;
2             }
3         }else if(currentState==STATE_SELECT_SONG){

    当用户选择唱片集并从列表中选定一首歌时,他或她将进入该方法的这部分,因为currentState将等于STATE_SELECT_SONG

1               if(cursor.moveToPosition(position)){

    正如前面所做的那样,调用Cursor对象上相同的moveToPosition方法,我们可以获得实际选择的这首歌。在这种情况下,我们会获得包含文件路径的列以及该文件的MIME类型。我们将它转换成一个File对象,并创建一个意图以启动内置的音乐播放器应用程序。

 1                 int fileColumn=cursor.getColumnIndex(MediaStore.Audio.Media.DATA);
 2                 int mimeTypeColumn=cursor.getColumnIndex(MediaStore.Audio.Media.MIME_TYPE);
 3                 String audioFilePath=cursor.getString(fileColumn);
 4                 String mimeType=cursor.getString(mimeTypeColumn);
 5                 Intent intent=new Intent(android.content.Intent.ACTION_VIEW);
 6                 File file=new File(audioFilePath);
 7                 intent.setDataAndType(Uri.fromFile(file), mimeType);
 8                 startActivity(intent);
 9             }
10         }
11     }
12 }

    下面是用于上述代码的布局XML文件,你将注意到它包含了一个ID为list的ListView对象。这是用于我们正在扩展的ListActivity的默认的ID。

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     android:layout_width="match_parent"
 3     android:layout_height="match_parent"
 4     android:orientation="vertical"
 5     >
 6  <ListView 
 7      android:id="@+android:id/list"
 8      android:layout_width="wrap_content"
 9      android:layout_height="wrap_content"></ListView>
10 </LinearLayout>

     现在就完成了该示例应用程序,它使得我们能够通过使用MediaStore浏览唱片集,并选择歌曲进行播放。以可以使用非常类似的方法来构建应用程序,从而能够基于艺术家和流派来浏览和选择音乐。

posted on 2014-08-27 16:59  宁静致远,一览众山小  阅读(908)  评论(0编辑  收藏  举报

导航