Android 实现系统深度休眠笔记

来源:互联网 发布:nginx lua脚本开发 编辑:程序博客网 时间:2024/05/22 15:39

休眠

  • Android手机有两个处理器,一个叫Application Processor(AP),一个叫Baseband Processor(BP)。非通话时间,BP的能耗基本上在5mA左右,而AP只要处于非休眠状态,能耗至少在50mA以上,执行图形运算时会更高。一般手机待机时,AP、LCD、WIFI均进入休眠状态,这时Android中应用程序的代码也会停止执行。Android为了确保应用程序中关键代码的正确执行,提供了Wake Lock的API,使得APP可以通过之阻止AP进入休眠。但不一定必要,首先,完全没必要担心AP休眠会导致收不到消息推送。通讯协议栈运行于BP,一旦收到数据包(按:收到 TCP 数据包才会唤醒 AP,UDP 包不会唤醒),BP会将AP唤醒,唤醒的时间足够AP执行代码完成对收到的数据包的处理过程。其它的如Connectivity事件触发时AP同样会被唤醒。那么唯一的问题就是程序如何执行向服务器发送心跳包的逻辑。你显然不能靠AP来做心跳计时。Android提供的Alarm Manager就是来解决这个问题的。Alarm应该是BP计时(或其它某个带石英钟的芯片,不太确定,但绝对不是AP),触发时唤醒AP执行程序代码。那么Wake Lock API有啥用呢?比如心跳包从请求到应答,比如断线重连重新登陆这些关键逻辑的执行过程,就需要Wake Lock来保护(按:只在这些关键逻辑时,需要Wake Lock API确保不休眠)。而一旦一个关键逻辑执行成功,就应该立即释放掉Wake Lock了。两次心跳请求间隔5到10分钟,基本不会怎么耗电。除非网络不稳定,频繁断线重连,那种情况办法不多。

  • 休眠几个坑点及解决

    1.向服务器轮询的代码不执行:曾经做一个应用,利用Timer和TimerTask,来设置对服务器进行定时的轮询,但是发现机器在某段时间后,轮询就不再进行了。查了很久才发现是休眠造成的。后来解决的办法是,利用系统的AlarmManager来执行轮询。因为虽然系统让机器休眠,节省电量,但并不是完全的关机,系统有一部分优先级很高的程序还是在执行的,比如闹钟,利用AlarmManager可以定时启动自己的程序,让cpu启动,执行完毕再休眠(按:如上述,就是通过 BP 唤醒 AP)。
    2.后台长连接断开:最近遇到的问题。利用Socket长连接实现QQ类似的聊天功能,发现在屏幕熄灭一段时间后,Socket就被断开。屏幕开启的时候需进行重连,但每次看Log的时候又发现网络是链接的,后来才发现是cpu休眠导致链接被断开,当你插上数据线看log的时候,网络cpu恢复,一看网络确实是链接的, 坑。最后使用了PARTIAL_WAKE_LOCK,保持CPU不休眠。
    3.调试时是不会休眠的:在调试2的时候,就发现,有时Socket会断开,有时不会断开,后来才搞明白,因为我有时是插着数据线进行调试,有时拔掉数据线,这时Android的休眠状态是不一样的。而且不同的机器也有不同的表现,比如有的机器,插着数据线就会充电,有的不会,有的机器的设置的充电时屏幕不变暗等等。

  • DC连接汽车12V永不掉电,熄火时ACC发出掉电信号时,行车记录装置采用不关机,深度休眠策略。关闭屏幕,停止录像,记录轨迹的同时,需要打开飞行模式(蓝牙,WiFi),关闭FM发射,关闭GPS。如果此时有音乐播放和后台导航,也需要关闭。

  • 深度休眠时,待机电流降到10-30mA,此时底层摄像头已不再断电,所以在此步骤进行之前,要停掉Camera预览。否则机器唤醒的时候预览区域会出现卡死。

  • 关闭屏幕,发送自定义广播:

context.sendBroadcast(new Intent("tchip.intent.action.ACTION_KEY_POWER"));

接收的应用,需要具备INJECT_EVENTS权限:

 <uses-permission android:name="android.permission.INJECT_EVENTS" />

和系统的userId:

 android:sharedUserId="android.uid.system"

接收到此广播后,发出对应的key即可:

sendKeyCode(KeyEvent.KEYCODE_POWER);
  • 打开/关闭飞行模式,同样发送自定义广播给拥有系统uid的应用,同时需要具备权限写入WRITE_SECURE_SETTINGS,打开setting.db可以看到三个表,其中secure表是一些敏感字段:
    这里写图片描述
    这里写图片描述
 <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>

然后执行对应的操作:

