进程保活之——这绝对不是扯淡

之前写了篇关于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的音频来适当的降低耗电

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值