对于Android的学习,需要掌握的东西有点多,需要我们认真,小心,不断的进取。前天突然有个想法,觉得Mp3播放器是一个可以练习的项目,于是在网上搜了下,发现有人已经写了博客,看了他们的博客后,我觉得他们说的一点很对,Mp3播放器基本用到了Android里面的许多知识点,做完这个过后,可能对于Android整个架构有了一定了解,我于是也想尝试下,于是准备边做,编写博客,来记录自己开发的过程,这个也许叫作项目开发日志吧。

第一个我的想法是先做:本地音乐播放器。
于是我用了个粗浅的方法来加载mp3文件,用Listview控件来显示所有的本地音乐。

实现的效果如下:
主界面:

左边显示的是音乐的ID,上面是文件名,下面是歌手,右边是个点击按钮,但是这个按钮的功能现在还没做。
播放界面如下:

实现的功能:点击主界面的歌,进入播放界面播放。
后退回来,再次点击主界面的歌,进入播放界面重新播放。
上一首和下一首的播放功能还没做。这个需要获得mp3的路径,目前方法不是很好,感觉比较挫,等想到新方法,在来考虑这个功能。

其实最为重要的是:获得mp3的信息和播放service的实现,这个最为重要。

mp3信息类:

public class Mp3Info {        private String  name;        private long ID;        private String title;//音乐标题        private  String artist;//艺术家        private long duration;//时长        private long size;  //文件大小        private String url; //文件路径        public String getName()        {                return this.name;        }        public void setName(String name)        {                this.name =name;        }        public String getTitle()        {                return this.title;        }        public void setTitle(String title)        {                this.title =title;        }        public void setArtist(String artist)        {                this.artist     =artist;        }        public String getArtist(){                return this.artist;        }        public String getUrl(){                return this.url;        }        public void setUrl(String url)        {                this.url =url;        }        public void setID(long id)        {                this.ID =id;        }        public long getID(){                return this.ID;        }        public long getDuration(){                return this.duration;        }        public long getSize()        {                return this.size;        }        public  void setDuration(long duration){                this.duration   =duration;        }        public void setSize(long size){                this.size       = size;        }}

获得mp3的信息函数

