现在的位置: 首页 > 综合 > 正文

Android 中保持屏幕唤醒的方法

2013年03月03日 ⁄ 综合 ⁄ 共 8200字 ⁄ 字号 评论关闭

最近在解一个 bug 时,用到了这个知识点。在这里总结一下:

bug 是这样描述的:

在 Camera 切换到摄像时,摄像过程大概持续2,3分钟,就自动进锁屏了

有时也会持续很长时间进锁屏。

这是一个概率性的问题(即随机出现)。

 

从原理上分析上来看,肯定是屏幕被锁住了。

log上分析会看到下面的信息:

07-01 13:56:24.144 284 315 I ActivityManager: goingToSleep
07-01 13:56:24.154 100 201 I SurfaceTexture: [SurfaceView] [virtual android::status_t android::SurfaceTexture::convertToAuxSlot(bool)] create dst buffer and image
07-01 13:56:24.168 100 201 I MDP : [MDP INFO](201): mHalMdp_RegisterLoopMemory(): client id:0 VA:0x48ACC000 size:0x384000 color:4 size:(1280x720) roi:(0 0 1280 720) rotate:0
07-01 13:56:24.168 100 201 I MDP : [MDP INFO](201): mHalMdp_RegisterLoopMemory():MVA Obj Create:client id:0 0x48ACC000(VA) 0x00000000(MVA) 0x00000001(M4U flag) 0x00000001(alloc MVA flag)
07-01 13:56:24.168 100 201 I SurfaceTexture: register MVA success, type:1, srcImgYAddr:0x48acc000, SFTexCnt:6
07-01 13:56:24.207 284 315 D ActivityManager: ACT-AM_PAUSE_ACTIVITY ActivityRecord{41b2ba28 com.android.camera/.VideoCamera}
07-01 13:56:24.208 4187 4187 I videocamera: onPause

和在摄像界面按锁屏键进锁屏的log是一样的。but 我什么都没按。

要不是我自己碰到了我还真不相信会有这个bug,从log 上看我一直认为是测试人员误按了锁屏键导致此问题的。

 

再来了解一下VideoCamera 是怎么保持屏幕唤醒的呢?