/**     * 当前是否开启飞行模式     */    private boolean isAirplaneModeOn(Context context) {        // 返回值是1时表示处于飞行模式        int modeIdx = Settings.Global.getInt(context.getContentResolver(),                Settings.Global.AIRPLANE_MODE_ON, 0);        boolean isEnabled = (modeIdx == 1);        //MyLog.v("[SleepReceiver]isAirplaneModeOn:" + isEnabled);        return isEnabled;    }    /**     * 设置飞行模式     */    private void setAirplaneMode(boolean setAirPlane, Context context) {        Settings.Global.putInt(context.getContentResolver(),                Settings.Global.AIRPLANE_MODE_ON, setAirPlane ? 1 : 0);        // 广播飞行模式的改变,让相应的程序可以处理。        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);        intent.putExtra("state", setAirPlane);        context.sendBroadcast(intent);    }
  • 根据包名杀死后台应用:
public void killAppByPackageName(String package){        ActivityManager myActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);         List<ActivityManager.RunningAppProcessInfo> mRunningPros = myActivityManager.getRunningAppProcesses();        for (ActivityManager.RunningAppProcessInfo amPro : mRunningPros){                               if(amPro.processName.contains(package)){                try {                    Method forceStopPackage = myActivityManager.getClass().getDeclaredMethod("forceStopPackage", String.class);                      forceStopPackage.setAccessible(true);                      forceStopPackage.invoke(myActivityManager, amPro.processName);                }                catch (Exception e) {                }            }        }
  • 媒体/铃声声音静音,需要保存休眠前的音量,供唤醒后恢复:
if (Constant.Module.muteWhenSleep) {    int volumeMusic = audioManager                          .getStreamMaxVolume(AudioManager.STREAM_MUSIC);    int volumeRing = audioManager                           .getStreamVolume(AudioManager.STREAM_RING);    editor.putInt("volumeMusic", volumeMusic);    editor.putInt("volumeRing", volumeRing);    editor.commit();}
  • 关闭/打开GPS
context.sendBroadcast(new Intent(                        "tchip.intent.action.ACTION_GPS_OFF"));
private static boolean getGpsState(Context context) {        ContentResolver resolver = context.getContentResolver();        boolean gpsState = Settings.Secure.isLocationProviderEnabled(resolver,                LocationManager.GPS_PROVIDER);        Log.v("ZMS", "[GPS]Now State:" + gpsState);        return gpsState;    }    private void setGpsState(Context context, boolean isGpsOn) {        ContentResolver resolver = context.getContentResolver();        boolean nowState = getGpsState(context);        if (isGpsOn != nowState) {            Log.v("ZMS", "[GPS]Set State:" + isGpsOn);            Settings.Secure.setLocationProviderEnabled(resolver,                    LocationManager.GPS_PROVIDER, isGpsOn);        }    }
  • 停止录像预览,释放recorder:由于熄屏时,不会触发SurfaceView的surfaceDestroy,所以将destroy的过程移动到Activity的onPause中执行
private void releaseCameraZone() {    release();    // mHolder = null;    if (mCamera != null) {        mCamera.stopPreview();    }    MyApplication.shouldResetRecordWhenResume = true;}public void release() {    releaseRecorder();    closeCamera();}private void releaseRecorder() {        if (mMyRecorder != null) {            mMyRecorder.stop();            mMyRecorder.close();            mMyRecorder.release();            mMyRecorder = null;            MyLog.d("Record Release");        }}private boolean closeCamera() {    if (mCamera == null)        return true;    try {        mCamera.lock();        mCamera.stopPreview();        mCamera.setPreviewDisplay(null);        mCamera.release();        mCamera.unlock();        mCamera = null;        return true;    } catch (Exception ex) {        mCamera = null;        return false;    }}


唤醒

  • 摄像头预览区域重新绘制,在Activity的onResume中判断是否需要执行:
if (!MyApplication.isFirstLaunch) {            if (!MyApplication.isVideoReording                    || MyApplication.shouldResetRecordWhenResume) {                MyApplication.shouldResetRecordWhenResume = false;                // 重置预览区域                if (mCamera == null) {                    // mHolder = holder;                    setup();                } else {                    try {                        mCamera.lock();                        mCamera.setPreviewDisplay(mHolder);                        mCamera.startPreview();                        mCamera.unlock();                    } catch (Exception e) {                        // e.printStackTrace();                    }                }            }        } else {            MyApplication.isFirstLaunch = false;        }
public void setup() {    release();    if (openCamera()) {        setupRecorder();    }}
  • 点亮屏幕
/**     * 点亮屏幕     *      * @param context     */    public static void lightScreen(Context context) {        // 获取电源管理器对象        PowerManager pm = (PowerManager) context                .getSystemService(Context.POWER_SERVICE);        // 获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是LogCat里用的Tag        PowerManager.WakeLock wl = pm.newWakeLock(                PowerManager.ACQUIRE_CAUSES_WAKEUP                        | PowerManager.SCREEN_DIM_WAKE_LOCK, "bright");        wl.acquire(); // 点亮屏幕        wl.release(); // 释放        // 得到键盘锁管理器对象        KeyguardManager km = (KeyguardManager) context                .getSystemService(Context.KEYGUARD_SERVICE);        // 参数是LogCat里用的Tag        KeyguardLock kl = km.newKeyguardLock("ZMS");        kl.disableKeyguard();    }
  • 恢复音量设置
if (Constant.Module.muteWhenSleep) {    int volumeMusic = preferences.getInt("volumeMusic", 8);    int volumeRing = preferences.getInt("volumeRing", 8);    audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,                            volumeMusic, 0);    audioManager.setStreamVolume(AudioManager.STREAM_RING,                            volumeRing, 0);}
  • 打开GPS:发送广播
  • 关闭飞行模式:发送广播
  • 打开自动录像标志位
0 0
原创粉丝点击