Android应用性能优化系列视图篇——优化之路从Window开始

来源:互联网 发布:单片机芯片破解 编辑:程序博客网 时间:2024/06/06 08:35

众所周知,Activity是Android应用程序的载体,允许用户在其上创建一个用户界面即视图,而这个视图又是通过Window来管理,同样,Dialog的视图其实也是通过Window来管理。而Window又是通过WindowManager将视图呈现到手机屏幕上。总而言之,Window在用户界面即视图中扮演着至关重要的角色。所以,如果要做好视图性能优化,理解Window的本质则是第一步!

那么,Window到底是一个什么样的存在呢?

以Activity为例,当一个Activity被实例化后,首先会被调用其attach方法,具体过程详见Android系统源码。attach方法中有一个非常重要的逻辑,那就是创建Window(API 22源码)。

    final void attach(...) {        ...        mWindow = new PhoneWindow(this);        mWindow.setCallback(this);        mWindow.setOnWindowDismissedCallback(this);        mWindow.getLayoutInflater().setPrivateFactory(this);        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {            mWindow.setSoftInputMode(info.softInputMode);        }        if (info.uiOptions != 0) {            mWindow.setUiOptions(info.uiOptions);        }        ...        mWindow.setWindowManager(                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),                mToken, mComponent.flattenToString(),                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);        if (mParent != null) {            mWindow.setContainer(mParent.getWindow());        }        mWindowManager = mWindow.getWindowManager();        mCurrentConfig = config;    }

可以看到,这里的Window对象的实现是PhoneWindow,顾名思义,就是手机窗口的意思。PhoneWindow的具体逻辑我们不去细看了,但是需要知道这样一件事情:PhoneWindow中最顶级的视图是DecorView,像状态栏,ActionBar栏,Content等等都是其间接子视图,DecorView继承于FrameLayout。

下面,说说DecorView的几个子视图。

a、状态栏

位于手机屏幕最上方,用来显示时间、电量、网络等等。

b、ActionBar

ActionBar是在Android 3.0之后引入的一个非常重要的导航栏功能,替代了原有的Title,如果设置成隐藏,则将不会存在于DecorView中。

c、Content

这一部分是开发者自行开发的视图,也是应用程序界面内容的主体,开发者通过在Activity或者Dialog中使用setContentView的方式添加到Window中,Content的容器默认是一个id为Window.ID_ANDROID_CONTENT的FrameLayout视图。

以结构图的方式,描述如下:

这里写图片描述

注1:DecorView不是直接包含这些视图的,里面还包裹了一层LinearLayout,由于作用不大,省略未画。
注2:ActionBar非浮在content上方,而是纵向线性排列。

弄明白了Window的结构,下面可以开始优化之路了。


1、Window背景优化

可能为了防止content的内容不设置导致当前Activity的界面为透明,android默认给Window添加了背景,通过Activity的Theme属性定义了一些风格样式,这些样式中就包含了Window的背景,属性为android:windowBackground,这个背景被设置给了DecorView。

android:windowBackground这个属性非常重要,由于国内很多公司都不喜欢Android官方的设计风格,所以很多开发者都喜欢覆盖这个属性,定义自己的一套Window背景。但是,如果Content区域呈现的界面内容被完全铺满了,比如界面的背景是一张满屏图片,那么,如果定义了这个属性,将导致过度绘制。

所谓过度绘制,就是指在同一个区域进行了多个层级的绘制,对位于下层的用户看不见的内容进行绘制其实都是以一种对GPU的浪费。过度绘制不仅仅浪费设备资源,而且容易引发性能问题,比如卡顿等。

解决方案1:定义多套Activity的样式style,在不需要使用Window默认背景的Activity的样式中定义:

<item name="android:windowBackground">@null</item>

解决方案2:代码中手动置空Window背景:

getWindow().setBackgroundDrawable(null);

2、Content区域利用

Content区域布局为FrameLayout,是Activity或者Dialog的界面容器,如果其layout的根视图也为FrameLayout的话,很明显多了一层冗余的布局次。

那么,如何来解决这种问题呢?

几乎所有的有经验的开发者都会立刻想到使用merge标签,这样可以省去多出来的一层FrameLayout布局。当然,也会有反对者立刻跳出来,说如果layout的根布局需要设置一些属性,比如background、margin、padding等,而这些属性merge标签是完全没法处理的。

解决方案:正常使用merge标签,自定义属性可以通过代码获取到Content容器布局来设置:

FrameLayout content = (FrameLayout) getWindow().findViewById(Window.ID_ANDROID_CONTENT);

除此之外,由于可以直接获取到Content的容器布局,所以可以很方便地在界面上添加一些悬浮视图,而不需要在layout文件中写,而后者往往容易引发布局层级冗余。


3、DecorView区域利用

曾经遇到这样一个需求:在项目所有Activity页面的右上角添加一个颜色标识,用来区分页面的不同类型或属性。

方案a:在所有layout中加视图。

当然,这种方案不需要经过多少思考就被放弃掉了,因为不管这种方案能不能实现需求,布局和代码的改动量都是巨大无比的,而且,大多数的页面层级都会多上一层。

方案b:代码中直接视图添加到content区域

正如前面第2点所说,利用content区域,这样layout就完全不需要改动了,工作量很低,然而却无法实现需求。因为如果存在ActionBar的话,content区域位置是在ActionBar下方,纵向线性排列,无法将布局悬浮到ActionBar上方。

方法c:添加视图到DecorView区域

前面说过DecorView实际上是FrameLayout布局,里面包裹了一层LinearLayout,使得StatusBar、ActionBar、Content按照纵向线性排列,代码中可以通过以下方式获取到DecorView:

FrameLayout decorView = (FrameLayout) getWindow().getDecorView();

有了decorView,自然就好办了,可以直接添加视图,而且是可以添加到屏幕的任意位置,而且是处于最上层(除了特殊的StatusBar)。

比如,可以给每个Activity的页面中添加一个自定义View,代码如下:

getApplication().registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {            @Override            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {                FrameLayout decorView = (FrameLayout) activity.getWindow().getDecorView();                View view = new View(activity);                view.setLayoutParams(new FrameLayout.LayoutParams(100, 100));                view.setBackgroundColor(Color.RED);                decorView.addView(view);            }            @Override            public void onActivityStarted(Activity activity) {            }            @Override            public void onActivityResumed(Activity activity) {            }            @Override            public void onActivityPaused(Activity activity) {            }            @Override            public void onActivityStopped(Activity activity) {            }            @Override            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {            }            @Override            public void onActivityDestroyed(Activity activity) {            }        });

注:DecorView的区域顶部被状态栏遮盖,如果要添加视图需要将marginTop设置为状态栏高度。


本博客不定期持续更新,欢迎关注和交流:

http://blog.csdn.net/megatronkings

3 1