zhu main :
package com.lyz.news.day33nusic; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.util.Log; import android.view.View; import android.widget.SeekBar; public class MainActivity extends Activity { /** * 安卓api Handler就要求消息队列应该写成 static了这样我们才能在service服务里才能发信息, * bundle.putInt("duration",duration); * bundle.putInt("currentPosition",currentPosition); */ static Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // 我们把消息取出来 Bundle bundle = msg.getData(); // //当前进度 int duration = bundle.getInt("duration"); //当前进度 // 通过key我们拿到value currentPosition 最大进度 int currentPostition = bundle.getInt("currentPosition"); // 我们需要在这里使用SeekBar sb, 所以需要把sb设置为静态,否则访问不到; //刷新进度条; sb.setMax(duration);//主要这两个API setMax音乐的最大长度,setProgress 当前音乐 sb.setProgress(currentPostition); } }; MusicInterface mi; private MyserviceConn conn; private Intent intent; private static SeekBar sb; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //设置一下SeekBar,因为我们要SeekBar设置当前进度, sb = (SeekBar) findViewById(R.id.sb); /** * 和触摸一样也是:触摸是一个方法有三种状态 触摸按下 ,抬起, 移动 * 这个是分为3个方法:触摸按下 ,抬起, 移动 *用着3个那个ne ? * 我们只要在用户改变进度 ,抬起手结束哪一刹那,改变就可以了 */ sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override//进度移动 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { Log.i("MainActivity", "触摸移动!!!"); } //开始追踪' @Override public void onStartTrackingTouch(SeekBar seekBar) { Log.i("MainActivity", "触摸按了!!!"); } //停止追踪'触摸 ,谁触发这个方法就传谁进来 @Override public void onStopTrackingTouch(SeekBar seekBar) { Log.i("MainActivity", "触摸松开!!!!!!"); int progress = seekBar.getProgress(); //改变播放进度 mi.seekTo(progress); } }); intent = new Intent(MainActivity.this, MusicService.class); startService(intent);//start 设置服务 /** * 这里如果你要是就用一次,没有解绑就可以使用匿名内部类,new Myserivce () * 还可以给他调出来搞 */ // bindService(intent,new MyserviceConn(),BIND_AUTO_CREATE); conn = new MyserviceConn(); bindService(intent, conn, BIND_ABOVE_CLIENT);//然后绑定 } public void play(View v) { mi.play(); } public void pause(View v) { mi.pause(); } public void continuePlay(View v) { mi.continuePlay(); } public void exit(View v) { // 解除绑定 unbindService(conn); //关闭服务 stopService(intent); finish(); } private class MyserviceConn implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { //这是本地服务,所以不需要aidl,直接用接口强转 mi = (MusicInterface) service; } @Override public void onServiceDisconnected(ComponentName name) { } } }Service:
package com.lyz.news.day33nusic; import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.Message; import java.util.Timer; import java.util.TimerTask; /** * Created by Administrator on 2015/9/20. */ public class MusicService extends Service { MediaPlayer player; private Timer timer; @Override//暂停 public IBinder onBind(Intent intent) { return new MusicController(); } @Override public void onCreate() { super.onCreate(); //初始化创建一个MediaPlayer对象 player = new MediaPlayer(); //我 在这不小心写一个,导致资源初始化就被null所以报错空指针, // player=null; } @Override public void onDestroy() { super.onDestroy(); // 关闭音乐,但现在基本不用了 player.stop(); // 释放所有资源,在c语言的角度,player已经不存在了,想再播放必须从新new了 player.release(); //停掉计时器 if (timer != null) { timer.cancel(); timer = null; } } class MusicController extends Binder implements MusicInterface { @Override public void play() { MusicService.this.play(); } @Override public void pause() { // MusicService.this 代表当前这个类的对象。方法来在内部类调用本类的方法 MusicService.this.pause(); } @Override public void continuePlay() { MusicService.this.continuePlay(); } @Override public void seekTo(int progress) { player.seekTo(progress); } } //改变当前播放进度,视频居然在()随便想传什么,就传了个什么,因为这个是service里的方法我们想让他在Mainactvity里被调用只能给他到中间人里 public void seekTo(int progress){ player.seekTo(progress); } //播放音乐 public void play() { // 必须重置,重置后就可以加载数据,和调用方法 player.reset(); try { //这个资源,可能是不存在,可能是无法加载,所以必须try cath player.setDataSource("sdcard/qw.mp3"); //设置网络资源 // player.setDataSource("http://192.168.1.101:8080/examples/qy.mp3 "); // 准备,加载资源,是同步,对于本地什么的可以,但是音乐太大了,无法确定是否加载完,网络要用异步,因为必须要在子线程,加载; // player.prepare(); // 播放,网络音乐 // player.start(); //网络异步 player.prepareAsync(); //设置一个播放监听,监听一下他是否加载完了,加载完了,让他进入下面的方法,因为你直接start不知道是否加载完了,所以必须用回调 player.setOnPreparedListener( new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { // 能进到这里说明已经加载完了 player.start(); addTimer(); } }); } catch (Exception e) { e.printStackTrace(); } } // 暂停 public void pause() { player.pause(); } //再次播放,继续播放 public void continuePlay() { player.start(); } public void addTimer() { // timer==null 我们就new一个 if (timer == null) { timer = new Timer(); /** * timer可以设置 你执行 子线程 的时间;所以叫做计时器 * 我们开了一个子线程让获取的 歌曲的总时长 歌曲的总进度 * 不断的变化,半秒一更新 * * * long per period 默认是毫秒 执行间隔 设置1000 视频荷给的是500??为什么 就是让他1秒执行一次run() 我需要传递给SeekBar ,需要在子线更新UI 所以用消息队列; */ timer.schedule(new TimerTask() {//schedule @Override public void run() { // 获取 歌曲的总时长 int duration = player.getDuration(); // 获取歌曲的进度 int currentPosition = player.getCurrentPosition(); //创建消息 Message msg = MainActivity.handler.obtainMessage(); /** 以前我用obj来装消息,但是obj值能装一个内容; *但是我们能把2个数据定义成一个数组,或是集合啊 * * 通过我们的消息发送给我们的主线程 */ //把 歌曲的总时长, 获取歌曲的进度 封装到Bundle Bundle bundle = new Bundle(); bundle.putInt("duration", duration); bundle.putInt("currentPosition", currentPosition); // setData Bundle 里的数据塞到消息对象 msg.setData(bundle); //不要直接发送空信息,我们要携带数据,我们数据send发送到 handler那边去 MainActivity.handler.sendMessage(msg); } // 开始计时任务后的5毫秒中,第一执行run方法,以后每500毫秒执行一次run方法来获取进度 }, 5, 500); } } }interface:
package com.lyz.news.day33nusic; /** * Created by Administrator on 2015/9/20. */ public interface MusicInterface { void play(); void pause(); void continuePlay( ); void seekTo(int progress); }
记得要在清单文件里 设置 服务;