与下面几个函数有关。

    private void resetScreenOn() {
        mHandler.removeMessages(CLEAR_SCREEN_DELAY);
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    }

    private void keepScreenOnAwhile() {
        Log.d(TAG, TAG + "\t call keepScreenOnAwhile() !");
        mHandler.removeMessages(CLEAR_SCREEN_DELAY);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);

    }

    private void keepScreenOn() {
        Log.d(TAG, TAG + "\t call keepScreenOn() !");
        mHandler.removeMessages(CLEAR_SCREEN_DELAY);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    }
                 case CLEAR_SCREEN_DELAY: {
                    Log.i(TAG, "received CLEAR_SCREEN_DELAY");
                    getWindow().clearFlags(
                            WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                    break;

 

在开始录像的函数 startVideoRecording 中调用 keepScreenOn() 来保持屏幕唤醒状态。

在停止录像的函数 stopVideoRecording 中调用 keepScreenOnAwhile() 来继续保持屏幕唤醒2分钟。2分钟后清除掉 FLAG_KEEP_SCREEN_ON ,使屏幕进入正常锁屏流程。

在videocamera activity 的 onPause 函数中调用 resetScreenOn() 来清除掉 FLAG_KEEP_SCREEN_ON ,使屏幕可以进入正常锁屏流程。

我们知道 onPause 函数一般是当该 Activity 在切换到后台的时候执行的。检查代码,发现在流程上并没有问题。后来了解到是系统维护的这个 FLAG_KEEP_SCREEN_ON 有问题才导致这种现象产生的。

为了解决问题,我了解到,还有一种保持屏幕唤醒的方法,即电源管理的 wakeLock 锁。了解到其用法如下:

{@samplecode
  PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
  PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
  wl.acquire();
    ..screen will stay on during this section..
  wl.release();
  }

先产生一个 PowerManager 对象,再获取一个 wakelock 锁。在需要保持屏幕唤醒的地方加上该锁,恢复的时候释放该锁即可。需要注意一下的是 pm.newWakeLock 的第一个参数。从代码注释上可以看到

 * <p>The following flags are defined, with varying effects on system power.  <i>These flags are
 * mutually exclusive - you may only specify one of them.</i>
 * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
 *
 *     <thead>
 *     <tr><th>Flag Value</th> 
 *     <th>CPU</th> <th>Screen</th> <th>Keyboard</th></tr>
 *     </thead>
 *
 *     <tbody>
 *     <tr><th>{@link #PARTIAL_WAKE_LOCK}</th>
 *         <td>On*</td> <td>Off</td> <td>Off</td> 
 *     </tr>
 *     
 *     <tr><th>{@link #SCREEN_DIM_WAKE_LOCK}</th>
 *         <td>On</td> <td>Dim</td> <td>Off</td> 
 *     </tr>
 *
 *     <tr><th>{@link #SCREEN_BRIGHT_WAKE_LOCK}</th>
 *         <td>On</td> <td>Bright</td> <td>Off</td> 
 *     </tr>
 *     
 *     <tr><th>{@link #FULL_WAKE_LOCK}</th>
 *         <td>On</td> <td>Bright</td> <td>Bright</td> 
 *     </tr>
 *     </tbody>
 * </table>
 

 
 其中 Dim 是表示暗淡,即半亮的意思,这时我们选择 SCREEN_BRIGHT_WAKE_LOCK ,即 CPU  开 和 SCREEN 全亮 Keyboard 关

 来自 Google 开发者的提醒: 
 Device battery life will be significantly affected by the use of this API.
 Do not acquire WakeLocks unless you really need them, use the minimum levels possible,
 and be sure to release it as soon as you can.
 
 在 resetScreenOn(), keepScreenOn(), keepScreenOnAwhile() 及 case CLEAR_SCREEN_DELAY 下面的代码中,
 将 addFlags 处改为wl.acquire(),clearFlags 处改为wl.release() 即可最终这个问题就这样通过换锁解决了。 
 
 亦可参考:http://android6.blog.51cto.com/2035380/382792

 

续:换锁以后,又出现了诸如退出照相机以后,到待机界面不能进锁屏等严重问题。后来又回退了该修改。

再继续分析原来的问题,分析 log

Line 112274: 09-28 10:33:19.114  1046  1046 I videocamera: actualNextUpdateDelay: 905
 Line 112483: 09-28 10:33:20.026  1046  1046 E videocamera: -------------updateRecordingTime----------mCaptureTimeLapse= false
 Line 112484: 09-28 10:33:20.027  1046  1046 I videocamera: actualNextUpdateDelay: 982
 Line 112694: 09-28 10:33:21.010  1046  1046 E videocamera: -------------updateRecordingTime----------mCaptureTimeLapse= false
 Line 112695: 09-28 10:33:21.011  1046  1046 I videocamera: actualNextUpdateDelay: 999
 Line 112914: 09-28 10:33:22.010  1046  1046 E videocamera: -------------updateRecordingTime----------mCaptureTimeLapse= false
 Line 112915: 09-28 10:33:22.011  1046  1046 I videocamera: actualNextUpdateDelay: 998
 Line 113185: 09-28 10:33:23.008  1046  1046 E videocamera: -------------updateRecordingTime----------mCaptureTimeLapse= false
 Line 113186: 09-28 10:33:23.009  1046  1046 I videocamera: actualNextUpdateDelay: 1000
  Line 113444: 09-28 10:33:23.571  1046  1046 V videocamera: keepScreenOnAwhile
 Line 113445: 09-28 10:33:23.571  1046  1046 V videocamera: fulin resetScreenOn addFlags FLAG_KEEP_SCREEN_ON
 Line 113446: 09-28 10:33:23.571  1046  1046 W videocamera: >> onUserInteraction >> removeMessages EXIT_VIDEOCAMERA when go to gallery
 Line 113446: 09-28 10:33:23.571  1046  1046 W videocamera: >> onUserInteraction >> removeMessages EXIT_VIDEOCAMERA when go to gallery
 Line 113447: 09-28 10:33:23.572  1046  1046 V videocamera: onTouch
 Line 113454: 09-28 10:33:23.575   298   507 V WindowManager: fulin obscured false w.mSurface Surface(name=com.android.camera/com.android.camera.VideoCamera, identity=109) attrFlags 16844160 w Window{421399d8 com.android.camera/com.android.camera.VideoCamera
paused=false}
 Line 113454: 09-28 10:33:23.575   298   507 V WindowManager: fulin obscured false w.mSurface Surface(name=com.android.camera/com.android.camera.VideoCamera, identity=109) attrFlags 16844160 w Window{421399d8 com.android.camera/com.android.camera.VideoCamera
paused=false}
 Line 113462: 09-28 10:33:23.577   298   507 V WindowManager: Update reported visibility: AppWindowToken{422ab530 token=Token{41a8b198 ActivityRecord{41a2f8b0 com.android.camera/.VideoCamera}}}
 Line 113463: 09-28 10:33:23.577   298   507 V WindowManager: Win Window{421399d8 com.android.camera/com.android.camera.VideoCamera paused=false}: isDrawn=true, isAnimating=false
 Line 113494: 09-28 10:33:23.593   298   507 V WindowManager: VIS AppWindowToken{422ab530 token=Token{41a8b198 ActivityRecord{41a2f8b0 com.android.camera/.VideoCamera}}}: interesting=2 visible=2
 Line 113586: 09-28 10:33:24.056  1046  1046 E videocamera: -------------updateRecordingTime----------mCaptureTimeLapse= false
 Line 113587: 09-28 10:33:24.058  1046  1046 I videocamera: actualNextUpdateDelay: 952
 Line 113801: 09-28 10:33:25.022  1046  1046 E videocamera: -------------updateRecordingTime----------mCaptureTimeLapse= false
 Line 113805: 09-28 10:33:25.025  1046  1046 I videocamera: actualNextUpdateDelay: 987
 Line 113998: 09-28 10:33:26.012  1046  1046 E videocamera: -------------updateRecordingTime----------mCaptureTimeLapse= false
 Line 113999: 09-28 10:33:26.013  1046  1046 I videocamera: actualNextUpdateDelay: 996
 Line 114255: 09-28 10:33:27.073  1046  1046 E videocamera: -------------updateRecordingTime----------mCaptureTimeLapse= false
 Line 114275: 09-28 10:33:27.089  1046  1046 I videocamera: actualNextUpdateDelay: 936
 Line 114484: 09-28 10:33:28.025  1046  1046 E videocamera: -------------updateRecordingTime----------mCaptureTimeLapse= false
 Line 114485: 09-28 10:33:28.026  1046  1046 I videocamera: actualNextUpdateDelay: 983

  Line 7573: 09-28 10:35:22.018  1046  1046 E videocamera: -------------updateRecordingTime----------mCaptureTimeLapse= false
 Line 7574: 09-28 10:35:22.018  1046  1046 I videocamera: actualNextUpdateDelay: 991
 Line 7794: 09-28 10:35:23.033  1046  1046 E videocamera: -------------updateRecordingTime----------mCaptureTimeLapse= false
 Line 7796: 09-28 10:35:23.060  1046  1046 I videocamera: actualNextUpdateDelay: 976
 Line 7912: 09-28 10:35:23.572  1046  1046 V videocamera: fulin handleMessage clearFlags FLAG_KEEP_SCREEN_ON
 Line 7912: 09-28 10:35:23.572  1046  1046 V videocamera: fulin handleMessage clearFlags FLAG_KEEP_SCREEN_ON

分析此 log 发现,用户在录相的过程中点了屏幕,执行了 onUserInteraction 函数。而在此函数中又调用了 keepScreenOnAwhile 函数,进而开启了一个延时去清除 FLAG_KEEP_SCREEN_ON 该 flag,最终导致摄像过程中灭屏锁屏。

原因终于分析清楚了。

修改方法:

    @Override
    public void onUserInteraction() {
        super.onUserInteraction();
        Log.v(TAG,TAG + "\t call onUserInteraction isRecording = "+mMediaRecorderRecording);
        if(!isRecording()){
	   keepScreenOnAwhile();		
        }
        if (!mMediaRecorderRecording && !mGoToGallery){
            exitVideoCameraAwhile();
        } else {
            if (mHandler != null) {
                Log.w(TAG, ">> onUserInteraction >> removeMessages EXIT_VIDEOCAMERA when go to gallery");
                mHandler.removeMessages(EXIT_VIDEOCAMERA);
            }
        }
    }

就是在 onUserInteraction() 函数调用 keepScreenOnAwhile() 之前添加一个判断条件,如果是正在录像的话,那么就不执行此函数了。

 

【上篇】
【下篇】

抱歉!评论已关闭.