热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android6.0屏幕固定功能详解

android6.0在设置->安全->屏幕固定开启后,然后再长按home键出现最近的几个Activity可以选择一个图钉按钮就开启了屏幕固定功能。这篇文章主要介绍了Android6.0屏幕固定功能的相关资料,感兴趣的朋友一起看看吧

可能大家看到这个标题不知道是什么东西,我先说明下,android6.0在设置->安全->屏幕固定开启后,然后再长按home键出现最近的几个Activity可以选择一个图钉按钮就开启了屏幕固定功能。

屏幕固定开启后,屏幕只能固定在设定的Task上的Activity切换。

一、设置固定屏幕

我们先来看SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java的代码,这段代码就是长按home键出现几个Activity,然后按了图钉的那个按钮。在这里直接调用了AMS的startLockTaskModeOnCurrent函数。

@Override 
public void onClick(View v) { 
if (v.getId() == R.id.screen_pinning_ok_button || mRequestWindow == v) { 
try { 
ActivityManagerNative.getDefault().startLockTaskModeOnCurrent(); 
} catch (RemoteException e) {} 
} 
clearPrompt(); 
}

我们来看AMS的startLockTaskModeOnCurrent函数,先调用ActivityStackSupervisor的topRunningActivityLocked获取最前面的Activity,然后调用startLockTaskModeLocked函数,参数是TaskRecord。

public void startLockTaskModeOnCurrent() throws RemoteException { 
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, 
"startLockTaskModeOnCurrent"); 
long ident = Binder.clearCallingIdentity(); 
try { 
synchronized (this) { 
ActivityRecord r = mStackSupervisor.topRunningActivityLocked(); 
if (r != null) { 
startLockTaskModeLocked(r.task); 
} 
} 
} finally { 
Binder.restoreCallingIdentity(ident); 
} 
}

我们再来看topRunningActivityLocked函数,先从mFocusedStack中获取最前面的Activity。如果没有再遍历所有的mStacks获取。

ActivityRecord topRunningActivityLocked() { 
final ActivityStack focusedStack = mFocusedStack; 
ActivityRecord r = focusedStack.topRunningActivityLocked(null); 
if (r != null) { 
return r; 
} 
// Return to the home stack. 
final ArrayList stacks = mHomeStack.mStacks; 
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { 
final ActivityStack stack = stacks.get(stackNdx); 
if (stack != focusedStack && isFrontStack(stack)) { 
r = stack.topRunningActivityLocked(null); 
if (r != null) { 
return r; 
} 
} 
} 
return null; 
}

在startLockTaskModeLocked函数中主要是调用了ActivityStackSupervisor的setLockTaskModeLocked函数,下面我们来看这个函数,我们的task不为null,第一次mLockTaskModeTasks为空,会发送一个LOCK_TASK_START_MSG消息

void setLockTaskModeLocked(TaskRecord task, int lockTaskModeState, String reason, 
boolean andResume) { 
if (task == null) { 
// Take out of lock task mode if necessary 
final TaskRecord lockedTask = getLockedTaskLocked(); 
if (lockedTask != null) { 
removeLockedTaskLocked(lockedTask); 
if (!mLockTaskModeTasks.isEmpty()) { 
// There are locked tasks remaining, can only finish this task, not unlock it. 
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, 
"setLockTaskModeLocked: Tasks remaining, can't unlock"); 
lockedTask.performClearTaskLocked(); 
resumeTopActivitiesLocked(); 
return; 
} 
} 
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, 
"setLockTaskModeLocked: No tasks to unlock. Callers=" + Debug.getCallers(4)); 
return; 
} 
// Should have already been checked, but do it again. 
if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) { 
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, 
"setLockTaskModeLocked: Can't lock due to auth"); 
return; 
} 
if (isLockTaskModeViolation(task)) { 
Slog.e(TAG_LOCKTASK, "setLockTaskMode: Attempt to start an unauthorized lock task."); 
return; 
} 
if (mLockTaskModeTasks.isEmpty()) { 
// First locktask. 
final Message lockTaskMsg = Message.obtain(); 
lockTaskMsg.obj = task.intent.getComponent().getPackageName(); 
lockTaskMsg.arg1 = task.userId; 
lockTaskMsg.what = LOCK_TASK_START_MSG;//发送消息 
lockTaskMsg.arg2 = lockTaskModeState; 
mHandler.sendMessage(lockTaskMsg); 
} 
// Add it or move it to the top. 
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskModeLocked: Locking to " + task + 
" Callers=" + Debug.getCallers(4)); 
mLockTaskModeTasks.remove(task); 
mLockTaskModeTasks.add(task);//加入到mLockModeTasks中 
if (task.mLockTaskUid == -1) { 
task.mLockTaskUid = task.effectiveUid; 
} 
if (andResume) { 
findTaskToMoveToFrontLocked(task, 0, null, reason);//把task放最前面 
resumeTopActivitiesLocked();//显示新的Activity 
} 
}

