在适配Android 7.0时,出现了在锁屏(灭屏&加锁)状态下,发起视频呼叫,界面显示为半屏的问题。
在亮屏时,显示是正常的,没问题。对比两次的日志,初步有如下分析。
APP安装成功后,第一次拉视频在亮屏或锁屏下,有以下结果。
@1. 亮屏。 第一次拉视频时亮屏,打印MDSVideoTrafficActivity(24284): Configuration().orientation==2,显示正常;
结束掉当前呼叫,锁屏后,再次拉视频,打印 MDSVideoTrafficActivity(24284): Configuration().orientation==2,显示正常;
部分日志如下:
Line 6522: 04-04 09:47:01.718 D/MDSVideoTrafficActivity(24284): screen is on true
Line 7124: 04-04 09:47:01.904 D/MDSVideoTrafficActivity(24284): SCREEN_ORIENTATION_LANDSCAPE----widthScreen--800---heightScreen---480
Line 8022: 04-04 09:47:02.050 D/MDSVideoTrafficActivity(24284): Configuration().orientation==2
....
Line 25155: 04-04 09:47:11.953 D/MDSVideoTrafficActivity(24284): screen is on false
Line 25652: 04-04 09:47:12.214 D/MDSVideoTrafficActivity(24284): SCREEN_ORIENTATION_LANDSCAPE----widthScreen--800---heightScreen---480
Line 26109: 04-04 09:47:12.443 D/MDSVideoTrafficActivity(24284): Configuration().orientation==2
结论: 第一次拉视频在亮屏模式下是正常的,之后锁屏再次拉视频也是正常的。
@2. 锁屏。 第一次拉视频时锁屏,打印MDSVideoTrafficActivity(24552): Configuration().orientation==1,显示半屏;
结束掉当前呼叫,锁屏后,再次拉视频,仍显示半屏。
部分日志如下:
Line 8369: 04-04 09:48:01.831 D/MDSVideoTrafficActivity(24552): screen is on false
Line 9310: 04-04 09:48:02.362 D/MDSVideoTrafficActivity(24552): ORIENTATION_PORTRAIT----widthScreen--800---heightScreen---480
Line 9585: 04-04 09:48:02.485 D/MDSVideoTrafficActivity(24552): Configuration().orientation==1
....
Line 32198: 04-04 09:48:13.519 D/MDSVideoTrafficActivity(24552): screen is on false
Line 32539: 04-04 09:48:13.731 D/MDSVideoTrafficActivity(24552): ORIENTATION_PORTRAIT----widthScreen--800---heightScreen---480
Line 33433: 04-04 09:48:14.074 D/MDSVideoTrafficActivity(24552): Configuration().orientation==1
结论: 第一次拉视频时锁屏,则显示为半屏,之后锁屏再次拉视频也是显示半屏。
初步分析,是在锁屏时,获取系统方向值Configuration().orientation==1(竖屏),在亮屏时,获取系统方向Configuration().orientation==2(横屏)。但请教了Android系统侧的同事,并没有得到很好的解决,但提供了一个好的建议给我。
系统侧同事提供的建议是:设置屏幕方向的方法setRequestedOrientation被系统过滤掉了,可能是因为设置时屏幕并没有解锁或没有亮屏。所以需要在视频呼叫到来的时候,先亮屏并解锁,然后等解锁的回调过来后,再设置屏幕方向。
参考代码如下:
a. 亮屏、解锁方法参考:
public void acquireScreenOn() {
if(null == mPM){
mPM = (PowerManager) getSystemService(Context.POWER_SERVICE);
}
boolean isScreenOn = mPM.isScreenOn();
Log.d(TAG, " screen is on " + isScreenOn);
// if (!isScreenOn) {
wl = mPM.newWakeLock(
PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
"bright");
wl.acquire(10000);
Log.d(TAG, " acquire ");
// }
mTrafficWindowHandler.sendEmptyMessageDelayed(DELAY_RELEASE, 1000);
}
public boolean isScreenOn(){
if(null != mPM){
return mPM.isScreenOn();
}
return false;
}
public void releaseScreenOn() {
if (wl != null && wl.isHeld()) {
wl.release();
Log.d(TAG, " release ");
}
}
private void disableKeyguard(){
Log.d(TAG, "disableKeyguard()");
if(mKeyguardManager==null){
mKeyguardManager = (KeyguardManager) this.getSystemService(Context.KEYGUARD_SERVICE);
}
if(mKeyguardLock == null ){
mKeyguardLock = mKeyguardManager.newKeyguardLock("unLock");
}
// if( mKeyguardManager.inKeyguardRestrictedInputMode() ){
mKeyguardLock.disableKeyguard();
Log.d(TAG, "mKeyLock.disableKeyguard()");
// }
}
private void reenableKeyguard(){
if(mKeyguardLock!=null){
mKeyguardLock.reenableKeyguard();
}
}
b. 注册屏幕解锁的监听:
mDeviceScreenListener.register(new ScreenStateListener() {
@Override
public void onUserPresent() {
Log.d(TAG, "--onUserPresent--isLandScape:"+isLandScape);
if(isLandScape){
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
Log.d(TAG, "--onUserPresent--setRequestedOrientation --");
}
}
@Override
public void onScreenOn() {
Log.d(TAG, "--onScreenOn");
}
@Override
public void onScreenOff() {
Log.d(TAG, "--onScreenOff");
}
});
c. 屏幕状态的监听封装
public class DeviceScreenListener {
private Context mContext;
private ScreenBroadcastReceiver mScreenBroadcastReceiver;
private ScreenStateListener mScreenStateListener;
public DeviceScreenListener(Context context) {
mContext = context;
mScreenBroadcastReceiver = new ScreenBroadcastReceiver();
}
/**
* 设备屏幕状态广播接收者
*/
private class ScreenBroadcastReceiver extends BroadcastReceiver {
private String action = null;
@Override
public void onReceive(Context context, Intent intent) {
action = intent.getAction();
if (Intent.ACTION_SCREEN_ON.equals(action)) {
/**
* 屏幕亮
*/
mScreenStateListener.onScreenOn();
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
/**
* 屏幕锁定
*/
mScreenStateListener.onScreenOff();
} else if (Intent.ACTION_USER_PRESENT.equals(action)) {
/**
* 屏幕解锁了且可以使用
*/
mScreenStateListener.onUserPresent();
}
}
}
/**
* 开始监听屏幕开/关状态
*
* @param listener
*/
public void register(ScreenStateListener listener) {
mScreenStateListener = listener;
/**
* 注册屏幕设备开屏/锁屏的状态监听
*/
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
mContext.registerReceiver(mScreenBroadcastReceiver, filter);
//initScreenState(); //可选
}
/**
* 代码启动阶段获取设备屏幕初始状态
*/
/**
private void initScreenState() {
PowerManager manager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
if (manager.isScreenOn()) {
if (mScreenStateListener != null) {
mScreenStateListener.onScreenOn();
}
} else {
if (mScreenStateListener != null) {
mScreenStateListener.onScreenOff();
}
}
}
*/
/**
* 注销屏幕设备开屏/锁屏的状态监听
*/
public void unregister() {
mContext.unregisterReceiver(mScreenBroadcastReceiver);
mScreenBroadcastReceiver = null;
mScreenStateListener = null;
}
interface ScreenStateListener {
/**
* 此时屏幕已经点亮,但可能是在锁屏状态
* 比如用户之前锁定了屏幕,按了电源键启动屏幕,则回调此方法
*/
void onScreenOn();
/**
* 屏幕被锁定
*/
void onScreenOff();
/**
* 屏幕解锁且可以正常使用
*/
void onUserPresent();
}
}
经过验证,发现在Android 7.0 锁屏模式下,进行视频呼叫,需要先亮屏(acquireScreenOn),再解锁(disableKeyguard),注意其他地方不要重复调用(disableKeyguard),这样会导致ACTION_USER_PRESENT广播在当前的Activity中收不到。(我就曾经被这个坑,坑了很久)。
另外,在Activity的onCreate方法中,先执行亮屏acquireScreenOn方法,之后延迟1秒钟,执行解锁disableKeyguard方法,然后在解锁的回调中设置屏幕方向。就是先点亮屏幕,之后再解锁,最后进入视频界面,这样保证系统有足够的反应时间,确保我们调用了setRequestedOrientation,系统可以按照我们设置的方向来绘制。
在视频呼叫结束时,要调用释放屏幕锁(releaseScreenOn),反解锁(reenableKeyguard)。
总结:
不管是在适配Android 7.0及更高的版本,都可能出现一些低版本中不存在的问题,这时最好先去网上查找资料,你遇到的问题,别人也可能遇到过并且已经解决了;就算别人没解决,也可能会提供给你一个解决问题的着入点。其次,分析问题,要有针对性,不能盲目地尝试,可能越盲目,就会越快丧失自信心。比如,可以多次测试,查找规律,我就是在多次测试下,发现第一次时锁屏与亮屏的差异等等,找到问题的规律,再根据不同表现对应不同的日志分析,慢慢就会找到突破口。