Kitkat小屏幕模式单手操作研究之wm命令的实现方式分析

第二节:wm命令的实现方式分析

 

wm命令是在Wm.java文件中实现的。我们来分析一下执行的过程:

 

通过上述的可以看出是在adb shell中调用wm命令,可以知道wm是一个可执行文件,那么对于java类来说入口方法时main()方法。所以来看Wm类的main()方法如下:

public static void main(String[] args) {

        (new Wm()).run(args);

    }


 

使用Wm类的默认构造方法创建了一个Wm的实例,然后调用run()方法继续执行,将命令行传入的参数args一并传入。

由于run()方法是从Wm的父类BaseCommand继承来的,代码如下。

下面来看run()方法的内容:

public void run(String[] args) {

      ……

            onRun();

       …….

}

 


经过一些对参数的验证,最后调用Wm实现的onRun()方法,代码如下:

public void onRun() throws Exception {

        mWm = IWindowManager.Stub.asInterface(ServiceManager.checkService(

                        Context.WINDOW_SERVICE));

        …….

        String op = nextArgRequired();

        if (op.equals("size")) {

            runDisplaySize();

        } else if (op.equals("density")) {

            runDisplayDensity();

        } else if (op.equals("overscan")) {

            runDisplayOverscan();

        } else {

            …….

        }

    }


从前面章节对wm命令的介绍,都是对屏幕的进行操作的,必然涉及到WmS的操作,所以这里要获得WmS的代理对象,赋值给mWm变量,后续的我们要调用WmS的方法,都是通过mWm来调用的。

接下来通过nextArgRequired()获得命令字符串,然后根据命令字符串,执行不同的命令。

注意:这里使用了多个if语句,其实在JDK7开始,switch的条件语句已经可以使用字符串了。

 

根据命令的不同分别调用runDisplaySize()、runDisplayDensity()、runDisplayOverscan()方法,见名知义,所以这里不赘述。我们以runDisplayOverscan()方法为例,进行分析,其他的类似,最后都是通过mWm调用WmS的某个方法,对DisplayContent进行设置。

 

下面我们上该方法的代码:

private void runDisplayOverscan() throws Exception {

        String overscanStr = nextArgRequired();

        Rect rect = new Rect();

        int density;

        if ("reset".equals(overscanStr)) {

            rect.set(0, 0, 0, 0);

        } else {

            final Pattern FLATTENED_PATTERN = Pattern.compile(

                    "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)");

            Matcher matcher = FLATTENED_PATTERN.matcher(overscanStr);

            if (!matcher.matches()) {

                System.err.println("Error: bad rectangle arg: " + overscanStr);

                return;

            }

            rect.left = Integer.parseInt(matcher.group(1));

            rect.top = Integer.parseInt(matcher.group(2));

            rect.right = Integer.parseInt(matcher.group(3));

            rect.bottom = Integer.parseInt(matcher.group(4));

        }

 

        try {

            mWm.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right, rect.bottom);

        } catch (RemoteException e) {

        }

    }


 

1. 获得命令的第一个参数,如果是resize,就意味着要恢复到原始的overscan的值,看到给rect矩形变量设置了四个0,这意味着设备原始是没有overscan值的。

 

2. 如果第一个参数不是resize,就意味着要对overscan进行设置,但是从wm overscan的参数模式可以看出是四个使用逗号(,)的整数值,可以是负整数值。这里使用正则表达式,将该参数转换成一个数组放到到matcher中,接着依次取出,分别赋值给rect的left,top,right,bottom的值。

 

3.当执行以上两步后,我们已经给rect矩形变量设置完值了,接下来就是告诉WmS,可以根据这些值,更新屏幕的overscan的值了。这里调用WmS的setOverscan()方法,把rect的四个坐标值传入。

注意:这里仅仅使用rect来存储left,top,right,bottom的值,并不代表overcan是一个矩形区域,其实overscan代表的是不绘制屏幕内容的区域。如下图中的浅橙色所示:

 Kitkat小屏幕模式单手操作研究之wm命令的实现方式分析_第1张图片

图2-1 屏幕的绘制参数示意图

4.接下来我们来看WmS中的setOverscan()的实现:

@Override

    public void setOverscan(int displayId, int left, int top, int right, int bottom) {

        if (mContext.checkCallingOrSelfPermission(

                android.Manifest.permission.WRITE_SECURE_SETTINGS) !=

                PackageManager.PERMISSION_GRANTED) {

            throw new SecurityException("Must hold permission " +

                    android.Manifest.permission.WRITE_SECURE_SETTINGS);

        }

        final long ident = Binder.clearCallingIdentity();

        try {

            synchronized(mWindowMap) {

                DisplayContent displayContent = getDisplayContentLocked(displayId);

                if (displayContent != null) {

                    setOverscanLocked(displayContent, left, top, right, bottom);

                }

            }

        } finally {

            Binder.restoreCallingIdentity(ident);

        }

    }


1 首先来检查执行方法的类是否有足够的权限,这里必须有android.Manifest.permission.WRITE_SECURE_SETTINGS的权限才能调用该方法。

2 接着就是获得DisplayContent对象,这里我们传入的displayId就是Display.DEFAULT_DISPLAY,值为0,其实对于手机来说只有一块屏幕,所以就是DEFAULT_DISPLAY。当displayContent不为null的时候,调用WmS的setOverscanLocked()私有方法继续进行设置。

3 我们来看看setOverscanLocked()方法

private void setOverscanLocked(DisplayContent displayContent,

            int left, int top, int right, int bottom) {

        final DisplayInfo displayInfo = displayContent.getDisplayInfo();

        synchronized (displayContent.mDisplaySizeLock) {

            displayInfo.overscanLeft = left;

            displayInfo.overscanTop = top;

            displayInfo.overscanRight = right;

            displayInfo.overscanBottom = bottom;

        }

 

        mDisplaySettings.setOverscanLocked(displayInfo.name, left, top, right, bottom);

        mDisplaySettings.writeSettingsLocked();

 

        reconfigureDisplayLocked(displayContent);

    }


该方法中做了三件事情:

l  调用displayContent的getDisplayInfo()方法获得DisplayInfo对象,然后根据传入的overscan的值,设置DisplayInfo中的响应的值。

l  调用mDisplaySettings的setOverscanLocked()方法,将overscan的值保存成一条名字为diaplayInfo.name的记录,通过这个名字,我们就可以从mDisplaySettings中读出该DisplayContent的overscan的内容。接着调用那个mDisplaySettings的writeSettingsLocked()方法,将刚才设置的内容写入到display_settings.xml设置文件中。

l  调用reconfigureDisplayLocked(displayContent)方法,进一步更新DisplayContent的内容,然后调用

performLayoutAndPlaceSurfacesLocked()方法对屏幕进行绘制。这个过程不再进行介绍。

你可能感兴趣的:(Android,应用开发积累,Android,framework分析)