【源码解析】Launcher 8.0源码(6)---Launcher的源码启动过程第一步之InvariantDeviceProfile获取硬件参数,确认布局参数

获取硬件参数是在InvariantDeviceProfile中进行的,确定布局参数是在DeviceProfile中进行的,本篇文章就对这个至关重要的一步进行详细分析

InvariantDeviceProfile构造方法

我们来看InvariantDeviceProfile的构造方法,构造方法一开始是获取是获取获取硬件参数
首先需要获取系统组件windowmanager、Display 、DisplayMetrics 。而后获取长和宽的px值通过屏幕密度切换成屏幕的实际物理尺寸。最终得到minWidthDps 和minHeightDps 这两个手机的实际长和宽。

WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        DisplayMetrics dm = new DisplayMetrics();
        display.getMetrics(dm);

        Point smallestSize = new Point();
        Point largestSize = new Point();
        display.getCurrentSizeRange(smallestSize, largestSize);

        // This guarantees that width < height
        minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm);
        minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm);

获取到手机的实际长宽以后就走到最关键的两个方法

ArrayList<InvariantDeviceProfile> closestProfiles = findClosestDeviceProfiles(
                minWidthDps, minHeightDps, getPredefinedDeviceProfiles(context));

        InvariantDeviceProfile interpolatedDeviceProfileOut =
                invDistWeightedInterpolate(minWidthDps,  minHeightDps, closestProfiles);

这两个方法的参数都传入了手机的实际宽高minWidthDps, minHeightDps,不同的是在findClosestDeviceProfiles方法中调用了getPredefinedDeviceProfiles(context)这个方法。
其实这个方法主要做的事情就是解析xml布局R.xml.device_profiles返回一个类型为InvariantDeviceProfile的ArrayList集合。具体的代码比较多,就不贴了。

device_profiles的xml文件

对于这个xml文件是由很多profile文件组成,其中一个.

<profile
        launcher:name="Short Stubby"
        launcher:minWidthDps="275"
        launcher:minHeightDps="420"
        launcher:numRows="3"
        launcher:numColumns="4"
        launcher:numFolderRows="3"
        launcher:numFolderColumns="4"
        launcher:minAllAppsPredictionColumns="4"
        launcher:iconSize="48"
        launcher:iconTextSize="11"
        launcher:numHotseatIcons="5"
        launcher:defaultLayoutId="@xml/default_workspace_4x4"
        />

在这个profile中有很多的属性,包括最小的宽高minWidthDps和minHeightDps,布局的行列数,文件夹的行列数,allapp的行列数,图标大小,图标名字代销,快捷栏图标大小,快捷栏图标数,以及默认布局文件。这些参数都需要在后面获取到。
在这11个profile中从
launcher:minWidthDps="255" launcher:minHeightDps="300"

launcher:minWidthDps="1527"launcher:minHeightDps="2527"
所有的设备都包含了。

findClosestDeviceProfiles()

接着我们来回过头来看一些findClosestDeviceProfiles(minWidthDps, minHeightDps, getPredefinedDeviceProfiles(context))这个方法。

 ArrayList<InvariantDeviceProfile> findClosestDeviceProfiles(
            final float width, final float height, ArrayList<InvariantDeviceProfile> points) {

        // Sort the profiles by their closeness to the dimensions
        ArrayList<InvariantDeviceProfile> pointsByNearness = points;
        Collections.sort(pointsByNearness, new Comparator<InvariantDeviceProfile>() {
            public int compare(InvariantDeviceProfile a, InvariantDeviceProfile b) {
                return Float.compare(dist(width, height, a.minWidthDps, a.minHeightDps),
                        dist(width, height, b.minWidthDps, b.minHeightDps));
            }
        });

        return pointsByNearness;
    }

