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:发送广播
- 关闭飞行模式:发送广播
- 打开自动录像标志位
- Android 实现系统深度休眠笔记
- 防止Android系统休眠
- Android 系统永不休眠
- android wake_lock 在底层实现系统不休眠
- Android-休眠的实现
- Android 休眠和唤醒(不会深度休眠状态)
- android计时与系统休眠
- android 4.4 系统永不休眠
- android计时与系统休眠
- Android系统无法进入休眠
- android 4.4 系统永不休眠
- android 系统的休眠与唤醒+linux 系统休眠
- android 4.2 系统增永不休眠同时隐藏休眠选项
- Android的休眠与唤醒 && Android系统关机或重启的几种实现方式
- 系统休眠在.net中的实现
- Win7系统笔记本如何实现关盖不休眠
- android系统休眠与唤醒源代码分析
- Android系统插入OTG后不休眠
- iOS 取相册
- Android各大网络请求库的比较及实战
- LeetCode|Rotate Array-java
- Git冲突:commit your changes or stash them before you can merge.
- Maven仓库
- Android 实现系统深度休眠笔记
- web前端面试题
- JDBC中的Dao设计模式
- 在MAC上查找和设置$JAVA_HOME
- Redhat Ext4 File System Guide
- windows Server 2008各版本有何区别?
- 如何向Android Studio中导入GitHub中的开源项目
- 学习笔记:Node.js(一)
- Java 注释技巧