极客班作业——视图优化
来源:互联网 发布:java web 配置log4j2 编辑:程序博客网 时间:2024/06/06 12:54
作业项目地址:https://github.com/lzyzsd/AndroidUIPorblems
这次主要使用开发者选项中的show overdraw选项来排除问题。
第一部分——MainActivity界面
在打开开发者选项中的ShowOverDraw选项后MainActivity的界面是这个样子(见下图)。可以看到主界面存在多次绘制现象,最高为4次绘制(红色部分)。回到代码进行分析。
查看MainActivity的布局文件activity_main的代码:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="com.github.lzyzsd.androiduiproblems.MainActivity" android:background="#2e2e2e"> <Button android:id="@+id/btn_show_overdraw" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="20dp" android:text="OverDrawView"/> <Button android:id="@+id/btn_busy_on_draw" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="20dp" android:text="BusyOnDraw"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:background="#2e2e2e"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher"/> <LinearLayout android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:background="#6c6c6c"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textSize="20sp" android:text="This is test"/> </LinearLayout> </LinearLayout></LinearLayout>可以看到在LinearLayout中都有设置background这个属性。android里如果对控件添加了background属性就会使控件多绘制一次,这样很容易造成过渡绘制问题。所以我们把background属性去掉,让控件背景默认为透明的。修改后效果如图:
现在界面上还存在1倍过度绘制问题。因为android会为每个window设置默认背景,所以我们就修改代码把默认背景去掉。在MainActivity的onCreate方法中添加如下代码:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setBackgroundDrawable(null); setContentView(R.layout.activity_main);重新运行程序后效果如下:
虽然问题解决了,但是界面展示效果大打折扣,所以window默认背景我们就不去掉了。
再分析布局代码,发现以下代码中存在多余的嵌套
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:background="#2e2e2e"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher"/> <LinearLayout android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:background="#6c6c6c"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textSize="20sp" android:text="This is test"/> </LinearLayout> </LinearLayout>为了使视图绘制时间减少,优化性能,同时达到相同的展示效果,故减少一个LinearLayout层级,最终修改后布局代码如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="com.github.lzyzsd.androiduiproblems.MainActivity" > <Button android:id="@+id/btn_show_overdraw" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="20dp" android:text="OverDrawView"/> <Button android:id="@+id/btn_busy_on_draw" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="20dp" android:text="BusyOnDraw"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginTop="20dp" > <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textSize="20sp" android:text="This is test"/> </LinearLayout></LinearLayout>
第二部分——OverDrawViewActivity界面
OverDrawViewActivity界面初次打开效果如下图:
可以看到存在很明显的过度绘制问题。
因为OverDrawViewActivity界面的布局文件只有一个OverDrawView的自定义控件,所以我们需要分析自定义控件的代码来解决问题,自定义控件代码如下:
public class OverDrawView extends View { Paint mPaint = new Paint(); public OverDrawView(Context context) { super(context); } public OverDrawView(Context context, AttributeSet attrs) { super(context, attrs); } public OverDrawView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public OverDrawView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } private void init() { mPaint.setStyle(Paint.Style.FILL); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int width = getWidth(); int height = getHeight(); mPaint.setColor(Color.GRAY); canvas.drawRect(0, 0, width, height, mPaint); mPaint.setColor(Color.CYAN); canvas.drawRect(0, height/4, width, height, mPaint); mPaint.setColor(Color.DKGRAY); canvas.drawRect(0, height/3, width, height, mPaint); mPaint.setColor(Color.LTGRAY); canvas.drawRect(0, height/2, width, height, mPaint); }}在onDraw方法中一共调用了四次canvas.drawRect()方法,进行了四次矩形绘制,但是四次绘制都会在一些区域进行重复绘制,有些区域甚至重复绘制了四次。因为从代码来看,后绘制的矩形会遮盖掉前一次绘制的矩形和自己重复的地方,所以我们只需修改代码将各自绘制的区域进行重新分配,是互相之间没有交集,避免过度绘制。修改后代码如下:
public class OverDrawView extends View { Paint mPaint = new Paint(); public OverDrawView(Context context) { super(context); } public OverDrawView(Context context, AttributeSet attrs) { super(context, attrs); } public OverDrawView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public OverDrawView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } private void init() { mPaint.setStyle(Paint.Style.FILL); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int width = getWidth(); int height = getHeight(); mPaint.setColor(Color.GRAY); canvas.drawRect(0, 0, width, height/4, mPaint); mPaint.setColor(Color.CYAN); canvas.drawRect(0, height/4, width, height/3, mPaint); mPaint.setColor(Color.DKGRAY); canvas.drawRect(0, height/3, width, height/2, mPaint); mPaint.setColor(Color.LTGRAY); canvas.drawRect(0, height/2, width, height, mPaint); }}修改后重新运行程序,OverDrawViewActivity界面效果如下图:
可以看到前面的大面积红色区域已经没有了,但界面仍存在1次过度绘制问题,所以我们在OverDrawViewActivity的onCreate方法中添加下面这行代码:
getWindow().setBackgroundDrawable(null);这样window的默认背景被设置为null,重新运行后效果如下图:
第三部分——BusyOnDrawActivity界面
BusyOnDrawActivity界面展示如下图:
查看代码后发现BusyOnDrawActivity界面布局是由大量的自定义控件组成的。页面主要是用ScrollView和LinearLayout来实现布局,没有使用RelativeLayout,同时也没有过度绘制现象。但是在主页面点击BUSYONDRAW按钮跳转到BusyOnDrawActivity时,页面出现了明显的卡顿,说明BusyOnDrawActivity页面绘制时间过长(PS:由于本人的Genymotion出了点问题无法用虚拟机,只能使用真机来测试,而我的真机的GPU Profiler中没有On screen as bars这个选项,所以就没有绘制时间的相关截图。),自定义控件的绘制代码性能有待优化。
于是查看自定义控件BusyOnDrawView代码如下:
public class BusyOnDrawView extends View { public BusyOnDrawView(Context context) { super(context); } public BusyOnDrawView(Context context, AttributeSet attrs) { super(context, attrs); } public BusyOnDrawView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public BusyOnDrawView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); for (int i = 0; i < 1000; i++) { System.out.println("canvas = [" + canvas + "]" + i); } Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(4); int radius = Math.min(getWidth(), getHeight()) / 2; canvas.drawCircle(getWidth()/2, getHeight()/2, radius, paint); }}发现两个主要问题。第一,在onDraw方法中创建Paint对象,因为onDraw方法会被系统多次调用所以最好不要在onDraw方法中创建对象,避免大量的内存使用从而导致GC,影响程序使用性能;第二,在onDraw方法中有一个for循环,而且这个for循环很耗时(循环了1000次),还创建大量的StringBuilder对象。这个for循环严重影响了自定义控件的绘制效率,使BusyOnDrawActivity界面绘制时间大大增加,导致页面出现卡顿现象。
解决方法是1.在onDraw方法外创建私有Paint对象。2.去掉这个for循环,在这里这个for循环所做的事是无意义的。最后修改代码如下:
public class BusyOnDrawView extends View { private Paint paint = new Paint(); public BusyOnDrawView(Context context) { super(context); } public BusyOnDrawView(Context context, AttributeSet attrs) { super(context, attrs); } public BusyOnDrawView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public BusyOnDrawView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(4); int radius = Math.min(getWidth(), getHeight()) / 2; canvas.drawCircle(getWidth()/2, getHeight()/2, radius, paint); }}
重新启动测试后,界面跳转变得非常流畅,没有卡顿现象出现。
如果在onDraw方法中一定要做耗时操作的话,建议使用线程来完成耗时操作,但同时会使代码逻辑变得很复杂或带来其他性能上的问题(比如onDraw方法中不要创建对象),所以最好不要在onDraw里做耗时操作。
- 极客班作业——视图优化
- Geekband作业13--视图优化
- 作业——在线学习Android课程之第十二周(内存、视图、电量优化)
- 作业——在线学习Android课程之第十三周(视图优化案例)
- 极客班作业——内存优化
- 视图性能优化——索引视图
- MySQL优化之——视图
- 视图优化
- Android应用性能优化系列视图篇——优化之路从Window开始
- 运筹学作业——社会网络中的信息传播优化问题
- 工程优化作业——成功失败法和黄金分割法
- 作业——在线学习Android课程之第十二周(内存优化)
- 作业——在线学习Android课程之第十三周(图片优化)
- Unity3D作业六项目一——优化打飞碟游戏
- cs231n 编程作业(2)学习心得——多种优化方法
- 作业:网页代码优化。
- 网站优化的作业..
- BW:后台作业优化
- struts2.xml的自带校验的使用
- 北邮OJ 884. 16校赛-Average Modulo
- 如何在手机上安装两个同样的app
- mongoDB 3.0 安全权限访问控制
- iOS崩溃crash大解析
- 极客班作业——视图优化
- 多线程_线程间通讯
- Spring加载配置文件applicationContext.xml的方式
- Tricks(三十七)—— C++ string类 split 的实现
- 主流开源编解码器Xvid,x264,ffmpeg 性能对比
- 讯飞语音包实现Android语音理解①
- 静态的adapter
- initWithFrame 与initWithCoder
- iOS跳转界面时隐藏tabBar的方法