我们再来看消息处理,在消息处理中主要调用了WMS的disableKeyguard函数。

case LOCK_TASK_START_MSG: { 
// When lock task starts, we disable the status bars. 
try { 
if (mLockTaskNotify == null) { 
mLockTaskNotify = new LockTaskNotify(mService.mContext); 
} 
mLockTaskNotify.show(true); 
mLockTaskModeState = msg.arg2; 
if (getStatusBarService() != null) { 
int flags = 0; 
if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) { 
flags = StatusBarManager.DISABLE_MASK 
& (~StatusBarManager.DISABLE_BACK); 
} else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) { 
flags = StatusBarManager.DISABLE_MASK 
& (~StatusBarManager.DISABLE_BACK) 
& (~StatusBarManager.DISABLE_HOME) 
& (~StatusBarManager.DISABLE_RECENT); 
} 
getStatusBarService().disable(flags, mToken, 
mService.mContext.getPackageName()); 
} 
mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG); 
if (getDevicePolicyManager() != null) { 
getDevicePolicyManager().notifyLockTaskModeChanged(true, 
(String)msg.obj, msg.arg1); 
} 
} catch (RemoteException ex) { 
throw new RuntimeException(ex); 
} 
} break;

二、固定屏幕后Activity启动流程

在固定屏幕后,如果我们启动其他TaskRecord的Activity是不能启动的,我们来看下这个原理。在startActivityUncheckedLocked函数中会调用isLockTaskModeViolation函数来判断是否进一步的Activity的启动流程,我们来看下这个函数,调用getLockedTaskLocked来看mLockTaskModeTasks(就是锁定屏幕的那些Task),如果当前的task就是当前正在固定屏幕的task,直接return false就是可以继续启动Activity的流程,而如果不是,我们需要看task的mLockTaskAuth变量。

boolean isLockTaskModeViolation(TaskRecord task, boolean isNewClearTask) { 
if (getLockedTaskLocked() == task && !isNewClearTask) { 
return false; 
} 
final int lockTaskAuth = task.mLockTaskAuth; 
switch (lockTaskAuth) { 
case LOCK_TASK_AUTH_DONT_LOCK: 
return !mLockTaskModeTasks.isEmpty(); 
case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: 
case LOCK_TASK_AUTH_LAUNCHABLE: 
case LOCK_TASK_AUTH_WHITELISTED: 
return false; 
case LOCK_TASK_AUTH_PINNABLE: 
// Pinnable tasks can't be launched on top of locktask tasks. 
return !mLockTaskModeTasks.isEmpty(); 
default: 
Slog.w(TAG, "isLockTaskModeViolation: invalid lockTaskAuth value=" + lockTaskAuth); 
return true; 
} 
}

我们再来看TaskRecord的setLockedTaskAuth函数,在新建一个TaskRecord的时候会调用setIntent函数,而setIntent函数又是在TaskRecord的构造函数中调用的。我们来看这个函数mLockTaskAuth的值是根据mLockTaskMode来定的,而mLockTaskMode又是ActivityInfo传入的,这个值是在PKMS解析AndroidManifest.xml的时候构造的,默认就是LOCK_TASK_LAUNCH_MODE_DEFAULT,而当没有白名单mLockTaskAuth最后就是LOCK_TASK_AUTH_PINNABLE。

