1.拔插u盘的播放记忆

对于音乐应用来说拔插U盘会收到来自TW服务发送的0x9e1f拔插数据
其中msg.arg2数据0为拔出,1为插入,
当收到0时并且确认当前正在播放的文件为插拔的U盘时,会stopMusic,并且清空临时播放列表

1
2
3
4
if(msg.arg2 == 0) {
mTW.mPlaylistRecord.clearRecord();
stopMusic();
}

而此时的播放信息(进度,播放路径等)并未置为初始状态
所以当插入U盘时,
如果判断之前的播放路径为插入的U盘路径
则根据播放路径找到临时播放列表
然后直接进行播放即可

1
2
3
4
5
6
7
8
9
mTW.loadFile(mContext,mTW.mPlaylistRecord, mTW.mCurrentPath);
mTW.toRPlaylist(mTW.mCurrentIndex);
if(!isPlaying() && (mTW.getService() == TWMusic.ACTIVITY_RUSEME)) {
if(prepare(mTW.mCurrentAPath) == 0 && !isPlaying()) {
seekTo(mTW.mCurrentPos);
playMusic();
}
duck(false);
}

只要注意这种状态下播放进度的更新mCurrentPos值,不要置为0,即可保证记忆播放
还有一点要注意的是,有的平台插入U盘时,会连续发送0和1过来,需要修改为插入U盘只发送1,否则会导致播放记忆异常

2.断ACC启动的播放记忆

音乐关于播放状态的记忆形式,目前是以file文件存储的方式,这样做可以保证其他应用可以调用到保存的文件,例如主界面
保存在以下时机时更新

  1. 播放新的文件
  2. 方控停止
  3. 程序完全销毁
  4. 音乐界面退出
    保存的逻辑为
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    try {
    BufferedWriter bw = null;
    try {
    bw = new BufferedWriter(new FileWriter("/data/tw/music"));
    bw.write(mCurrentAPath);
    bw.write('\n');
    bw.write(Integer.toString(mCurrentIndex));
    bw.write('\n');
    bw.write(Integer.toString(mCurrentPos));
    XTLog.i(mCurrentPos);
    bw.write('\n');
    bw.write(Integer.toString(mShuffle));
    bw.write('\n');
    bw.write(Integer.toString(mRepeat));
    bw.write('\n');
    bw.flush();
    mTW.write(0x9f1a,1,0,"sync");
    } catch (Exception e) {
    XTLog.i();
    new File("/data/tw/music").delete();
    } finally {
    if(bw != null) {
    bw.close();
    bw = null;
    }
    }
    FileUtils.setPermissions("/data/tw/music", 0666, -1, -1);
    } catch (Exception e) {
    Log.i(TAG, ""+e.toString());
    }
    保存的位置为
    1
    /data/tw/music
    读取的方式为TWUtils走open,也就是应用初始化的时候读取保存的播放信息更新
    然后应用在走onresume的生命周期时进行记忆播放
    1
    2
    3
    4
    5
    6
    if (!isPlaying() && mTW.mSource == 0x03){
    if(prepare(mTW.mCurrentAPath) == 0) {
    seekTo(mTW.mCurrentPos);
    playMusic();
    }
    }

3.退出进入音乐的播放记忆

先介绍音乐的view层与mode层的绑定逻辑
首先要明确mode是单例的,当音乐activirty启动和音乐服务启动时,都会绑定这个mode

1
2
3
4
5
6
private void BindView(){
mModel.bindMsuiclView(this,mContext);
}
public void onCreate(){
BindView();
}

mode种对应的处理是添加到一个view集合中

1
2
3
4
5
6
7
8
9
10
@Override
public void bindMsuiclView(MusicModelView musicPresenter,Context context) {
if (musicModelViews.size() == 0){
this.mContext = context;
onCreate();
}
if (!musicModelViews.contains(musicPresenter)){
musicModelViews.add(musicPresenter);
}
}

当音乐activity和音乐服务都退出时,
会解绑各自的对象

1
2
3
4
5
6
public void onDestroy() {
unBindView();
}
private void unBindView(){
mModel.unBindMsuiclView(this);
}

view集合也会对应remove对应的对象

1
2
3
4
5
6
7
8
9
10
@Override
public void unBindMsuiclView(MusicModelView musicPresenter) {
if (musicModelViews.contains(musicPresenter)){
musicModelViews.remove(musicPresenter);
}
if (musicModelViews.size()==0){
onDestory();
}
saveData();
}

当没有view绑定mode时(musicModelViews.size()==0)mode才会自毁回收
所以当音乐activity返回退出时,其实mode对象处于被音乐服务持有运行的状态
而根据服务的生存特性,当activity销毁时服务其实还在在后台运行,

1
2
3
4
5
@Override
protected void onDestroy() {
mPresenter.onPause();
mPresenter.musicPause();
mPresenter.onDestroy();

音乐activity退出时,只做暂停和解绑处理,此时播放器仅仅是暂停状态
所以当音乐activity重新启动时,走到onresume时,会恢复退出之前的播放状态,并更新播放信息。

4.启动360状态的播放记忆

正常情况下,非断电重启时,机器会根据模式恢复程序运行,而针对一些带DVR和360的平台,例如T5,TS10,
有强制启动DVR/360的启动方式,当跟音乐同时启动时,根据机器性能和状态,有可能会导致音乐播放异常问题。
所以这时候,音乐这边是建议当判断要启动360时,音乐只以启动服务的方式运行在后台
具体的操作放在TW服务中携带一个Extra启动音乐服务

1
2
3
4
5
6
7
8
9
10
11
startServiceAPK("com.tw.music","com.tw.music.MusicService","resume");
...
private void startServiceAPK(String pkg, String cls, String cmd) {
try {
Intent it = new Intent();
it.setComponent(new ComponentName(pkg, cls));
it.putExtra("cmd", cmd);
startService(it);
} catch (Exception e) {
}
}

音乐服务会自启播放

1
2
3
4
5
else if ("resume".equals(cmd)){
if (!mMusicInfo.isPlaying()){
mPresenter.musicPlay();
}
}