points就是我们通过解析R.xml.device_profiles这个布局来获得的类型为InvariantDeviceProfile的ArrayList,赋值给pointsByNearness ,然后进行排序,排序方式是调用Collections.sort的第二种方式自定义比较器来实现的关于Collections.sort的讲解友情链接比较的。在自定义比较器的方法中采用浮点类型的比较方法Float.compare(f1,f2),此方法返回值0,则f1在数值上等于f2; 返回值小于0,f1在数值上比f2小; 返回值大于0,f1在数值上比f2大.而真正的算法是在dist方法中。

 @Thunk float dist(float x0, float y0, float x1, float y1) {
        return (float) Math.hypot(x1 - x0, y1 - y0);
    }

我们可以看到使用了Math.hypot,此方法返回的结果是sqrt(x2 +y2) ,也就是直角三角形的斜边长,其实就是手机屏幕的对角线的长度即手机尺寸。
dist方法得到的结果是读取的profile的斜边长度与手机屏幕的对角线的长度的一个差值。最后进行compare,差值小的在前面,差值大的在后面。之后将新的排序返回。这就是findClosestDeviceProfiles方法做的事情。
总结一下findClosestDeviceProfiles:通过将手机实际尺寸和理论的11个模板的理论尺寸进行比较,得到与目标的匹配度,匹配度越高,在closestProfiles的集合里面越靠前。
讲解完InvariantDeviceProfile构造方法中的findClosestDeviceProfiles方法,我们接着来看第二个精华的地方,那就是 invDistWeightedInterpolate(minWidthDps, minHeightDps, closestProfiles);这个方法

invDistWeightedInterpolate()

调用这个方法时传进去的参数是当前手机真实的宽和高,以及经过排序后得到的与目标匹配度由高到低的profiles集合。具体的操作在代码中进行了注解,其实,很多手机型号一致的,计算的时候不算多,有些许差别,计算出来的偏差值也不多,所以这个偏差值纠正就分析到这里。

InvariantDeviceProfile invDistWeightedInterpolate(float width, float height,
                ArrayList<InvariantDeviceProfile> points) {
        float weights = 0;
       //获取集合中的第一个InvariantDeviceProfile,也就是匹配度最高的那一个
        InvariantDeviceProfile p = points.get(0);
//判断如果当前手机与profile的差值等于0,则直接返回这个InvariantDeviceProfile。
        if (dist(width, height, p.minWidthDps, p.minHeightDps) == 0) {
            return p;
        }
//如果不相同的话就要计算偏差值然后给InvariantDeviceProfile进行赋值。
        InvariantDeviceProfile out = new InvariantDeviceProfile();
        for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) {
            p = new InvariantDeviceProfile(points.get(i));
            float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER);
            weights += w;
            out.add(p.multiply(w));
        }
        return out.multiply(1.0f/weights);
    }

到这里这两个方法就讲解完了,然后我们再返回头去看InvariantDeviceProfile的构造方法接着往下走

InvariantDeviceProfile closestProfile = closestProfiles.get(0);

interpolatedDeviceProfileOut和closestProfile ,如果没有偏差值的话是完全相同的,指向同一个对象。
但此为止,InvariantDeviceProfile构造方法中,我们测量了当前手机的长宽,再由实际长宽和11个模板比较得到了最佳模板,以及和模板不一样的偏差值。

接下来构造方法就会进行赋值。
numRows = closestProfile.numRows;
        numColumns = closestProfile.numColumns;
        numHotseatIcons = closestProfile.numHotseatIcons;
        defaultLayoutId = closestProfile.defaultLayoutId;
        demoModeLayoutId = closestProfile.demoModeLayoutId;
        numFolderRows = closestProfile.numFolderRows;
        numFolderColumns = closestProfile.numFolderColumns;
        minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns;

        iconSize = interpolatedDeviceProfileOut.iconSize;
        landscapeIconSize = interpolatedDeviceProfileOut.landscapeIconSize;
        iconBitmapSize = Utilities.pxFromDp(iconSize, dm);
        iconTextSize = interpolatedDeviceProfileOut.iconTextSize;
        fillResIconDpi = getLauncherIconDensity(iconBitmapSize);

