在上一篇源码分析中,我们提到了AIDL,这部分的详解网上已经很详细,就不再赘述了,本篇仅仅针对其实现部分的代码做一些分析。在这部分源码中,IMediaPlaybackService.aidl中定义了一系列方法,MediaPlaybackService则是对其中定义方法的具体实现,由于这个类的方法非常多,本篇仅对其中部分函数进行简要的分析。
MediaPlaybackService
首先,我们看一看这个service定义的mMediaplayerHandler,其结构并不复杂,根据msg的类型分成四种情况进行处理。其代码如下:
private Handler mMediaplayerHandler = new Handler() {
float mCurrentVolume = 1.0f;
@Override
public void handleMessage(Message msg) {
MusicUtils.debugLog("mMediaplayerHandler.handleMessage " + msg.what);
switch (msg.what) {
//反复发送延迟msg,完成声音渐强的效果
case FADEIN:
if (!isPlaying()) {
mCurrentVolume = 0f;
mPlayer.setVolume(mCurrentVolume);
play();
mMediaplayerHandler.sendEmptyMessageDelayed(FADEIN, 10);
} else {
mCurrentVolume += 0.01f;
if (mCurrentVolume < 1.0f) {
mMediaplayerHandler.sendEmptyMessageDelayed(FADEIN, 10);
} else {
mCurrentVolume = 1.0f;
}
mPlayer.setVolume(mCurrentVolume);
}
break;
//分为两种情况。第一种是正在播放时,因为音频损坏
//等类似的原因导致播放服务无法继续,则强制播放下
//一首;另一种则是当服务暂停时又重启,
//则重新播放当前曲目
case SERVER_DIED:
if (mIsSupposedToBePlaying) {
next(true);
} else {
// the server died when we were idle, so just
// reopen the same song (it will start again
// from the beginning though when the user
// restarts)
openCurrent();
}
break;
//音轨没有歌曲后的处理方式
case TRACK_ENDED:
if (mRepeatMode == REPEAT_CURRENT) {
seek(0);
play();
} else {
next(false);
}
break;
//解锁后的操作
case RELEASE_WAKELOCK:
mWakeLock.release();
break;
default:
break;
}
}
};
接下来我们看看service中的生命周期函数。在MediaPlaybackService中重写了onCreate和onDestory两个方法。在onCreate中,MediaPlaybackService新建了一个AudioManager并绑定了一个按钮事件的监听,同时绑定了SD卡相关事件的监听,onDestory所做的主要工作和前者相反,主要是进行一些解绑。在这部分中,我们只有一个特殊的地方需要额外关注,即saveQueue(boolean full)方法,下面针对其中个人觉得有意思的代码做一下简要的分析,首先该方法具体代码如下:
private void saveQueue(boolean full) {
if (!mQueueIsSaveable) {
return;
}
Editor ed = mPreferences.edit();
if (full) {
//用于记录转换歌曲id号所得的序列化字符串。歌曲id
//可作为歌曲在数据库中的唯一索引
StringBuilder q = new StringBuilder();
// 为加快生成速度,这里使用反转十六进制存储(性能较优)
int len = mPlayListLen;
for (int i = 0; i < len; i++) {
long n = mPlayList[i];
if (n < 0) {
continue;
} else if (n == 0) {
q.append("0;");
} else {
//这里是进行的十进制向十六进制的转换
//hexdigits[]事先存储好了1-9,a-f十六进制的char字符
while (n != 0) {
int digit = (int)(n & 0xf);
n >>>= 4;
q.append(hexdigits[digit]);
}
q.append(";");
}
}
ed.putString("queue", q.toString());
ed.putInt("cardid", mCardId);
if (mShuffleMode != SHUFFLE_NONE) {
// 随机模式下也需要记录历史数据
len = mHistory.size();
q.setLength(0);
for (int i = 0; i < len; i++) {
int n = mHistory.get(i);
if (n == 0) {
q.append("0;");
} else {
while (n != 0) {
int digit = (n & 0xf);
n >>>= 4;
q.append(hexdigits[digit]);
}
q.append(";");
}
}
ed.putString("history", q.toString());
}
}
ed.putInt("curpos", mPlayPos);
if (mPlayer.isInitialized()) {
ed.putLong("seekpos", mPlayer.position());
}
ed.putInt("repeatmode", mRepeatMode);
ed.putInt("shufflemode", mShuffleMode);
SharedPreferencesCompat.apply(ed);
}
这一段代码整体直接看逻辑并不难理解,可能产生的疑问主要有关用反转十六进制存储是什么意思。要明白这部分的问题,我们先来看看reloadQueue相关代码。
int plen = 0;
int n = 0;
int shift = 0;
for (int i = 0; i < qlen; i++) {
char c = q.charAt(i);
if (c == ';') {
ensurePlayListCapacity(plen + 1);
mPlayList[plen] = n;
plen++;
n = 0;
shift = 0;
} else {
if (c >= '0' && c <= '9') {
n += ((c - '0') << shift);
} else if (c >= 'a' && c <= 'f') {
n += ((10 + c - 'a') << shift);
} else {
// bogus playlist data
plen = 0;
break;
}
shift += 4;
}
}
以上部分的代码是reloadQueue方法中对应的解析部分,对照saveQueue部分代码,我们可以看出,它实质上是将id号进行序列化和反序列化的过程。而从十进制转化为十六进制可以减少序列化后字符串的长度,也可以减少序列化和反序列化过程中的循环次数。反转存储的方式则给读取带来了很多方便(若不反转,则解析的时候需要首先读出一段字符,计算长度才能进行反序列化)。reloadQueue剩下部分的代码注释比较清晰,主要是进行一些边界和特殊情况处理,这里就不赘述了。
MediaPlaybackService剩下的部分主要是基本的逻辑,包括play,stop,pause等方法,主要都是考虑了边界条件,增删相关数据或者调用MediaPlayer的相关方法,内容多但逻辑并不复杂,暂时就不在本篇文章上进行分析了。在下一篇源码解析中,我们会回到MusicBrowserActivity,跟着其内部调用过程往下看,谢谢大家。