Android 过度绘制性能优化<6>

过度绘制:专家的理解如下:

Overdraw(过度绘制)描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的UI结构里面,如果不可见的UI也在做绘制的操作,会导致某些像素区域被绘制了多次。这样就会浪费大量的CPU以及GPU资源


过度绘制在以前做项目中优化性能的时候就涉及到了,检查过度绘制倒是挺简单的,进入手机设置APP页面,开发者选项->Debug GPU overdraw选项,选择Show overdraw areas即可,所以的APP布局页面都会显示特别的背景颜色.

判断是否过度绘制就是根据上面显示出来的背景颜色来确定,是否存在过度绘制:

1X Overdraw : 代表同一个像素点上面只绘制了一次(比如仅仅设置了背景),

2X Overdraw : 代表绘制了两次.

其他的一次类推.


下面做一个APP验证一下:

<1> : 新建Android 工程,工程树如下:

Android 过度绘制性能优化<6>_第1张图片

<2> : 所有程序代码如下:

DurianMainActivity.java:


package org.durian.durianoverdraw;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class DurianMainActivity extends ActionBarActivity implements View.OnClickListener {

    private final static String TAG = "DurianMainActivity";

    private ListView mList;
    private DurianBaseAdapter mDurianBaseAdapter;

    private ImageView image;
    private Bitmap bitmap;

    private Button mButton;
    private Button mTranButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.durian_main);

        mList = (ListView) findViewById(R.id.list);

        image = (ImageView) findViewById(R.id.loadimage);

        mDurianBaseAdapter = new DurianBaseAdapter(this);
        mList.setAdapter(mDurianBaseAdapter);

        mButton = (Button) findViewById(R.id.button);
        mButton.setOnClickListener(this);

        mTranButton=(Button)findViewById(R.id.trans);
        mTranButton.setOnClickListener(this);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_durian_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            bitmap = BitmapFactory.decodeByteArray(buffer, 0, buffer.length);
            image.setImageBitmap(bitmap);

        }

    };

    private byte buffer[];

    private Bitmap imageLoadFromNet(String link) {

        URL url = null;
        try {
            url = new URL(link);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        HttpURLConnection conn;
        try {
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);

            InputStream in = conn.getInputStream();

            int len = 0;
            byte buf[] = new byte[1024];

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            while ((len = in.read(buf)) != -1) {
                out.write(buf, 0, len);
            }
            out.close();

            buffer = out.toByteArray();

        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;

    }

    @Override
    public void onClick(View v) {

        int id = v.getId();
        switch (id) {
            case R.id.button:

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        bitmap = imageLoadFromNet("http://pic.qiantucdn.com/58pic/16/06/95/58PIC4n58PICQTX_1024.jpg");
                        mHandler.sendEmptyMessage(0);
                    }
                }).start();
                break;
            case R.id.trans:
                image.setBackgroundColor(0x00ffffff);
                break;
        }

    }
}

DurianBaseAdapter.java




package org.durian.durianoverdraw;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

/**
 * Project name : DurianOverDraw
 * Created by zhibao.liu on 2016/1/11.
 * Time : 10:00
 * Email [email protected]
 * Action : durian
 */
public class DurianBaseAdapter extends BaseAdapter {


    private Context mContext;

    private LayoutInflater mInflater;

    private ViewHolder holder;
    public DurianBaseAdapter(Context context){

        mContext=context;
        mInflater= LayoutInflater.from(mContext);

        holder=new ViewHolder();

    }
    @Override
    public int getCount() {
        return 3;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if(convertView==null) {

            convertView=mInflater.inflate(R.layout.list_item,null);

            holder.imageView=(ImageView) convertView.findViewById(R.id.image);
            holder.titleText=(TextView)convertView.findViewById(R.id.title);
            holder.summaryText=(TextView)convertView.findViewById(R.id.summary);

            holder.imageView.setBackgroundColor(0xffffffff);

        }

        return convertView;
    }

    private class ViewHolder{

        ImageView imageView;
        TextView titleText;
        TextView summaryText;

    }
}