void setLockTaskAuth() { 
if (!mPrivileged && 
(mLockTaskMode == LOCK_TASK_LAUNCH_MODE_ALWAYS || 
mLockTaskMode == LOCK_TASK_LAUNCH_MODE_NEVER)) { 
// Non-priv apps are not allowed to use always or never, fall back to default 
mLockTaskMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; 
} 
switch (mLockTaskMode) { 
case LOCK_TASK_LAUNCH_MODE_DEFAULT: 
mLockTaskAuth = isLockTaskWhitelistedLocked() ? 
LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE; 
break; 
case LOCK_TASK_LAUNCH_MODE_NEVER: 
mLockTaskAuth = LOCK_TASK_AUTH_DONT_LOCK; 
break; 
case LOCK_TASK_LAUNCH_MODE_ALWAYS: 
mLockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV; 
break; 
case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED: 
mLockTaskAuth = isLockTaskWhitelistedLocked() ? 
LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE; 
break; 
} 
if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this + 
" mLockTaskAuth=" + lockTaskAuthToString()); 
}

我们再来看isLockTaskModeViolation函数如下代码,现在是task的mLockTaskAuth 是LOCK_TASK_AUTH_PINNABLE,而当前处于固定屏幕,所以mLockTaskModeTasks不为null,最后返回true。那Activity启动流程就不能走下去了,那就是代表启动普通的Activity会被阻止。

case LOCK_TASK_AUTH_PINNABLE: 
// Pinnable tasks can't be launched on top of locktask tasks. 
return !mLockTaskModeTasks.isEmpty();

三、取消固定屏幕

最后我们再来看看取消固定屏幕,取消屏幕会在PhoneStatusBar中取消,但是一定是要有虚拟键,原生就是这么设定的。最后调用了AMS的stopLockTaskModeOnCurrent函数。这个函数主要是调用了stopLockTaskMode函数,这个函数中主要是调用了ActivityStackSupervisor的setLockTaskModeLocked函数,之前在固定屏幕时也是调用了这个函数,但是这里我们仔细看,其第一个参数为null。

public void stopLockTaskMode() { 
final TaskRecord lockTask = mStackSupervisor.getLockedTaskLocked(); 
if (lockTask == null) { 
// Our work here is done. 
return; 
} 
final int callingUid = Binder.getCallingUid(); 
final int lockTaskUid = lockTask.mLockTaskUid; 
// Ensure the same caller for startLockTaskMode and stopLockTaskMode. 
// It is possible lockTaskMode was started by the system process because 
// android:lockTaskMode is set to a locking value in the application manifest instead of 
// the app calling startLockTaskMode. In this case {@link TaskRecord.mLockTaskUid} will 
// be 0, so we compare the callingUid to the {@link TaskRecord.effectiveUid} instead. 
if (getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_LOCKED && 
callingUid != lockTaskUid 
&& (lockTaskUid != 0 
|| (lockTaskUid == 0 && callingUid != lockTask.effectiveUid))) { 
throw new SecurityException("Invalid uid, expected " + lockTaskUid 
+ " callingUid=" + callingUid + " effectiveUid=" + lockTask.effectiveUid); 
} 
long ident = Binder.clearCallingIdentity(); 
try { 
Log.d(TAG, "stopLockTaskMode"); 
// Stop lock task 
synchronized (this) { 
mStackSupervisor.setLockTaskModeLocked(null, ActivityManager.LOCK_TASK_MODE_NONE, 
"stopLockTask", true); 
} 
} finally { 
Binder.restoreCallingIdentity(ident); 
} 
}

我们来看下这个函数,如果为空,现在调用getLockedTaskLocked获取当前固定屏幕的TaskRecord,然后调用removeLockedTaskLocked去除这个TaskRecord,如果还不为null,调用resumeTopActivitiesLocked启动下个Activity(一般也就是下个屏幕锁定的TaskRecord的Activity)。

如果为空了,直接返回。但是在我们下次启动普通的Activity的时候就恢复正常了,因为mLockTaskModeTasks已经为空了。

void setLockTaskModeLocked(TaskRecord task, int lockTaskModeState, String reason, 
boolean andResume) { 
if (task == null) { 
// Take out of lock task mode if necessary 
final TaskRecord lockedTask = getLockedTaskLocked(); 
if (lockedTask != null) { 
removeLockedTaskLocked(lockedTask); 
if (!mLockTaskModeTasks.isEmpty()) { 
// There are locked tasks remaining, can only finish this task, not unlock it. 
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, 
"setLockTaskModeLocked: Tasks remaining, can't unlock"); 
lockedTask.performClearTaskLocked(); 
resumeTopActivitiesLocked(); 
return; 
} 
} 
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, 
"setLockTaskModeLocked: No tasks to unlock. Callers=" + Debug.getCallers(4)); 
return; 
}

四、没有虚拟键如何取消屏幕固定

