之前写了篇关于service保活的博客,其实这么长时间下来,感觉service的存活率并没有什么提升,提升的只是心里安慰。。。
废话不多说,试了下酷狗音乐即使关掉了任何受保护开关,自启动开关,在华为6.0系统锁定屏幕后依然可以保持运行,即便是不播放音乐也可以保持程序不被杀死,所以就想到了是不是跟音乐播放有关,或者跟常驻通知栏有关
需要注意的是,即便是播放无声音频,手机的播放设备焦点也会被占用,可能会和系统提示音,电话铃音,消息提示音等发生冲突,例如打电话的时候会有嘟嘟的声响,就是由于播放焦点被占用的问题,所以我们可以在播放无声音频的时候先获取焦点,获取成功就进行播放,获取失败就不播放,在获取成功后在播放过程中要是焦点被夺走那么也暂停播放,同时也判断当前焦点是否已经被占用,如果被占用也不播放,否则会影响到
试了下发现跟常驻通知栏无关,于是我就试了下在程序后台偷偷的播放一段短音乐,然后把手机锁屏,惊奇的发现,程序不会被杀死了
public class MusicPlayer {
private static MediaPlayer mediaPlayer;
private static AssetFileDescriptor assetFileDescriptor;
private static AudioManager audioManager;
private static AudioManager.OnAudioFocusChangeListener onAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
|| focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
SyncLogUtil.d("audio focus loss...");
mediaPlayer.pause();
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
mediaPlayer.start();
}
}
}
};
public static void init(Context context) {
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
assetFileDescriptor = context.getAssets().openFd("quiet.amr");
} catch (IOException e) {
SyncLogUtil.e(e);
}
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
audioManager.abandonAudioFocus(onAudioFocusChangeListener);
SyncLogUtil.d("play completely and abandon audio focus");
// mediaPlayer.stop();
/*mediaPlayer.start();
mediaPlayer.setLooping(true);*/
}
});
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
SyncLogUtil.d("start play...");
}
});
}
public static void play() {
if (mediaPlayer.isPlaying()) {
return;
}
if (isAudioFocused()) {
SyncLogUtil.w("audio is on focus");
return;
}
int result = audioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) { // 获取焦点失败
SyncLogUtil.w("request audio focus failed");
return;
}
SyncLogUtil.d("request audio focus successfully");
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(assetFileDescriptor.getFileDescriptor(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength());
mediaPlayer.prepareAsync();
} catch (Exception e) {
SyncLogUtil.e(e);
}
}
private static boolean isAudioFocused() {
boolean isFocused = false;
try {
Method method = Class.forName("android.media.AudioSystem").getMethod("isStreamActive", int.class, int.class);
if (method != null) {
boolean music = (boolean) method.invoke(null, AudioManager.STREAM_MUSIC, 0);
boolean alarm = (boolean) method.invoke(null, AudioManager.STREAM_ALARM, 0);
boolean ring = (boolean) method.invoke(null, AudioManager.STREAM_RING, 0);
boolean voiceCall = (boolean) method.invoke(null, AudioManager.STREAM_VOICE_CALL, 0);
boolean notification = (boolean) method.invoke(null, AudioManager.STREAM_NOTIFICATION, 0);
boolean system = (boolean) method.invoke(null, AudioManager.STREAM_SYSTEM, 0);
boolean bluetoothSco = (boolean) method.invoke(null, 6, 0);
boolean systemEnforced = (boolean) method.invoke(null, 7, 0);
boolean dtmf = (boolean) method.invoke(null, AudioManager.STREAM_DTMF, 0);
boolean tts = (boolean) method.invoke(null, 9, 0);
isFocused = music || alarm || ring || voiceCall || notification || system || bluetoothSco || systemEnforced || dtmf || tts;
/*SyncLogUtil.i("music:" + music);
SyncLogUtil.i("alarm:" + alarm);
SyncLogUtil.i("ring:" + ring);
SyncLogUtil.i("voiceCall:" + voiceCall);
SyncLogUtil.i("notification:" + notification);
SyncLogUtil.i("system:" + system);
SyncLogUtil.i("bluetoothSco:" + bluetoothSco);
SyncLogUtil.i("systemEnforced:" + systemEnforced);
SyncLogUtil.i("dtmf:" + dtmf);
SyncLogUtil.i("tts:" + tts);*/
} else {
SyncLogUtil.e("android.media.AudioSystem.isStreamActive method is null");
}
} catch (Exception e) {
SyncLogUtil.e(e);
}
return isFocused;
}
public static void recycle() {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
assetFileDescriptor = null;
try {
Method method = audioManager.getClass().getMethod("unregisterAudioFocusListener", AudioManager.OnAudioFocusChangeListener.class);
if (method != null) {
method.invoke(audioManager, onAudioFocusChangeListener);
SyncLogUtil.d("unregisterAudioFocusListener successfully");
}
} catch (NoSuchMethodException e) {
SyncLogUtil.e(e);
} catch (InvocationTargetException e) {
SyncLogUtil.e(e);
} catch (IllegalAccessException e) {
SyncLogUtil.e(e);
} finally {
audioManager = null;
}
}
}
Service:
public int onStartCommand(final Intent intent, int flags, int startId) {
SyncLogUtil.d("TCPConnection Service has started.");
if (intent != null) {
int taskType = intent.getIntExtra(TaskType.TAG, 0);
SyncLogUtil.i("start connection service task type:" + taskType);
switch (taskType) {
case TaskType.HEART_BEAT:
executeHeartbeat(intent);
break;
case TaskType.ALARM_CONNECT:
alarmConnect(intent);
break;
case TaskType.APP_REMOVED:
String pkgName = intent.getStringExtra("package_name");
SyncLogUtil.w("remove pkgName:" + pkgName);
if (!pkgName.equals(getPackageName())) {
unbindAppPushInfo(pkgName);
}
break;
case TaskType.DEFAULT:
SyncLogUtil.d("default do nothing");
break;
default:
SyncLogUtil.w("unknow task type:" + taskType);
}
} else {
SyncLogUtil.d("ConnectionService.onStartCommand");
}
MusicPlayer.play(this);
return super.onStartCommand(intent, flags, startId);
}
public void onDestroy() {
SyncLogUtil.e("TCPConnection Service has destroy:" + getPackageName());
cancelSendTaskThread();
HeartBeatScheduler.resetHeartbeatSuccessTime();
HeartBeatScheduler.resetShortHeartbeatSuccessCount();
HeartBeatScheduler.cancel(this);
serviceHandler.removeCallbacksAndMessages(null);
if (connection != null) {
TCPConnection tcpConnection = (TCPConnection) connection;
tcpConnection.setOnConnectListener(null);
tcpConnection.setOnDataListener(null);
connection = null;
}
clear();
unregisterReceiver(screenStateReceiver);
MusicPlayer.stop();
// Process.killProcess(Process.myPid());
}
MediaPlayer播放要记得释放,但是在Service的onCreate里面播放完这段音乐之后在onDestroy方法里面去release掉,也就是说如果这个Service一直常驻MediaPlayer就也是一直不会被释放,同时当音乐播放完了之后MediaPlayer就不处于playing状态了,但是它占用的资源并没有被释放,也就是说这时候仍然持有这手机系统上一种类似于“唤醒锁”的东西,这个锁是有计时的,当程序因为播放音乐,或者操作蓝牙,进程都会获取到这个锁,获取了锁那么进程就不会被系统杀死,但是一直持有这个锁会导致手机耗电增加,所以要及时的释放锁,然后系统会重新开始计时,比如放进程释放了锁之后5分钟内没有再获取锁那么就将进程杀死,如果5分钟内再次获取了锁那么就会重新开始计时,这样可以保证进程不会被杀死,我在华为手机上试了,如果一直播放音乐,进程就不会死,如果音乐播放完了,过一会儿进程就死了,但是这种方法依然实用性不强,确实会在一定程度上增加耗电,这就好比是一直开一个音乐软件在播放音乐一样,所以进程保活是达到了,但是又出来一个耗电的问题,这里可以做一个策略,隔一段时间再去播放一段很小的,音量为0的音频来适当的降低耗电