Android 实现系统深度休眠笔记
休眠几个坑点及解决
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 {
mCa
mera.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) {
评论