Android 初步学习BroadCast与Service实现简单的音乐播放器

Activity代码

package com.siyehuazhilian.musicplay;

import java.util.ArrayList;
import java.util.HashMap;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.graphics.Color;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.SimpleAdapter;
import android.widget.TextView;

public class MainActivity extends Activity implements OnClickListener {
	// ListView
	private ListView listView;
	// 适配器
	private SimpleAdapter adapter;
	// 数据源
	private ArrayList<HashMap<String, String>> list;
	// 当前播放的曲目
	private int currentPositionMusic = -1;

	// 上一首
	private ImageButton lastImageButton;
	// 播放
	private ImageButton playImageButton;
	// 下一首
	private ImageButton nextImageButton;
	// 循环
	private ImageButton loopImageButton;
	// 播放进度
	private SeekBar playSeekBar;
	// 当前播放曲目
	private TextView currentPlayingSong;
	// 是否是第一次进来
	private boolean ifFirstIn = true;

	private Intent intent;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		listView = (ListView) findViewById(R.id.listview);
		lastImageButton = (ImageButton) findViewById(R.id.imagebutton_previous);
		playImageButton = (ImageButton) findViewById(R.id.imagebutton_play);
		nextImageButton = (ImageButton) findViewById(R.id.imagebutton_next);
		loopImageButton = (ImageButton) findViewById(R.id.imagebutton_loops);
		playSeekBar = (SeekBar) findViewById(R.id.seekbar_play);
		currentPlayingSong = (TextView) findViewById(R.id.textview_songinformation);
		list = new ArrayList<HashMap<String, String>>();

		adapter = new SimpleAdapter(this, list, R.layout.list_item,
				new String[] { "title" }, new int[] { R.id.textview_item });
		listView.setAdapter(adapter);

