Android开发学习Android项目性能优化

新闻类App (MVP + RxJava + Retrofit+

2019-04-16  本文已影响83人  Peakmain

Github地址:新闻类App (MVP + RxJava + Retrofit+Dagger+ARouter)
关于内存优化,之前写过一篇文章,android性能优化之内存优化,大家可以先看下这篇文章

内存问题

内存抖动:图形是锯齿状,GC导致卡顿
内存泄漏:可用内存减少,不断的GC
内存溢出:OOM异常,程序异常

内存优化工具

工具使用大家可以找相关资料,这里我并不介绍工具的使用
Memory Profiler
1.实时图表表示应用内存使用量,可以识别内存泄漏,内存抖动等
2.提供捕获堆转储,强制GC以及跟踪内存分配的能力

image.png

Memory Analyzer
MAT:强大的java Heap分析工具,查找内存泄漏以及内存占用
生成整体报告,分析问题等
线下使用
LeakCanary
自动检查内存泄漏工具
Github:https://github.com/square/leakcanary

内存管理机制

java内存回收算法

标记-清除算法

缺点

复制算法

优缺点

标记-整理算法

优缺点

分代收集算法

Android内存管理机制

Dalivk与Art区别

Low Memory killer

内存抖动介绍

定义:内存频繁分配和回收导致内存不稳定
表现:频繁GC,内存曲线呈锯齿状
危害:导致卡顿,OOM异常

为什么内存抖动导致OOM

内存抖动实战

首先使用Memory Profiler初步排查
使用Memory Profiler或CPU Profiler结合代码排查
模拟内存抖动代码

public class MemoryShakeActivity extends AppCompatActivity implements View.OnClickListener {

    @SuppressLint("HandlerLeak")
    private static Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 创造内存抖动
            for (int index = 0; index <= 100; index++){
                String arg[] = new String[100000];
            }
            mHandler.sendEmptyMessageDelayed(0,30);
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory);
        findViewById(R.id.bt_memory).setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        mHandler.sendEmptyMessage(0);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}

memory profiler出现以下情况


image.png

点击工具按钮的红色圆圈按钮,过一段时间再次点击停止记录


image.png
过一段时间下方会出现以下的情况
image.png

我们发现内存消耗最多的String[]点击String[].并选择其中的一个String[]


image.png
双击handleMessage就会跳转到相关代码

内存泄漏实战

定义:内存中存在已经没有用的对象
表现:内存抖动,可用内存逐渐变少
危害:内存不足,GC频繁,OOM异常

代码实战

public class MemoryLeakActivity extends AppCompatActivity implements CallBack{

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memoryleak);
        ImageView imageView = findViewById(R.id.iv_memoryleak);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.avatar);
        imageView.setImageBitmap(bitmap);

        CallBackManager.addCallBack(this);
    }


    @Override
    public void dpOperate() {

    }
}
public class CallBackManager {

    public static ArrayList<CallBack> sCallBacks = new ArrayList<>();

    public static void addCallBack(CallBack callBack) {
        sCallBacks.add(callBack);
    }

    public static void removeCallBack(CallBack callBack) {
        sCallBacks.remove(callBack);
    }

}
public interface CallBack {
    void dpOperate();
}

从某个页面进去多进去几次之后会出现内存泄漏,memory profiler此时只能判断是否内存泄漏,解决还得使用MAT工具,
MAT下载地址https://www.eclipse.org/mat/

image.png

首先点击1然后点击2导出文件,但是导出的文件需要进行转换,我们需要找到自己的android sdk目录下的platform-tools目录中找到hprof-conv.exe文件,然后cmd命令找到platform-tools目录,执行命令

hprof-conv 源文件 输出文

我的是:

hprof-conv C:\Users\asus\Desktop\result.hprof C:\Users\asus\Desktop\result1.hprof

打开MAT->打开文件->Overview->Histogram->搜索MemoryLeakActivity


image.png

搜索MemoryLeakActivity之后的结果,此时有Objects有代表的确有内存泄漏8个


image.png
右击选择ListObjects->With incoming references
image.png

继续右击


image.png

结果如下


image.png

代表CallBackManager中sCallBacks持有了MemoryLeakActivity对象

解决办法MemoryLeakActivity中添加

   @Override
    protected void onDestroy() {
        super.onDestroy();
        CallBackManager.removeCallBack(this);
    }

检测不合理图片

Bitmap内存模型

获取Bitmap占用的内存

常规方式

ARTHook

public class ImageHook extends XC_MethodHook {

    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        super.afterHookedMethod(param);
        //实现逻辑
        ImageView imageView = (ImageView) param.thisObject;
        checkBitmap(imageView, imageView.getDrawable());
    }

    private static void checkBitmap(Object thiz, Drawable drawable) {
        if (drawable instanceof BitmapDrawable && thiz instanceof View) {
            final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
            if (bitmap != null) {
                final View view = (View) thiz;
                int width = view.getWidth();
                int height = view.getHeight();
                if (width > 0 && height > 0) {
                    // 图标宽高都大于view带下的2倍以上,则警告
                    if (bitmap.getWidth() >= (width << 1)
                            && bitmap.getHeight() >= (height << 1)) {
                        warn(bitmap.getWidth(), bitmap.getHeight(), width, height, new RuntimeException("Bitmap size too large"));
                    }
                } else {
                    final Throwable stackTrace = new RuntimeException();
                    view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                        @Override
                        public boolean onPreDraw() {
                            int w = view.getWidth();
                            int h = view.getHeight();
                            if (w > 0 && h > 0) {
                                if (bitmap.getWidth() >= (w << 1)
                                        && bitmap.getHeight() >= (h << 1)) {
                                    warn(bitmap.getWidth(), bitmap.getHeight(), w, h, stackTrace);
                                }
                                view.getViewTreeObserver().removeOnPreDrawListener(this);
                            }
                            return true;
                        }
                    });
                }
            }
        }
    }


    private static void warn(int bitmapWidth, int bitmapHeight, int viewWidth, int viewHeight, Throwable t) {
        String warnInfo = new StringBuilder("Bitmap size too large: ")
                .append("\n real size: (").append(bitmapWidth).append(',').append(bitmapHeight).append(')')
                .append("\n desired size: (").append(viewWidth).append(',').append(viewHeight).append(')')
                .append("\n call stack trace: \n").append(Log.getStackTraceString(t)).append('\n')
                .toString();

        LogUtils.e(warnInfo);
    }
}

App中调用

       DexposedBridge.hookAllConstructors(ImageView.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
            //setImageBitmap 方法名字 Bitmap 参数类型 
                DexposedBridge.findAndHookMethod(ImageView.class,"setImageBitmap",
                        Bitmap.class,new ImageHook());
            }
        });

线上内存泄漏监控

常规实现一

常规实现二

LeakCanary原理(源码:后期我会单独写篇文章)

LeakCanary定制

线上监控完整方案

上一篇 下一篇

猜你喜欢

热点阅读