     public List getMp3Infos() {                Cursor cursor = getContentResolver().query(                        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,                        MediaStore.Audio.Media.DEFAULT_SORT_ORDER);                List mp3Infos = new ArrayList();                for (int i = 0; i < cursor.getCount(); i++) {                        Mp3Info mp3Info = new Mp3Info();                        cursor.moveToNext();                        long id = cursor.getLong(cursor                                .getColumnIndex(MediaStore.Audio.Media._ID));   //音乐id                        String title = cursor.getString((cursor                                .getColumnIndex(MediaStore.Audio.Media.TITLE)));//音乐标题                        String artist = cursor.getString(cursor                                .getColumnIndex(MediaStore.Audio.Media.ARTIST));//艺术家                        long duration = cursor.getLong(cursor                                .getColumnIndex(MediaStore.Audio.Media.DURATION));//时长                        long size = cursor.getLong(cursor                                .getColumnIndex(MediaStore.Audio.Media.SIZE));  //文件大小                        String url = cursor.getString(cursor                                .getColumnIndex(MediaStore.Audio.Media.DATA));              //文件路径                        int isMusic = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.IS_MUSIC));//是否为音乐                        if (isMusic != 0) {     //只把音乐添加到集合当中                                mp3Info.setID(id);                                mp3Info.setTitle(title);                                mp3Info.setArtist(artist);                                mp3Info.setDuration(duration);                                mp3Info.setSize(size);                                mp3Info.setUrl(url);                                mp3Infos.add(mp3Info);                        }                }                return mp3Infos;        }

这是调用文件数据库查找音频,并将文件信息保存在结构体里面。

接下来介绍建立MusicService.java

       public class MusicService extends Service {        // mp3的绝对路径。            String path;        //一个Binder用来和Activity来交互        class MyBinder extends Binder {                public Service getService(){                        return MusicService.this;                }        }        @Override        //每次程序执行的时候需要调用的函数        public int onStartCommand(Intent intent, int flags, int startId) {                path    =intent.getStringExtra("url");                init();                if(mediaPlayer.isPlaying()) {                        pause();                }                return super.onStartCommand(intent, flags, startId);        }        IBinder musicBinder  = new MyBinder();        //播放音乐的媒体类        MediaPlayer mediaPlayer;        private String TAG = "MyService";        //第一次创建执行,或者service结束在开启的时候执行        @Override        public void onCreate() {                super.onCreate();                Log.d(TAG, "onCreate() executed");        }        //必须重载的方法        @Override        public IBinder onBind(Intent arg0)        {                // TODO Auto-generated method stub                //当绑定后,返回一个musicBinder                return musicBinder;        }        //初始化音乐播放        void init(){                //进入Idle                mediaPlayer = new MediaPlayer();                try {                        //初始化                        mediaPlayer.setDataSource(path);                        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);                        // prepare 通过异步的方式装载媒体资源                        mediaPlayer.prepareAsync();                } catch (Exception e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                }        }        //返回当前的播放进度,是double类型,即播放的百分比        public double getProgress(){                int position = mediaPlayer.getCurrentPosition();                int time = mediaPlayer.getDuration();                double progress = (double)position / (double)time;                return progress;        }        //通过activity调节播放进度        public void setProgress(int max , int dest){                int time = mediaPlayer.getDuration();                mediaPlayer.seekTo(time*dest/max);        }        //测试播放音乐        public void play(){                if(mediaPlayer != null){                        mediaPlayer.start();                }        }        //暂停音乐        public void pause() {                if (mediaPlayer != null && mediaPlayer.isPlaying()) {                        mediaPlayer.pause();                }        }        //service 销毁时,停止播放音乐,释放资源        @Override        public void onDestroy() {                // 在activity结束的时候回收资源                if (mediaPlayer != null && mediaPlayer.isPlaying()) {                        mediaPlayer.stop();                        mediaPlayer.release();                        mediaPlayer = null;                }                super.onDestroy();        }}

接下来就是如何和activity交互,通过bindService函数来在activity里面绑定个服务。需要用到ServiceConnection函数。
如何通过处理来获得音乐播放的进度,并将数据从service返回到activity里面呢?android里面通过Handler来处理,通过重写handleMessage方法来得到service里面返回来的信息。在主线程里面,我们的等待时间不能超过5秒,否则就会出现UI无法刷新问题,我们这里用到了SeekBar这个进度条,所以这个UI更新的问题需要放在另外一个线程里面,这个时候需要重写Runnable接口。

具体看代码:MusicActivity.java

public class MusicActivity extends Activity {        private ImageView MusicPlay;        private ImageView MusicNext;        private ImageView MusicPrevious;        Boolean mBound = false;        //记录鼠标点击了几次        boolean flag =false;        MusicService mService;        SeekBar seekBar;        //多线程,后台更新UI        Thread myThread;        //控制后台线程退出        boolean playStatus = true;        //处理进度条更新        Handler mHandler = new Handler() {                @Override                public void handleMessage(Message msg) {                        switch (msg.what) {                                case 0:                                        //从bundle中获取进度,是double类型,播放的百分比                                        double progress = msg.getData().getDouble("progress");                                        //根据播放百分比,计算seekbar的实际位置                                        int max = seekBar.getMax();                                        int position = (int) (max * progress);                                        //设置seekbar的实际位置                                        seekBar.setProgress(position);                                        break;                                default:                                        break;                        }                }        };        @Override        public void onCreate(Bundle savedInstanceState) {                super.onCreate(savedInstanceState);                setContentView(R.layout.musicplay);                MusicPlay = (ImageView) findViewById(R.id.Musicplay);                MusicNext = (ImageView) findViewById(R.id.musicnext);                MusicPrevious = (ImageView) findViewById(R.id.musicprevious);                //定义一个新线程,用来发送消息,通知更新UI                myThread = new Thread(new UpdateProgress());                //绑定service;                Intent serviceIntent = new Intent(MusicActivity.this, MusicService.class);                //如果未绑定,则进行绑定,第三个参数是一个标志,它表明绑定中的操作.它一般应是BIND_AUTO_CREATE,这样就会在service不存在时创建一个                if (!mBound) {                        bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);                }                seekBar = (SeekBar) findViewById(R.id.MusicProgress);                seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {                        @Override                        public void onStopTrackingTouch(SeekBar seekBar) {                                //手动调节进度                                // TODO Auto-generated method stub                                //seekbar的拖动位置                                int dest = seekBar.getProgress();                                //seekbar的最大值                                int max = seekBar.getMax();                                //调用service调节播放进度                                mService.setProgress(max, dest);                        }                        @Override                        public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {                                // TODO Auto-generated method stub                        }                        @Override                        public void onStartTrackingTouch(SeekBar arg0) {                                // TODO Auto-generated method stub                        }                });                MusicPlay.setOnClickListener(new View.OnClickListener() {                        @Override                        public void onClick(View arg0) {                                if (mBound&&flag) {                                        MusicPlay.setImageDrawable(getResources().getDrawable(R.drawable.musicpause));                                        mService.pause();                                        flag =false;                                }else{                                        MusicPlay.setImageDrawable(getResources().getDrawable(R.drawable.musicplay));                                        mService.play();                                        flag =true;                                }                        }                });        }        //实现runnable接口,多线程实时更新进度条        public class UpdateProgress implements Runnable {                //通知UI更新的消息                //用来向UI线程传递进度的值                Bundle data = new Bundle();                //更新UI间隔时间                int milliseconds = 100;                double progress;                @Override                public void run() {                        // TODO Auto-generated method stub                        //用来标识是否还在播放状态,用来控制线程退出                        while (playStatus) {                                try {                                        //绑定成功才能开始更新UI                                        if (mBound) {                                                //发送消息,要求更新UI                                                Message msg = new Message();                                                data.clear();                                                progress = mService.getProgress();                                                msg.what = 0;                                                data.putDouble("progress", progress);                                                msg.setData(data);                                                mHandler.sendMessage(msg);                                        }                                        Thread.sleep(milliseconds);                                        //Thread.currentThread().sleep(milliseconds);                                        //每隔100ms更新一次UI                                } catch (InterruptedException e) {                                        // TODO Auto-generated catch block                                        e.printStackTrace();                                }                        }                }        }        /**         * Defines callbacks for service binding, passed to bindService()         */        private ServiceConnection mConnection = new ServiceConnection() {                @Override                public void onServiceConnected(ComponentName className, IBinder binder) {                         // We've bound to LocalService, cast the IBinder and get LocalService instance                        MusicService.MyBinder myBinder = (MusicService.MyBinder) binder;                        //获取service                        mService = (MusicService) myBinder.getService();                        //绑定成功                        mBound = true;                        //开启线程,更新UI                        myThread.start();                        MusicPlay.setImageDrawable(getResources().getDrawable(R.drawable.musicplay));                        mService.play();                        flag =true;                }                @Override                public void onServiceDisconnected(ComponentName arg0) {                        mBound = false;                }        };        public boolean onCreateOptionsMenu(Menu menu){                // Inflate the menu; this adds items to the action bar if it is present.                //      getMenuInflater().inflate(R.menu.main, menu);                return true;        }        public void onDestroy() {                //销毁activity时,要记得销毁线程                playStatus = false;                super.onDestroy();        }}

基本难点都介绍完了,现在看下MainActivity.java代码:

public class MainActivity extends Activity implements AdapterView.OnItemClickListener {        //Music的listview控件        private ListView MusicList;        // 存储数据的数组列表        ArrayList> MusiclistData = new ArrayList>();        // 适配器        private SimpleAdapter MusicListAdapter;        List mp3Infos;        @Override        public void onCreate(Bundle savedInstanceState) {                super.onCreate(savedInstanceState);                setContentView(R.layout.activity_main);                MusicList = (ListView) findViewById(R.id.listviewmusic);                mp3Infos = getMp3Infos();                GetData(mp3Infos);                MusicListAdapter = new SimpleAdapter(                        this,                        MusiclistData,                        R.layout.listmusic,                        new String[]{"ID", "Title", "Artist", "Icon"},                        new int[]{R.id.MusicID, R.id.Musictitle, R.id.MusicArtist, R.id.MusicIcon}                );                //赋予数据                MusicList.setAdapter(MusicListAdapter);                MusicList.setOnItemClickListener(MusiclistListen);        }        AdapterView.OnItemClickListener MusiclistListen = new AdapterView.OnItemClickListener() {                @Override                public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {                         //Toast.makeText(MainActivity.this, String.valueOf(l), Toast.LENGTH_SHORT).show();                        //判断当前服务是否已经开启                        if(isServiceRunning(getBaseContext(),"com.flashmusic.MusicService")){                                stopService(new Intent(MainActivity.this, MusicService.class));                        }                        Intent intent   = new Intent();                        intent.putExtra("url", mp3Infos.get(i).getUrl());                        intent.setClass(MainActivity.this, MusicService.class);                        //启动服务                        startService(intent);                        //启动音乐播放界面                        startActivity(new Intent(MainActivity.this,MusicActivity.class));                }        };        public static boolean isServiceRunning(Context mContext,String className) {                boolean isRunning = false;                ActivityManager activityManager = (ActivityManager)                        mContext.getSystemService(Context.ACTIVITY_SERVICE);                List serviceList= activityManager.getRunningServices(50);                if (!(serviceList.size()>0)) {                        return false;                }                for (int i=0; iif (serviceList.get(i).service.getClassName().equals(className) == true) {                                isRunning = true;                                break;                        }                }                return isRunning;        }        public void GetData(List mp3Infos) {                for (int i = 0; i < mp3Infos.size(); i++) {                        HashMap map = new HashMap();                        map.put("ID", i + 1);                        map.put("Title", mp3Infos.get(i).getTitle());                        map.put("Artist", mp3Infos.get(i).getArtist());                        map.put("Icon", R.drawable.musicicon);                        MusiclistData.add(map);                }        }        public List getMp3Infos() {                Cursor cursor = getContentResolver().query(                        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,                        MediaStore.Audio.Media.DEFAULT_SORT_ORDER);                List mp3Infos = new ArrayList();                for (int i = 0; i < cursor.getCount(); i++) {                        Mp3Info mp3Info = new Mp3Info();                        cursor.moveToNext();                        long id = cursor.getLong(cursor                                .getColumnIndex(MediaStore.Audio.Media._ID));   //音乐id                        String title = cursor.getString((cursor                                .getColumnIndex(MediaStore.Audio.Media.TITLE)));//音乐标题                        String artist = cursor.getString(cursor                                .getColumnIndex(MediaStore.Audio.Media.ARTIST));//艺术家                        long duration = cursor.getLong(cursor                                .getColumnIndex(MediaStore.Audio.Media.DURATION));//时长                        long size = cursor.getLong(cursor                                .getColumnIndex(MediaStore.Audio.Media.SIZE));  //文件大小                        String url = cursor.getString(cursor                                .getColumnIndex(MediaStore.Audio.Media.DATA));              //文件路径                        int isMusic = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.IS_MUSIC));//是否为音乐                        if (isMusic != 0) {     //只把音乐添加到集合当中                                mp3Info.setID(id);                                mp3Info.setTitle(title);                                mp3Info.setArtist(artist);                                mp3Info.setDuration(duration);                                mp3Info.setSize(size);                                mp3Info.setUrl(url);                                mp3Infos.add(mp3Info);                        }                }                return mp3Infos;        }        @Override        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {        }}

使用SimpleAdapter来加载数据,可以更好的定义子布局文件。

接下来布局文件就不介绍了,只贴下代码:
activity_main.xml,定义ListView控件

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:layout_width="match_parent"              android:layout_height="match_parent"              android:orientation="vertical"><ListView    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:id="@+id/listviewmusic">ListView>LinearLayout>

播放界面布局:musicplay.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:layout_width="fill_parent"              android:layout_height="fill_parent"              android:orientation="horizontal"                 android:background="#d8e4f3"><LinearLayout    android:layout_width="fill_parent"    android:layout_height="wrap_content"    android:id="@+id/linearLayout"    android:background="#295b75"    android:layout_alignParentBottom="true"    android:layout_alignParentLeft="true"    android:layout_alignParentStart="true"    android:layout_marginBottom="50dp">    <ImageView        android:layout_width="40dp"        android:layout_height="40dp"        android:id="@+id/imageView"        android:layout_weight="1"        android:layout_margin="10dp"/>    <ImageView        android:layout_width="40dp"        android:layout_height="40dp"        android:id="@+id/musicprevious"        android:layout_weight="1"        android:layout_margin="10dp"        android:src="@drawable/musicprevious"/>    <ImageView        android:layout_width="40dp"        android:layout_height="40dp"        android:id="@+id/Musicplay"        android:layout_weight="1"        android:layout_margin="10dp"        android:src="@drawable/musicpause"/>    <ImageView        android:layout_width="40dp"        android:layout_height="40dp"        android:id="@+id/musicnext"        android:layout_weight="1"        android:layout_margin="10dp"        android:src="@drawable/musicnext"/>    <ImageView        android:layout_width="40dp"        android:layout_height="40dp"        android:id="@+id/musicmenu"        android:layout_weight="1"        android:layout_margin="10dp"/>LinearLayout>    <SeekBar        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:id="@+id/MusicProgress"        android:max="100"        android:indeterminate="false"        android:layout_above="@+id/linearLayout"        android:layout_alignParentLeft="true"        android:layout_alignParentStart="true"        android:layout_marginBottom="20dp"/>RelativeLayout>

listmusic.xml,自定义子布局:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:layout_width="match_parent"              android:layout_height="60dp"              android:orientation="vertical">    <TextView        android:layout_width="70dp"        android:layout_height="wrap_content"        android:textAppearance="?android:attr/textAppearanceSmall"        android:text="Small Text"        android:id="@+id/MusicID"        android:gravity="center"        android:layout_alignParentLeft="true"        android:layout_alignParentStart="true"        android:layout_alignParentBottom="true"        android:layout_alignParentTop="true"/>    <TextView        android:layout_width="200dp"        android:layout_height="wrap_content"        android:textAppearance="?android:attr/textAppearanceSmall"        android:text="Small Text"        android:id="@+id/Musictitle"        android:textStyle="bold"        android:layout_alignParentTop="true"        android:layout_toRightOf="@+id/MusicID"        android:layout_toEndOf="@+id/MusicID"        android:layout_marginLeft="20dp"        android:layout_marginTop="10dp"        android:textSize="15dp"        android:singleLine="true"        android:ellipsize="end"/>    <TextView        android:layout_width="220dp"        android:layout_height="wrap_content"        android:textAppearance="?android:attr/textAppearanceSmall"        android:text="Small Text"        android:id="@+id/MusicArtist"        android:textColor="#878383"        android:layout_toRightOf="@+id/MusicID"        android:layout_toEndOf="@+id/MusicID"        android:layout_marginLeft="20dp"        android:layout_below="@+id/Musictitle"        android:layout_marginTop="7dp"        android:textSize="12dp"        android:ellipsize="end"        android:singleLine="true"/>    <ImageView        android:layout_width="25dp"        android:layout_height="25dp"        android:id="@+id/MusicIcon"        android:layout_centerVertical="true"        android:layout_alignParentRight="true"        android:layout_alignParentEnd="true"        android:layout_marginRight="30dp"        />RelativeLayout>

这篇文章就介绍到这里面,现在实现的功能只能够点击播放,然后点击暂停和播放,拖动滑动条实现快进和后退。
上一首和下一首还有播放模式都还未做完,具体看下篇文章的介绍。目前正在构思和参考别人的实现。

哈哈,继续加油,共同分享,不断提高自己的技术。

介绍service

源码下载:源代码下载地址

更多相关文章

  1. Android源码分析之WindowManager.LayoutParams属性更新过程
  2. android 蓝牙ble app开发(二) -- 关键概念,连接参数,连接请求
  3. 客觀評 Android、iOS、WP7
  4. Android(安卓)11 Beta 版正式发布!以及众多面向开发者的重磅更新
  5. android静音模式下仍可以播放媒体声音以及调节其大小。。。。
  6. android 使用handler更新ui,使用与原理分析详解(附上代码以及截图)
  7. 基于移动平台的多媒体框架——移植播放器到Android上
  8. 自定义实现圆形播放进度条(android,飞一般的感觉)
  9. android视频开发倍速播放,调整视频播放速度

随机推荐

  1. 把 React 作为 UI 运行时来使用 [每日前
  2. 数据透视表,一篇就够了
  3. 一文带你了解数据保护的重要性
  4. Excel数据获取
  5. 青铜和黄金选手分别怎么玩转python列表?
  6. CSS Flexbox 可视化手册 [每日前端夜话(0
  7. 用Python生成自己专属的手机春节壁纸
  8. super(props) 真的那么重要吗?[每日前端夜
  9. 推荐一个基于 Node.js 的表单验证库 [每
  10. CSS粘性定位是怎样工作的 [每日前端夜话0