在Android的辅助功能中,存在一个点击三次屏幕触发屏幕放大功能。
辅助功能中打开
放大后效果
这个功能的使用频率实在是低...但是为什么会想记录一下这个功能的实现原理。第一,在处理性能问题的时候遇到了相关代码;其次其实现的原理还是具有部分启发性质的。主要还是研究启发部分:
1、如何实现手势拦截
2、全局放大的原理(主要在system_server中存在双编舞者协作实现),如下图所示在启动手势放大过程中systrace抓取到下面的现象:
两个编舞者协同工作
一、手势拦截
在设置中打开放大手势的开关,会设置Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED这个属性值,AccessiblityContentObserver中的onChange会处理这个值的变化:
@Override
4456 public void onChange(boolean selfChange, Uri uri) {
4457 synchronized (mLock) {
4458 // Profiles share the accessibility state of the parent. Therefore,
4459 // we are checking for changes only the parent settings.
4460 UserState userState = getCurrentUserStateLocked();
4461
4462 // If the automation service is suppressing, we will update when it dies.
4463 if (userState.isUiAutomationSuppressingOtherServices()) {
4464 return;
4465 }
4466
4467 if (mTouchExplorationEnabledUri.equals(uri)) {
4468 if (readTouchExplorationEnabledSettingLocked(userState)) {
4469 onUserStateChangedLocked(userState);
4470 }
4471 } else if (mDisplayMagnificationEnabledUri.equals(uri)) {
4472 if (readDisplayMagnificationEnabledSettingLocked(userState)) {
4479 onUserStateChangedLocked(userState);
4480 }
4481 }
在onUserStateChangedLocked中会调用updateMagnificationLocked以及scheduleUpdateInputFilter去更新当前系统状态:
updateMagnificationLocked是用于建立wms和辅助功能服务的联系
scheduleUpdateInputFilter是用于在输入层面建立手势拦截,往Input流程中加入inputfilter
1820 private void updateMagnificationLocked(UserState userState) {
1821 if (userState.mUserId != mCurrentUserId) {
1822 return;
1823 }
1824
1825 if (userState.mIsDisplayMagnificationEnabled ||
1826 userHasListeningMagnificationServicesLocked(userState)) {
1827 // Initialize the magnification controller if necessary
1828 getMagnificationController();
//核心在于放大控制器的注册
1829 mMagnificationController.register();
1830 } else if (mMagnificationController != null) {
//当关闭此功能的时候会调用反注册
1831 mMagnificationController.unregister();
1832 }
1833 }
实际上就是通过MagnificationController去注册。
55/**
56 * This class is used to control and query the state of display magnification
57 * from the accessibility manager and related classes. It is responsible for
58 * holding the current state of magnification and animation, and it handles
59 * communication between the accessibility manager and window manager.
60 */
61class MagnificationController
从对这个类的描述可以看出,它是为了控制和查询当前屏幕的放大状态;其次用于辅助服务和WMS之间的通信工作。这些具体的含义还是放到代码中去一一解释。
首先看看他的register函数:
public void register() {
130 synchronized (mLock) {
131 if (!mRegistered) {
//step1、注册广播监听亮灭屏事件
132 mScreenStateObserver.register();
//step2、注册WMS中的回调(与WMS之间通信)
133 mWindowStateObserver.register();
//step3、使能跟动画相关的函数(虽然这个类名字有点奇怪,但还是能猜到是跟动画相关的)
134 mSpecAnimationBridge.setEnabled(true);
135 // Obtain initial state.
136 mWindowStateObserver.getMagnificationRegion(mMagnificationRegion);
137 mMagnificationRegion.getBounds(mMagnificationBounds);
138 mRegistered = true;
139 }
140 }
141 }
step1就略过从step2开始看它是如何跟wms进行交互的。
/**
957 * This class handles the screen magnification when accessibility is enabled.
958 */
959 private static class WindowStateObserver
960 implements WindowManagerInternal.MagnificationCallbacks {
......
975
976 public WindowStateObserver(Context context, MagnificationController controller) {
977 mController = controller;
978 mWindowManager = LocalServices.getService(WindowManagerInternal.class);
979 mHandler = new CallbackHandler(context);
980 }
981
982 public void register() {
987 mWindowManager.setMagnificationCallbacks(this);
990 }
991
WindowStateObserver实现了接口MagnificationCallbacks,这个接口是wms用于通知放大控制器当前wms端有了哪些变化的:
/**
51 * Callbacks for contextual changes that affect the screen magnification
52 * feature.
53 */
54 public interface MagnificationCallbacks {
55
56 /**
57 * Called when the region where magnification operates changes. Note that this isn't the
58 * entire screen. For example, IMEs are not magnified.
*这种情况在放大的情况下点开了输入法,输入法界面是不能够被放大的,但是由于其占用了一定的屏幕空间,就会导致放大的区域变小,wms就会回调注册的该方法
59 *
60 * @param magnificationRegion the current magnification region
61 */
62 public void onMagnificationRegionChanged(Region magnificationRegion);
63
64 /**
65 * Called when an application requests a rectangle on the screen to allow
66 * the client to apply the appropriate pan and scale.
67 *
68 * @param left The rectangle left.
69 * @param top The rectangle top.
70 * @param right The rectangle right.
71 * @param bottom The rectangle bottom.
72 */
73 public void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
74
75 /**
76 * Notifies that the rotation changed.
77 *
78 * @param rotation The current rotation.
79 */
80 public void onRotationChanged(int rotation);
81
82 /**
83 * Notifies that the context of the user changed. For example, an application
84 * was started.
*context发生变化(个人理解为当前Activity发生了切换)
85 */
86 public void onUserContextChanged();
87 }
通过注册WindowStateObserver到WMS,就建立wms和AccessibilityMS的沟通了。
回到前面的step3,使能SpecAnimationBridge,从下面这个类的注释可以看出它有两个功能
/**
727 * Class responsible for animating spec on the main thread and sending spec
728 * updates to the window manager.
729 */
730 private static class SpecAnimationBridge {
1:将放大相关的参数发送给wms
<