王学岗性能优化8——长图优化与电量优化

第一;长图优化
我们加载一张比屏幕还大的图片该如何处理呢?
解决方案一:沿对角线缩放,缩放到屏幕的宽度。然后上下滑动显示这张图片;
解决方案二:用手指按住图片,在屏幕上滑动显示。
以上两种方案都不会加载整张图片到内存,而是加载屏幕上显示的图片。
我们这里有一张大图片,我们把它放到assets目录里面.
这是那张大图片,够大的吧,超出屏幕长度几十倍。你要是不经处理直接加载到内存,估计直接OOM。
在这里插入图片描述
在这里插入图片描述
我们看下我们定义的BigView,专门加载上面那种大图使用我们自定义一个View,仿照腾讯的写法。读者可在项目中直接使用该View。

package com.example.administrator.lsn_8_demo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Scroller;

import java.io.IOException;
import java.io.InputStream;

public class BigView extends View implements GestureDetector.OnGestureListener,View.OnTouchListener{

    private Rect mRect;
    private BitmapFactory.Options mOptions;
    private GestureDetector mGestureDetector;
    private Scroller mScroller;
    private int mImageWidth;
    private int mImageHeight;
    private BitmapRegionDecoder mDecoder;
    private int mViewWidth;
    private int mViewHeight;
    private float mScale;
    private Bitmap bitmap;

    public BigView(Context context) {
        this(context,null,0);
    }

    public BigView(Context context, @Nullable AttributeSet attrs) {
        this(context,attrs,0);
    }

    public BigView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //指定要加载的区域
        //mrect指的是图上缩放后的区域
        mRect =new Rect();
        //需要复用,如果这张图换了一张图,同样的内存块就不需要加载。
        mOptions=new BitmapFactory.Options();
        //手势识别类,在屏幕上上下左右的去滑动,手势需要识别出用户往哪个方向动
        mGestureDetector=new GestureDetector(context,this);
        //设置onTouchListener
        setOnTouchListener(this);