前面说过如果没有虚拟键就不能取消屏幕固定了,我们说下几种方式

1.使用am命令 am task lock stop可以调用am的stopLockTaskMode函数

2.另一种我们可以在Activity.java中修改代码,比较长按返回键调用AMS的stopLockTaskMode方法,下面就是实现,Activity本身提供了stopLockTask就是调用了AMS的stopLockTaskMode方法

public boolean onKeyLongPress(int keyCode, KeyEvent event) { 
if (keyCode == KeyEvent.KEYCODE_BACK) { 
stopLockTask(); 
} 
return false; 
}

以上所述是小编给大家介绍的Android6.0 屏幕固定功能详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!


推荐阅读
  • 吴石访谈:腾讯安全科恩实验室如何引领物联网安全研究
    腾讯安全科恩实验室曾两次成功破解特斯拉自动驾驶系统,并远程控制汽车,展示了其在汽车安全领域的强大实力。近日,该实验室负责人吴石接受了InfoQ的专访,详细介绍了团队未来的重点方向——物联网安全。 ... [详细]
  • 在Notepad++中配置Markdown语法高亮及实时预览功能
    本文详细介绍了如何在Notepad++中配置Markdown语法高亮和实时预览功能,包括必要的插件安装和设置步骤。 ... [详细]
  • Android 中的布局方式之线性布局
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 处理Android EditText中数字输入与parseInt方法
    本文探讨了如何在Android应用中从EditText组件安全地获取并解析用户输入的数字,特别是用于设置端口号的情况。通过示例代码和异常处理策略,展示了有效的方法来避免因非法输入导致的应用崩溃。 ... [详细]
  • 2023年,Android开发前景如何?25岁还能转行吗?
    近期,关于Android开发行业的讨论在多个平台上热度不减,许多人担忧其未来发展。本文将探讨当前Android开发市场的现状、薪资水平及职业选择建议。 ... [详细]
  • 使用TabActivity实现Android顶部选项卡功能
    本文介绍如何通过继承TabActivity来创建Android应用中的顶部选项卡。通过简单的步骤,您可以轻松地添加多个选项卡,并实现基本的界面切换功能。 ... [详细]
  • 在日常生活中,支付宝已成为不可或缺的支付工具之一。本文将详细介绍如何通过支付宝实现免费提现,帮助用户更好地管理个人财务,避免不必要的手续费支出。 ... [详细]
  • 本文探讨了如何通过Service Locator模式来简化和优化在B/S架构中的服务命名访问,特别是对于需要频繁访问的服务,如JNDI和XMLNS。该模式通过缓存机制减少了重复查找的成本,并提供了对多种服务的统一访问接口。 ... [详细]
  • Windows操作系统提供了Encrypting File System (EFS)作为内置的数据加密工具,特别适用于对NTFS分区上的文件和文件夹进行加密处理。本文将详细介绍如何使用EFS加密文件夹,以及加密过程中的注意事项。 ... [详细]
  • Android与JUnit集成测试实践
    本文探讨了如何在Android项目中集成JUnit进行单元测试,并详细介绍了修改AndroidManifest.xml文件以支持测试的方法。 ... [详细]
  • 本文探讨了如何将Python对象转换为字节流,以实现文件保存、数据库存储或网络传输的需求。主要介绍了利用pickle模块进行序列化的具体方法。 ... [详细]
  • 本文探讨了如何在PHP与MySQL环境中实现高效的分页查询,包括基本的分页实现、性能优化技巧以及高级的分页策略。 ... [详细]
  • Maven + Spring + MyBatis + MySQL 环境搭建与实例解析
    本文详细介绍如何使用MySQL数据库进行环境搭建,包括创建数据库表并插入示例数据。随后,逐步指导如何配置Maven项目,整合Spring框架与MyBatis,实现高效的数据访问。 ... [详细]
  • 本文探讨了如何通过优化 DOM 操作来提升 JavaScript 的性能,包括使用 `createElement` 函数、动画元素、理解重绘事件及处理鼠标滚动事件等关键主题。 ... [详细]
  • 本文介绍如何在阿里云环境中利用 Docker 容器化技术部署一个简单的 Flask Web 应用,并确保其可通过互联网访问。内容涵盖 Python 代码编写、Dockerfile 配置、镜像构建及容器运行等步骤。 ... [详细]
author-avatar
安静2502917547
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有