RingtonePlayer用来实现铃声的播放逻辑。
涉及的类:
IAudioService:系统服务类,管理音频相关业务。
IRingtonePlayer :RingtonePlayer对外提供的AIDL接口。
Client :由请求播放的信息封装而成,使用Ringtone进行播放。
NotificationPlayer :用于异步播放音频,使用MediaPlayer进行播放。
属性
private IAudioService mAudioService;//系统音频服务类
private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG);//异步播放器
private final HashMap<IBinder, Client> mClients = new HashMap<IBinder, Client>();//Client的缓存
private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() {//铃声播放器的实现
...
}
初始化
@Override
public void start() {//所有SystemUI的初始化入口
mAsyncPlayer.setUsesWakeLock(mContext);//初始化异步播放器的WakeLock
mAudioService = IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE));//获取IAudioService的代理对象
try {
mAudioService.setRingtonePlayer(mCallback);//将IRingtonePlayer的实现设置到IAudioService中
} catch (RemoteException e) {
Log.e(TAG, "Problem registering RingtonePlayer: " + e);
}
}
Clinet
//implements IBinder.DeathRecipient
Client(IBinder token, Uri uri, UserHandle user, AudioAttributes aa,
@Nullable VolumeShaper.Configuration volumeShaperConfig) {
mToken = token;
mRingtone = new Ringtone(getContextForUser(user), false);//创建了Ringtone对象设置
mRingtone.setAudioAttributes(aa);
mRingtone.setUri(uri, volumeShaperConfig);
}
@Override
public void binderDied() {//死亡通知回调
synchronized (mClients) {
mClients.remove(mToken);//从缓存删除
}
mRingtone.stop();//停止播放
}
Clinet主要是封装Ringtone并实现了死亡通知接口
IRingtonePlayer的实现
@Override
public void play(IBinder token, Uri uri, AudioAttributes aa, float volume, boolean looping)
throws RemoteException {
playWithVolumeShaping(token, uri, aa, volume, looping, null);
}
@Override
public void playWithVolumeShaping(IBinder token, Uri uri, AudioAttributes aa, float volume,
boolean looping, @Nullable VolumeShaper.Configuration volumeShaperConfig)
throws RemoteException {
Client client;
synchronized (mClients) {
client = mClients.get(token);//缓存中获取
if (client == null) {//缓存中没找到
final UserHandle user = Binder.getCallingUserHandle();
client = new Client(token, uri, user, aa, volumeShaperConfig);//创建新的Client
token.linkToDeath(client, 0);//注册死亡通知
mClients.put(token, client);//加入缓存
}
}
client.mRingtone.setLooping(looping);
client.mRingtone.setVolume(volume);
client.mRingtone.play();//播放
}
@Override
public void stop(IBinder token) {
Client client;
synchronized (mClients) {
client = mClients.remove(token);//从缓存移除
}
if (client != null) {
client.mToken.unlinkToDeath(client, 0);//注销死亡通知
client.mRingtone.stop();//停止播放
}
}
//...
@Override
public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Async playback only available from system UID.");
}
if (UserHandle.ALL.equals(user)) {
user = UserHandle.SYSTEM;
}
mAsyncPlayer.play(getContextForUser(user), uri, looping, aa);//使用异步播放器进行播放
}
@Override
public void stopAsync() {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Async playback only available from system UID.");
}
mAsyncPlayer.stop();//使用异步播放器停止播放
}
从上可知,播放方式有2种:使用Clinet中的Ringstone播放,使用NotificationPlayer中的MediaPlayer播放
使用Client中的Ringtone播放
当收到开始播放请求即play/playWithVolumeShaping被调用时,首先从缓存中找对应的Client,如果没有创建一个,调用Client中的Ringtone的play方法开始播放
当收到停止播放请求即stop被调用时,首先从缓存中移除对应的Client,调用Client中的Ringtone的stop方法停止播放
当对应的IBinder死亡时,触发死亡通知,Client将从缓存移除,并调Ringtone的stop方法停止播放
使用NotificationPlayer中的MediaPlayer播放
当收到开始播放请求即playAsync被调用时,会调用NotificationPlayer.play方法
当收到停止播放请求即stopAsync被调用时,会调用NotificationPlayer.stop方法
public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) {
Command cmd = new Command();//创建命令对象并进行初始化
cmd.requestTime = SystemClock.uptimeMillis();
cmd.code = PLAY;//播放命令
cmd.context = context;
cmd.uri = uri;
cmd.looping = looping;
cmd.attributes = attributes;
synchronized (mCmdQueue) {
enqueueLocked(cmd);
mState = PLAY;//将状态设置为播放
}
}
public void stop() {
synchronized (mCmdQueue) {
if (mState != STOP) {//当前不是停止状态
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
cmd.code = STOP;//停止命令
enqueueLocked(cmd);
mState = STOP;//将状态设置为停止
}
}
}
private void enqueueLocked(Command cmd) {
mCmdQueue.add(cmd);//放入队列
if (mThread == null) {//CmdThread线程没有启动
acquireWakeLock();//请求WakeLock
mThread = new CmdThread();
mThread.start();//创建CmdThread并启动
}
}
Command的处理
无论是play还是stop,都是创建Command对象,由CmdThread线程去处理
public void run() {//CmdThread的run方法
while (true) {//死循环
Command cmd = null;
synchronized (mCmdQueue) {
cmd = mCmdQueue.removeFirst();//取出队列中的Command
}
switch (cmd.code) {
case PLAY:
startSound(cmd);//调用startSound方法处理播放命令
break;
case STOP:
//此处代码先不看
break;
}
synchronized (mCmdQueue) {
if (mCmdQueue.size() == 0) {//队列命令处理完成
mThread = null;
releaseWakeLock();//释放WakeLock
return;
}
}
}
}
很明显,CmdThread进行循环取出队列的Command对象进行处理,处理完成释放WakeLock,并将mThread置空。
播放命令的处理
播放命令调用了startSound(cmd)来进一步进行处理
private void startSound(Command cmd) {
try {
synchronized(mCompletionHandlingLock) {
if((mLooper != null)
&& (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
mLooper.quit();//这里的mLooper是CreationAndCompletionThread的Looper,这行代码意味着mLooper.loop方法结束运行
}
mCompletionThread = new CreationAndCompletionThread(cmd);//这是真正播放的线程
synchronized (mCompletionThread) {
mCompletionThread.start();//启动线程
mCompletionThread.wait();//等待 CmdThread等待 将会在MediaPlayer启动后唤醒
}
}
//...
}
catch (Exception e) {
Log.w(mTag, "error loading sound for " + cmd.uri, e);
}
}
这里的逻辑是先结束上一个CreationAndCompletionThread的运行,再创建一个CreationAndCompletionThread并启动。
public void run() {//CreationAndCompletionThread的run方法
Looper.prepare();//初始化线程的Looper
mLooper = Looper.myLooper();
MediaPlayer player = null;
synchronized(this) {
AudioManager audioManager =
(AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);
try {
player = new MediaPlayer();
if (mCmd.attributes == null) {
mCmd.attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build();
}
player.setAudioAttributes(mCmd.attributes);
player.setDataSource(mCmd.context, mCmd.uri);
player.setLooping(mCmd.looping);
player.setOnCompletionListener(NotificationPlayer.this);
player.setOnErrorListener(NotificationPlayer.this);
player.prepare();
if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null)
&& (mCmd.uri.getEncodedPath().length() > 0)) {
if (!audioManager.isMusicActiveRemotely()) {
synchronized (mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus == null) {
int focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
if (mCmd.looping) {
focusGain = AudioManager.AUDIOFOCUS_GAIN;
}
mNotificationRampTimeMs = audioManager.getFocusRampTimeMs(
focusGain, mCmd.attributes);
audioManager.requestAudioFocus(null, mCmd.attributes,
focusGain, 0);
mAudioManagerWithAudioFocus = audioManager;
} else {
if (DEBUG) Log.d(mTag, "AudioFocus was previously requested");
}
}
}
}
try {
Thread.sleep(mNotificationRampTimeMs);
} catch (InterruptedException e) {
Log.e(mTag, "Exception while sleeping to sync notification playback"
+ " with ducking", e);
}
player.start();//播放
} catch (Exception e) {
if (player != null) {
player.release();//异常释放
player = null;
}
abandonAudioFocusAfterError();
}
final MediaPlayer mp;
synchronized (mPlayerLock) {
mp = mPlayer;
mPlayer = player;
}
if (mp != null) {
mp.release();//释放上一次使用的MediaPlayer
}
this.notify();//唤醒等待 也就是唤醒startSound方法中的wait调用,CmdThread线程继续运行
}
Looper.loop();//由于没有消息处理,此方法调用后线程会阻塞
}
};
CreationAndCompletionThread根据Command中的参数启动MediaPlayer,并唤醒等待的CmdThread,最后调用Looper.loop()阻塞当前线程,直到CmdThread线程调用mLooper.quit(),CreationAndCompletionThread才会结束运行。
小结:CmdThread处理命令,所有命令处理完成即线程结束;CreationAndCompletionThread负责播放,停止播放/播放完成时线程才会结束。
播放完成的处理
public void onCompletion(MediaPlayer mp) {
synchronized(mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus != null) {
if (DEBUG) Log.d(mTag, "onCompletion() abandonning AudioFocus");
mAudioManagerWithAudioFocus.abandonAudioFocus(null);
mAudioManagerWithAudioFocus = null;
} else {
if (DEBUG) Log.d(mTag, "onCompletion() no need to abandon AudioFocus");
}
}
synchronized (mCmdQueue) {
synchronized(mCompletionHandlingLock) {
if ((mCmdQueue.size() == 0)) {
if (mLooper != null) {
mLooper.quit();//结束CreationAndCompletionThread
}
mCompletionThread = null;
}
}
}
synchronized (mPlayerLock) {
if (mp == mPlayer) {
mPlayer = null;
}
}
if (mp != null) {
mp.release();//释放
}
}
停止命令的处理
case STOP:
final MediaPlayer mp;
synchronized (mPlayerLock) {
mp = mPlayer;
mPlayer = null;
}
if (mp != null) {
long delay = SystemClock.uptimeMillis() - cmd.requestTime;
if (delay > 1000) {
Log.w(mTag, "Notification stop delayed by " + delay + "msecs");
}
try {
mp.stop();//停止播放
} catch (Exception e) { }
mp.release();
synchronized(mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus != null) {
if (DEBUG) { Log.d(mTag, "in STOP: abandonning AudioFocus"); }
mAudioManagerWithAudioFocus.abandonAudioFocus(null);
mAudioManagerWithAudioFocus = null;
}
}
synchronized (mCompletionHandlingLock) {
if ((mLooper != null) &&
(mLooper.getThread().getState() != Thread.State.TERMINATED))
{
if (DEBUG) { Log.d(mTag, "in STOP: quitting looper "+ mLooper); }
mLooper.quit();//意味着结束CreationAndCompletionThread
}
}
} else {
Log.w(mTag, "STOP command without a player");
}
break;
}