到这里根据手机的硬件参数,寻找到对应的模型,确定最终的布局的行列数就做完了,接下来则是正式构造我们需要使用的DeviceProfile。
赋值结束后就会构造DeviceProfile,因为手机屏幕有两个状态,横屏和竖屏,横屏和竖屏的区别就是长宽的不同,横屏的时候我们需要把边长长的设置成宽,竖屏是把长边设置为高。代码如下

 Point realSize = new Point();
        display.getRealSize(realSize);
        // The real size never changes. smallSide and largeSide will remain the
        // same in any orientation.
        int smallSide = Math.min(realSize.x, realSize.y);
        int largeSide = Math.max(realSize.x, realSize.y);

        landscapeProfile = new DeviceProfile(context, this, smallestSize, largestSize,
                largeSide, smallSide, true /* isLandscape */);
        portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize,
                smallSide, largeSide, false /* isLandscape */);

创建DeviceProfile,就是创建实际的硬件模型,具体的操作我们就来看一下DeviceProfile的构造方法。

  public DeviceProfile(Context context, InvariantDeviceProfile inv,
            Point minSize, Point maxSize,
            int width, int height, boolean isLandscape) {

        this.inv = inv;
        this.isLandscape = isLandscape;

        Resources res = context.getResources();
        DisplayMetrics dm = res.getDisplayMetrics();

        // Constants from resources确定硬件的类型,是平板,电视还是手机
        isTablet = res.getBoolean(R.bool.is_tablet);
        isLargeTablet = res.getBoolean(R.bool.is_large_tablet);
        isPhone = !isTablet && !isLargeTablet;

        // Some more constants判断横竖屏进行桌面hotseat的位置设置。
        transposeLayoutWithOrientation =
                res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);

        context = getContext(context, isVerticalBarLayout()
                ? Configuration.ORIENTATION_LANDSCAPE
                : Configuration.ORIENTATION_PORTRAIT);
        res = context.getResources();

//准备各种参数的px值
        ComponentName cn = new ComponentName(context.getPackageName(),
                this.getClass().getName());
     ...
省略部分代码
...
//对每个图标进行更详细的设置
        // Calculate all of the remaining variables.
        updateAvailableDimensions(dm, res);

     ...
省略部分代码
...
//对每个图标进行更详细的设置
        computeAllAppsButtonSize(context);
        mBadgeRenderer = new BadgeRenderer(context, iconSizePx);
    }

到此桌面的所有显示的内容的尺寸已经设置好。DeviceProfile也就构建完成。那么对于InvariantDeviceProfile是根据硬件来获得手机布局,而DeviceProfile则是根据手机布局,来设置具体的内容的硬件配置。两个相结合就完成了Launcher源码启动过程第一步之InvariantDeviceProfile获取硬件参数,确认布局参数。

结合上一篇,再通过这一篇的具体讲解,我们就知道了Launcher的源码启动过程第一步创建LauncherAppState 对象具体做了些什么:获取硬件参数InvariantDeviceProfile,通过具体的参数,计算出行列,和尺寸,得到符合标准的DeviceProfile布局,然后创建保存图片的缓存IconCatch和WidgetPreviewLoader以及其对应的操作数据库的类IconDB和CacheDb,最后注册广播LauncherModel来监听手机应用的变化。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,059评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,711评论 2 59
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • 梦里,一首黑白的歌唱着我干涩的喉咙不经意的痴心疯又在心底骂着天空一无所有的明 鱼缸里,一条名叫可乐的鱼死在了昨天,...
    立黄昏阅读 1,237评论 57 58
  • 周敦颐(1017-1073),字茂叔,原名敦实,号濂溪,道州营道(今湖南道县)人,生于北宋真宗天禧元年(公元101...
    学霸内参阅读 1,175评论 0 0