durian_main.xml:

<?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:orientation="vertical">

    <ListView
        android:id="@+id/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="load image" />

        <Button
            android:id="@+id/trans"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Transparent" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/image_trans"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/loadimage"
            android:background="#54625D"
            android:layout_width="450dp"
            android:layout_height="240dp"
            />

    </LinearLayout>

</LinearLayout>


list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="96dp"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/image"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        android:background="#ffff0f"
        android:src="@mipmap/ic_launcher"
         />

    <LinearLayout
        android:background="#fffffe"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="12dp"
        android:orientation="vertical">

        <LinearLayout
            android:background="#fffffe"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="#EEEEEF"
                android:text="title" />

        </LinearLayout>

        <TextView
            android:id="@+id/summary"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#EEEEEF"
            android:text="summary" />

    </LinearLayout>

</LinearLayout>

<3> : 运行结果:

Android 过度绘制性能优化<6>_第2张图片

根据上面的list_item布局,

title 颜色是深红色,即4X,summary是浅红色,即3X,title的背景是1X,summary背景2X

<4> : 下面看一个重点对象,我预先设置了一个ImageView,并且事先设置了它的背景:

点击"LOAD IMAGE"按钮:

Android 过度绘制性能优化<6>_第3张图片

注意图片和背景重叠的区域是浅绿色,浅绿色即2X,包含了两次渲染,即有两个"背景"

再点击"Transparent" 按钮:

Android 过度绘制性能优化<6>_第4张图片

发现了吧,颜色变了,变成1X了.也就是降低了图片的渲染工作.提供了性能.

上面主程序最好还调整一下:

package org.durian.durianoverdraw;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class DurianMainActivity extends ActionBarActivity implements View.OnClickListener {

    private final static String TAG = "DurianMainActivity";

    private ListView mList;
    private DurianBaseAdapter mDurianBaseAdapter;

    private ImageView image;
    private Bitmap bitmap;

    private Button mButton;
    private Button mTranButton;

    private LinearLayout mlinear;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.durian_main);

        mList = (ListView) findViewById(R.id.list);

        image = (ImageView) findViewById(R.id.loadimage);

        mDurianBaseAdapter = new DurianBaseAdapter(this);
        mList.setAdapter(mDurianBaseAdapter);

        mButton = (Button) findViewById(R.id.button);
        mButton.setOnClickListener(this);

        mTranButton=(Button)findViewById(R.id.trans);
        mTranButton.setOnClickListener(this);

        mlinear=(LinearLayout)findViewById(R.id.image_trans);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_durian_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            bitmap = BitmapFactory.decodeByteArray(buffer, 0, buffer.length);
            image.setImageBitmap(bitmap);

        }

    };

    private byte buffer[];

    private Bitmap imageLoadFromNet(String link) {

        URL url = null;
        try {
            url = new URL(link);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        HttpURLConnection conn;
        try {
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);

            InputStream in = conn.getInputStream();

            int len = 0;
            byte buf[] = new byte[1024];

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            while ((len = in.read(buf)) != -1) {
                out.write(buf, 0, len);
            }
            out.close();

            buffer = out.toByteArray();

        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;

    }

    @Override
    public void onClick(View v) {

        int id = v.getId();
        switch (id) {
            case R.id.button:

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        bitmap = imageLoadFromNet("http://pic.qiantucdn.com/58pic/16/06/95/58PIC4n58PICQTX_1024.jpg");
                        mHandler.sendEmptyMessage(0);
                    }
                }).start();
                break;
            case R.id.trans:
                image.setBackgroundColor(0x00ffffff);
                mlinear.setBackgroundColor(0x00ffffff);
                break;
        }

    }
}

后面这个图片下载并且显示,然后做过度绘制优化,可以看出,当图片下载以后,因为最上层是图片显示的,所以背景可以通过程序设置成完全透明的(因为背景user看不到,不需要显示),从而可以防止过度绘制.

























你可能感兴趣的:(Android 过度绘制性能优化<6>)