RingtonePlayer用來實現(xiàn)鈴聲的播放邏輯宪卿。
涉及的類:
IAudioService:系統(tǒng)服務類福澡,管理音頻相關(guān)業(yè)務甫贯。
IRingtonePlayer :RingtonePlayer對外提供的AIDL接口涛漂。
Client :由請求播放的信息封裝而成篇梭,使用Ringtone進行播放氢橙。
NotificationPlayer :用于異步播放音頻,使用MediaPlayer進行播放恬偷。
屬性
private IAudioService mAudioService;//系統(tǒng)音頻服務類
private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG);//異步播放器
private final HashMap<IBinder, Client> mClients = new HashMap<IBinder, Client>();//Client的緩存
private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() {//鈴聲播放器的實現(xiàn)
...
}
初始化
@Override
public void start() {//所有SystemUI的初始化入口
mAsyncPlayer.setUsesWakeLock(mContext);//初始化異步播放器的WakeLock
mAudioService = IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE));//獲取IAudioService的代理對象
try {
mAudioService.setRingtonePlayer(mCallback);//將IRingtonePlayer的實現(xiàn)設置到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);//創(chuàng)建了Ringtone對象設置
mRingtone.setAudioAttributes(aa);
mRingtone.setUri(uri, volumeShaperConfig);
}
@Override
public void binderDied() {//死亡通知回調(diào)
synchronized (mClients) {
mClients.remove(mToken);//從緩存刪除
}
mRingtone.stop();//停止播放
}
Clinet主要是封裝Ringtone并實現(xiàn)了死亡通知接口
IRingtonePlayer的實現(xiàn)
@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);//創(chuàng)建新的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被調(diào)用時袍患,首先從緩存中找對應的Client坦康,如果沒有創(chuàng)建一個,調(diào)用Client中的Ringtone的play方法開始播放
當收到停止播放請求即stop被調(diào)用時协怒,首先從緩存中移除對應的Client涝焙,調(diào)用Client中的Ringtone的stop方法停止播放
當對應的IBinder死亡時,觸發(fā)死亡通知孕暇,Client將從緩存移除仑撞,并調(diào)Ringtone的stop方法停止播放
使用NotificationPlayer中的MediaPlayer播放
當收到開始播放請求即playAsync被調(diào)用時,會調(diào)用NotificationPlayer.play方法
當收到停止播放請求即stopAsync被調(diào)用時妖滔,會調(diào)用NotificationPlayer.stop方法
public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) {
Command cmd = new Command();//創(chuàng)建命令對象并進行初始化
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;//將狀態(tài)設置為播放
}
}
public void stop() {
synchronized (mCmdQueue) {
if (mState != STOP) {//當前不是停止狀態(tài)
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
cmd.code = STOP;//停止命令
enqueueLocked(cmd);
mState = STOP;//將狀態(tài)設置為停止
}
}
}
private void enqueueLocked(Command cmd) {
mCmdQueue.add(cmd);//放入隊列
if (mThread == null) {//CmdThread線程沒有啟動
acquireWakeLock();//請求WakeLock
mThread = new CmdThread();
mThread.start();//創(chuàng)建CmdThread并啟動
}
}
Command的處理
無論是play還是stop隧哮,都是創(chuàng)建Command對象,由CmdThread線程去處理
public void run() {//CmdThread的run方法
while (true) {//死循環(huán)
Command cmd = null;
synchronized (mCmdQueue) {
cmd = mCmdQueue.removeFirst();//取出隊列中的Command
}
switch (cmd.code) {
case PLAY:
startSound(cmd);//調(diào)用startSound方法處理播放命令
break;
case STOP:
//此處代碼先不看
break;
}
synchronized (mCmdQueue) {
if (mCmdQueue.size() == 0) {//隊列命令處理完成
mThread = null;
releaseWakeLock();//釋放WakeLock
return;
}
}
}
}
很明顯座舍,CmdThread進行循環(huán)取出隊列的Command對象進行處理沮翔,處理完成釋放WakeLock,并將mThread置空曲秉。
播放命令的處理
播放命令調(diào)用了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方法結(jié)束運行
}
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);
}
}
這里的邏輯是先結(jié)束上一個CreationAndCompletionThread的運行疲牵,再創(chuàng)建一個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調(diào)用榆鼠,CmdThread線程繼續(xù)運行
}
Looper.loop();//由于沒有消息處理纲爸,此方法調(diào)用后線程會阻塞
}
};
CreationAndCompletionThread根據(jù)Command中的參數(shù)啟動MediaPlayer,并喚醒等待的CmdThread妆够,最后調(diào)用Looper.loop()阻塞當前線程识啦,直到CmdThread線程調(diào)用mLooper.quit(),CreationAndCompletionThread才會結(jié)束運行神妹。
小結(jié):CmdThread處理命令颓哮,所有命令處理完成即線程結(jié)束;CreationAndCompletionThread負責播放鸵荠,停止播放/播放完成時線程才會結(jié)束冕茅。
播放完成的處理
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();//結(jié)束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();//意味著結(jié)束CreationAndCompletionThread
}
}
} else {
Log.w(mTag, "STOP command without a player");
}
break;
}