		// 为listView设置监听器
		listView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> arg0, View arg1,
					int position, long arg3) {
				try {
					currentPositionMusic = position;
					playMusic();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});

		lastImageButton.setOnClickListener(this);
		playImageButton.setOnClickListener(this);
		nextImageButton.setOnClickListener(this);
		loopImageButton.setOnClickListener(this);
		playSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {

			}

			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {

			}

			@Override
			public void onProgressChanged(SeekBar seekBar, int progress,
					boolean fromUser) {
				if (fromUser) {
					// 改变进度条
					intent = new Intent("changed");
					intent.putExtra("seekbarprogress", progress);
					startService(intent);
				}
			}
		});
	}

	private void playMusic() {
		// 启动播放音乐的服务
		intent = new Intent("play");
		intent.putExtra("uri", ((HashMap<String, String>) list
				.get(currentPositionMusic)).get("path"));
		intent.putExtra("title", ((HashMap<String, String>) list
				.get(currentPositionMusic)).get("title"));
		startService(intent);
		// 把图片改为播放的图片
		playImageButton.setImageResource(R.drawable.play);
		// 同时更改SeekBar的进度,因为进度是不断变化的,所以需要一个子线程来刷新下
		// playSeekBar.setMax(mp.getDuration());
		// 设置当前播放曲目信息
		currentPlayingSong.setTextColor(Color.GREEN);
		currentPlayingSong.setText(list.get(currentPositionMusic).get("title"));
	}

	@Override
	protected void onResume() {
		super.onResume();
		// 得到所有音频
		if (ifFirstIn) {
			ifFirstIn = false;
			scanMusic();
		}
		// 动态注册广播
		IntentFilter filter = new IntentFilter();
		filter.addAction("seekbarmaxprogress");
		filter.addAction("seekbarprogress");
		filter.addAction("playNextSong");
		filter.addAction("pause");
		filter.addAction("setplay");
		filter.addAction("stoploop");
		filter.addAction("startloop");
		registerReceiver(broadcastReceiver, filter);
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		// 关闭通知
		intent = new Intent("stopnotification");
		startService(intent);

		// 停止服务
		stopService(intent);
		// 取消广播的注册
		unregisterReceiver(broadcastReceiver);
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setTitle("你确定要退出吗?");
			builder.setPositiveButton("确定",
					new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
							finish();
						}
					});
			builder.setNegativeButton("取消",
					new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
						}
					});
			builder.show();
			return true;
		} else {
			return super.onKeyDown(keyCode, event);
		}
	}

	/**
	 * 得多所有的音频
	 */
	private void scanMusic() {
		// 置空list集合中的所有元素,放置反复启动导致的数据重复,其实这个部分是因为每次进来都会重新获得焦点,执行onResume造成的
		// 这个修改虽然可以,但是每次进来都有重新加载,增加了手机的符合,所以可以设置一个标志,只有在第一进来的时候才会加载数据
//		list.clear();
		new Thread() {
			public void run() {
				Cursor cursor = getContentResolver().query(
						MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null,
						null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);

				while (cursor.moveToNext()) {
					String title = cursor
							.getString(cursor
									.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
					String path = cursor
							.getString(cursor
									.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
					String size = cursor
							.getString(cursor
									.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE));
					if (Long.parseLong(size) > 1024 * 1024) {
						HashMap<String, String> hashMap1 = new HashMap<String, String>();
						hashMap1.put("title", title);
						hashMap1.put("path", path);
						list.add(hashMap1);
					}
				}
				cursor.close();
			};
		}.start();

	}

	/**
	 * 监听
	 */
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.imagebutton_previous:
			playLastSong();
			break;
		case R.id.imagebutton_play:
			intent = new Intent("clickplay");
			startService(intent);
			break;
		case R.id.imagebutton_next:
			playNextSong();
			break;
		case R.id.imagebutton_loops:
			intent = new Intent("loops");
			startService(intent);
			break;
		default:
			break;
		}
	}

	/**
	 * 播放下一曲
	 */
	private void playNextSong() {
		// 只有当有音乐在播放的时候才可以点击下一首
		if (currentPositionMusic != -1) {
			// 如果当前歌曲为最后一首,就播放第一首歌曲
			if (currentPositionMusic == list.size() - 1) {
				// 设置当前歌曲为第一首
				currentPositionMusic = 0;
				try {
					// 播放第一首歌曲
					playMusic();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}// 否则就播放下一首歌曲
			else {
				// 设置当前歌曲为下一首
				currentPositionMusic++;
				try {
					// 播放下一首歌曲
					playMusic();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

		}
	}

	/**
	 * 播放上一曲
	 */
	private void playLastSong() {
		// 只有当有音乐在播放的时候才可以点击上一首
		if (currentPositionMusic != -1) {
			// 如果当前歌曲为第一首,就播放最后一首歌曲
			if (currentPositionMusic == 0) {
				// 设置当前歌曲为最后一首
				currentPositionMusic = list.size() - 1;
				try {
					// 播放最后一首歌曲
					playMusic();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}// 否则就播放上一首歌曲
			else {
				// 设置当前歌曲为前一首
				currentPositionMusic--;
				try {
					// 播放前一首歌曲
					playMusic();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

		}
	}

	/**
	 * 广播对象,动态注册,用来接收从Service传过来的消息,根据不同的消息做不同的事情
	 */
	private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			if (intent.getAction().equals("seekbarmaxprogress")) {
				playSeekBar.setMax(intent
						.getIntExtra("seekbarmaxprogress", 100));
			} else if (intent.getAction().equals("seekbarprogress")) {
				playSeekBar.setProgress(intent
						.getIntExtra("seekbarprogress", 0));
			} else if (intent.getAction().equals("playNextSong")) {
				playNextSong();
			} else if (intent.getAction().equals("pause")) {
				// 还要把图片改为暂停的图片
				playImageButton.setImageResource(R.drawable.pause);
			} else if (intent.getAction().equals("setplay")) {
				// 把图片设置成播放的图片
				playImageButton.setImageResource(R.drawable.play);
			} else if (intent.getAction().equals("stoploop")) {
				// 还要把图片改为不循环的图片
				loopImageButton.setImageResource(R.drawable.loop_false);
			} else if (intent.getAction().equals("startloop")) {
				// 把图片设置成循环播放的图片
				loopImageButton.setImageResource(R.drawable.loop_true);
			}

		}
	};

}


Service代码

package com.siyehuazhilian.service;

import java.util.HashMap;

import com.siyehuazhilian.musicplay.MainActivity;
import com.siyehuazhilian.musicplay.R;

import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.widget.Toast;

public class PlayMusicService extends Service {
	private MediaPlayer mediaPlayer;
	// 设置Seebar最大进度
	private static final int SET_SEEKBAR_MAX = 3;
	// 刷新SeekBar进度
	private static final int UPDATE_PROGRESS = 1;
	// 加载数据
	private static final int LOADING_DATA = 2;

	// 通知管理
	private NotificationManager notificationManager;
	// 通知
	private Notification notification;

	private Intent intent;

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	@SuppressLint("ShowToast")
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

		if (intent.getAction().equals("play")) {// 如果是播放Action,就播放音乐
			try {
				Toast.makeText(getApplicationContext(), "开始播放", 1000).show();
				playMusic(intent.getStringExtra("uri"));
				showNotification(intent.getStringExtra("title"));
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else if (intent.getAction().equals("changed")) {// 如果是拖动的Action,就跳至拖动的进度
			if (mediaPlayer != null) {
				mediaPlayer.seekTo(intent.getIntExtra("seekbarprogress", 0));
			}
		} else if (intent.getAction().equals("clickplay")) {// 如果是播放/暂停Action,就执行相应的播放或者暂停
			Toast.makeText(getApplicationContext(), "播放被点击了", 1000).show();
			if (mediaPlayer != null) {
				// 如果音乐播放器正在播放
				if (mediaPlayer.isPlaying()) {
					// 那就暂停它
					mediaPlayer.pause();
					// 发送暂停广播给程序
					this.intent = new Intent("pause");
					sendBroadcast(this.intent);
				} else {
					// 否则就播放它
					mediaPlayer.start();
					// 发送播放广播
					this.intent = new Intent("setplay");
					sendBroadcast(this.intent);
				}
			}
		} else if (intent.getAction().equals("loops")) {// 如果是loop循环Action,就把歌曲设置为循环播放
			Toast.makeText(getApplicationContext(), "循环被点击了", 1000).show();
			if (mediaPlayer != null) {
				// 如果音乐播放器正在循环
				if (mediaPlayer.isLooping()) {
					// 那就终止循环它
					mediaPlayer.setLooping(false);
					// 发送终止循环广播
					this.intent = new Intent("stoploop");
					sendBroadcast(this.intent);
				} else {
					// 否则就循环播放它
					mediaPlayer.setLooping(true);
					// 发送开始循环广播
					this.intent = new Intent("startloop");
					sendBroadcast(this.intent);
				}
			}
		} else if (intent.getAction().equals("stopnotification")) {
			notificationManager.cancelAll();
		}

		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		handler.removeMessages(UPDATE_PROGRESS);
		mediaPlayer.stop();
		mediaPlayer.release();
		mediaPlayer = null;
	}

	/**
	 * 播放音乐
	 * 
	 * @param path
	 * @throws Exception
	 */
	private void playMusic(String path) throws Exception {
		if (mediaPlayer == null) {
			// 新建一个mediaPalyer对象
			mediaPlayer = new MediaPlayer();
		}
		// 使mediaPalyer进入Idle状态
		mediaPlayer.reset();
		// 设置数据源,使mediaPlayer进入Intialized状态
		mediaPlayer.setDataSource(path);
		// 准备播放
		mediaPlayer.prepareAsync();
		mediaPlayer.setOnPreparedListener(new OnPreparedListener() {

			@Override
			public void onPrepared(MediaPlayer mp) {
				// 开始播放音乐
				mp.start();
				handler.sendEmptyMessage(SET_SEEKBAR_MAX);
				handler.sendEmptyMessage(UPDATE_PROGRESS);

			}
		});
		mediaPlayer.setOnCompletionListener(new OnCompletionListener() {

			@Override
			public void onCompletion(MediaPlayer mp) {
				if (mp.isLooping()) {
					mp.start();
				} else {
					intent = new Intent("playNextSong");
					sendBroadcast(intent);
				}
			}
		});
	}

	private Handler handler = new Handler() {
		@SuppressLint("HandlerLeak")
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case UPDATE_PROGRESS:
				// 设置最大当前播放进度
				intent = new Intent("seekbarprogress");
				intent.putExtra("seekbarprogress",
						mediaPlayer.getCurrentPosition());
				sendBroadcast(intent);
				// 需要随着播放设置
				handler.sendEmptyMessageDelayed(UPDATE_PROGRESS, 1000);
				break;
			case LOADING_DATA:
				// adapter.notifyDataSetInvalidated();
				break;
			case SET_SEEKBAR_MAX:
				intent = new Intent("seekbarmaxprogress");
				intent.putExtra("seekbarmaxprogress", mediaPlayer.getDuration());
				sendBroadcast(intent);
				// 因为进度条只需要设置一次就够了,所以不需要反复发送Message;
				break;
			default:
				break;
			}
		};
	};

	/**
	 * 显示通知
	 * 
	 */
	private void showNotification(String title) {
		notification = new Notification(R.drawable.app_icon, "百度音乐",
				System.currentTimeMillis());

		notification.flags |= Notification.FLAG_NO_CLEAR;
		notification.flags |= Notification.FLAG_ONGOING_EVENT;

		Intent intent = new Intent(this, MainActivity.class);
		intent.setAction(Intent.ACTION_MAIN);
		intent.addCategory(Intent.CATEGORY_LAUNCHER);
		PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
				intent, 0);

		notification.setLatestEventInfo(this, "正在播放", title, pendingIntent);

		notificationManager.notify(1, notification);
	}
}


AndroidManifest.xml代码


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.siyehuazhilian.musicplay"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />

    <uses-permission android:name="android.permission.VIBRATE" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/app_icon"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.Light.NoTitleBar" >
        <activity
            android:name="com.siyehuazhilian.musicplay.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />

                <data android:mimeType="audio/*" />
            </intent-filter>
        </activity>

        <service android:name="com.siyehuazhilian.service.PlayMusicService" >
            <intent-filter>
                <action android:name="play" />
                <action android:name="changed" />
                <action android:name="clickplay" />
                <action android:name="loops" />
                <action android:name="stopnotification" />
            </intent-filter>
        </service>
    </application>

</manifest>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值