        //滑动帮助类
        mScroller=new Scroller(context);

    }

    /**
     * 1,由使用者输入一张图片,从流中读取图片,
     * 2,用流是有好处的,不管是从网络,还是从你本地的文件都可以从流中读取
     */
    public void setImage(InputStream is){
        //先读取原图片的信息   高,宽
        mOptions.inJustDecodeBounds=true;
        //从流中读取图片,读取完成后信息会在mOption里
        BitmapFactory.decodeStream(is,null,mOptions);
        mImageWidth=mOptions.outWidth;
        mImageHeight=mOptions.outHeight;
        //开启复用,打开异变
        mOptions.inMutable=true;
        //设置格式成RGB_565,RGB_565存储的字节少,只要两字节
        mOptions.inPreferredConfig=Bitmap.Config.RGB_565;
        mOptions.inJustDecodeBounds=false;

        //创建一个区域解码器,可以只解码一部分
        try {
            //1,第二个参数的意思是能不能共享这个区域,一般情况下,我们不会让多个程序读同一个流的内容。
            //所以这个参数绝大部分情况下都是false.
            mDecoder=BitmapRegionDecoder.newInstance(is,false);
        } catch (IOException e) {
            e.printStackTrace();
        }

       //1,设置完这张图片以后,我就可以要求测量绘制这些动作按照压缩之后的
        //标准进行绘制。
        requestLayout();
    }

    /**
     * 1,在测量的时候把我们需要的内存区域获取到  存入到mRect中
     * 2,测量出要显示的区域
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取测量的view的大小
        //注意mViewWidth、mViewHeight不要写成局部变量,容易造成内存抖动
        mViewWidth=getMeasuredWidth();
        mViewHeight=getMeasuredHeight();

        //1,确定要加载的图片的区域
        //2,显示的内容肯定是从00(左上角)开始,
        mRect.left=0;
        mRect.top=0;
        mRect.right=mImageWidth;
        //获取一个高度缩放因子
        mScale=mViewWidth/(float)mImageWidth;
        //高度就根据缩放比进行获取
        mRect.bottom=(int)(mViewHeight/mScale);

    }

    /**
     * 1,画出内容
     * 2,在Android 3.0 引进了BitmapFactory.Options.inBitmap. 如果这个值被设置了,
     * decode方法会在加载内容的时候去重用已经存在的bitmap.
     * 这意味着bitmap的内存是被重新利用的,这样可以提升性能,
     * 并且减少了内存的分配与回收。
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //如果解码器拿不到,表示没有设置过要显示的图片
        if(null==mDecoder){
            return;
        }
        //复用上一张bitmap,bitmap所使用的内存块是可以复用的。
        //可以长时间复用同一内存块
        mOptions.inBitmap=bitmap;
        //1,解码指定的区域
        //2,在mRect这块空间里进行复用
        //3,使用decodeRegion的好处是我可以指定只占某一块,把一张图片的一块解码出来,
        // 放到bitmap里去
        bitmap=mDecoder.decodeRegion(mRect,mOptions);
        //1,把得到的矩阵大小的内存进行缩放  得到view的大小
        //matrix的大小就是view 的大小
        //2,定义一个matrix,负责缩放
        Matrix matrix=new Matrix();
        //宽高按照同样的方式缩放
        matrix.setScale(mScale,mScale);
        //画出来,不需要画笔
        canvas.drawBitmap(bitmap,matrix,null);


    }
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        //交给手势处理,因为手势处理可以得到方向,
        // onTouch处理方向很麻烦
        return mGestureDetector.onTouchEvent(event);
    }


    /**
     * 手按下的回调
     * @param e
     * @return
     */
    @Override
    public boolean onDown(MotionEvent e) {
        //滑动会产生惯性,如果上次滑动惯性还没有停止,强制停止
        if(!mScroller.isFinished()){
            mScroller.forceFinished(true);
        }
        //继续接收后续动作/事件
        return true;
    }


    /**
     *
     * @param e1   手指按下的事件,获取开始坐标
     * @param e2   当前首饰的事件,获取当前坐标
     * @param distanceX    左右(X)移动时的距离
     * @param distanceY   上下(Y)移动时的距离
     * @return
     */
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        //1,上下移动的时候,需要改变显示区域,就是改mRect的内容
        //2,这两个参数,第一个0表示左右不移动,第二个参数表示上下动的时候跟着distabceY进行移动
        //手指移动的时候有坐标,跟着坐标一起动。
        mRect.offset(0,(int)distanceY);
        //1,处理移动时已经移到了两个顶端的问题
        //顶部到了手机顶部就不能在向下走了。
        if(mRect.bottom>mImageHeight){
            mRect.bottom=mImageHeight;
            //(int)(mViewHeight/mScale)整个这块的高度
            mRect.top=mImageHeight-(int)(mViewHeight/mScale);
        }
        if(mRect.top<0){
            mRect.top=0;
            mRect.bottom=(int)(mViewHeight/mScale);
        }
        invalidate();
        return false;
    }

    /**
     * 处理惯性问题
     * @param e1   手指按下的事件,获取开始坐标
     * @param e2   当前首饰的事件,获取当前坐标
     * @param velocityX   每秒移动的x轴方向的像素点,滑动的快,移动的像素点多,慢就少
     * @param velocityY   每秒移动的y像素点
     * @return
     */
    //惯性滑动。 给定一个初始速度(velocityX,velocityY),该方法内部会根据这个速度去计算需要
    // 滑动的距离以及需要耗费的时间。通常用于:界面的惯性滑动等。

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        //1,做计算,x不处理,只处理Y
        //这个方法用来处理惯性滑动,比如我们手指松开了,由于惯性图片依然会继续滑动,惯性滑动到
        //2,哪里呢?就是我们这八个参数
        mScroller.fling(0,mRect.top,
                0,(int)-velocityY,
                0,0,
                0,mImageHeight-(int)(mViewHeight/mScale));
        return false;
    }
    /**
     * onFling的计算结果交给该方法
     */
    @Override
    public void computeScroll() {
        //已经不动了,滑动已经结束了。
        if(mScroller.isFinished()){
            return;
        }
        //true 表示当前滑动还没有结束,手指松开后会滑动一段距离
        if(mScroller.computeScrollOffset()){
            mRect.top=mScroller.getCurrY();
            mRect.bottom=mRect.top+(int)(mViewHeight/mScale);
            invalidate();
        }
    }

    @Override
    public void onShowPress(MotionEvent e) {
    }
    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }
    @Override
    public void onLongPress(MotionEvent e) {

    }
}

xml中的布局

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.example.administrator.lsn_8_demo.BigView
        android:id="@+id/bigView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
         />

</android.support.constraint.ConstraintLayout>

在MainActivity中使用

package com.example.administrator.lsn_8_demo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import java.io.IOException;
import java.io.InputStream;

public class MainActivity extends AppCompatActivity {

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

        BigView bigView=findViewById(R.id.bigView);
        InputStream is=null;
        try{
            //加载图片
            is=getAssets().open("big.png");
            bigView.setImage(is);
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            if(is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

第二电量优化
一:电量测试
电量测试:就是测试移动设备电量消耗快慢的一种测试方法。一般用平均电流来衡量电量消耗速度。平均电流越小,说明设备使用时间越长。但是平均电流多大才说明不耗电却没有一个统一标准。
硬件测试:利用硬件设备测试被测设备的电流,统计一段时间内(使用某个功能模块)的平均电流值。跟我们Android程序员没有太多关系
软件测试:利用系统工具导出分析报告
二:Battery Historian
Battery Historian是google开源的电池历史数据分析工具。下载地址
如何安装下载这个文件?猛戳这里和这里
看下耗电量图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qczg_wxg/article/details/90273574
今日推荐