Android内存优化

Android内存优化(六)LeakCanary使用详解
https://cloud.tencent.com/developer/article/1034262

install方法会返回RefWatcher用来监控对象,LeakApplication中还要提供getRefWatcher静态方法来返回全局RefWatcher。最后为了举例,我们在一段存在内存泄漏的代码中引入LeakCanary监控,如下所示。
LeakCanary
leakcanary-android-no-op

07
LeakCanary.install方法会返回RefWatcher用来监控对象,LeakApplication中还要提供getRefWatcher静态方法来返回全局RefWatcher。最后为了举例,我们在一段存在内存泄漏的代码中引入LeakCanary监控,如下所示。


RefWatcher

LeakApplication.23(watch)

中还要提供getRefWatcher静态方法来返回全局RefWatcher。最后为了举例,我们在一段存在内存泄漏的

MainActivity存在内存泄漏,原因就是非静态内部类LeakThread持有外部类MainActivity的引用,LeakThread中做了耗时操作,导致MainActivity无法被释放。关于内存泄漏可以查看Android内存优化(三)避免可控的内存泄漏这篇文章。

refWa.watch this ActivityRef


hprof文件)和info信息分享出去,如下图所示。

hprof文件)和info信息分享出去,如下图所示。 watch


45

内存泄漏分析框架LeakCanary的使用与原理解析
https://blog.csdn.net/AndrExpert/article/details/103781575

JVM)在内存管理方面给我们变成带来的便利。JVM的这一大特性使Java程序员从繁琐的内存管理工作中得到了一定解放,但是JVM的这个特点的实现也是有代价的,并且它也并非万能

 

LeakCanary 使用方法 

Java堆内存

内存泄漏的表述可为:当一个对象已经不需要再使用本该被回收时,
另外一个正在使用的对象持有它的引用从而导致它不能被垃圾收集器回收,
结果它们就一直存在于内存中(通常指Java堆内存),
占用有效空间,永远无法被删除。


GC Roots回收的对象必须是当前没有任何引用的对象


GC非静态内部类

1.2 “静态实例” 造成内存泄漏

,由于SomeResources类是一个非静态内部类,
它默认持有外部类StaticInstanceActivity的引用,就会导SomeResources的对象一直持有该引用,造成内存泄漏。
内部类 外部类StaticInstanceActivity


Application context

1.3 “Handler” 造成的内存泄漏

MessageQueue

非静态内部类创建静态实例造成的内存泄漏案例


在优化时,可以为WebView开启另外一个进程,通过AIDL与主线程进行通信,便于WebVIew所在的进程可以根据业务需要选择合适的时机进行销毁。

Process.killProcess Process.myPid


2.1 LeakCanary使用方法


support-fragment:1.6.3

a46
RefWatcher.watch(myDetachedView)
RefWatcher.watch

AndroidRefWatcherBuilder

AndroidExcludedRefs.createAppDefaults.build

buildAndInstall

listenerServiceClass

setEnabledAsync


LeakCanaryInternals.setEnabledAsync

ActivityRefWatcher.install 

FragmentRefWatcher.Helper.install

LeakCanaryInternals.setEnabledAsync

AndroidRefWatcherBuilder.build

ActivityRefWatcher activityRefWatcher = 

registerActivityLifecycleCallbacks

ActivityLifecycleCallbacks.onActivityDestroyed

   refWatcher.watch(activity);

randomUUID

KeyedWeakReference

ensureGoneAsync  reference

WeakReference ReferenceQueue

ReferenceQueue

RefWatcher.ensureGoneAsync
RefWatcher.ensureGone

RefWatcher.ensureGoneAsync

watchExecutor.execute

removeWeaklyReachableReferences


retainedKeys  key

gone reference


gcTrigger.runGc生成堆内存快照

System.nanoTime

heapdumpFile
heapDumper.dumpHeap

heapDumpBuilder.heapDumpFile

heapdumpListener.analyze heapDump

analyze 

HeapAnalyzerService

HeapAnalyzerService

ensureGone

removeWeaklyReachableReferences  gone 

retainedKeys        Set


KeyedWeakReference


!retainedKeys.contains

queue.poll


.remove


(2)将堆内存转储到文件并分析,获取泄漏对象的GC最短强引用路径

heapdumpListener.RefWatch

HeapAnalyzerService.runAnalysis(runAnalysis)


DisplayLeakService.class


DisplayLeakService.class


AbstractAnalysisResultService 

AbstractAnalysisResultService        listenerServiceClass

HeapAnalyzerService.runAnalysis

AbstractAnalysisResultService  listenerServiceClass
=DisplayLeakService

AndroidRefWatcherBuilder


,该方法中最终调用的是HeapAnalyzerService#runAnalysis方法在后台服务中执行堆快照分析任务。

HeapAnalyzerService.runAnalysis

extends

ForegroundService    AnalyzerProgressListener


listenerServiceClass.getName

ContextCompat.startForegroundService context,intent


IntentService Service 

onHandleIntentInForeground

LISTENER_CLASS_EXTRA HEAPDUMP_EXTRA

DisplayLeakService.class

HeapAnalyzer heapAnalyzer = new 
HeapAnalyzer heapDump.excludedRefs,
heapDump.reachabilityInspectorClasses

HeapAnalyzer heapAnalyzer = 
new HeapAnalyzer heapDumper.excludedRefs,
heapDumper.reachabilityInspectorClasses

excludedRefs  reachabilityInspectorClasses 

  // 即分析堆内存快照,找出 GC roots 的最短强引用路径,并确定是否是泄露

HeapAnalyzer

GC Roots回收的对象必须是当前没有任何引用的对象

AnalysisResult result = heapAnalyzer.checkForLeak

sendResultToListener        listenerClassName heapdump result

分析堆内存

listener.onProgressUpdatell

MemoryMappedFileBuffer

HprofBuffer HprofParser

//移除相同 GC roots
deduplicateGcRoots snapshot

//
findLeakingReference

.getClassObj.getClassName()

findLeakTrace

Instance leakingRef = findLeakingReference(referenceKey, snapshot);


 由上述源码可知,该方法最终调用findLeakingReference方法来判断是否真的存在内存泄漏,如果存在(leakingRef!=null),就调用findLeakTrace方法找出这个泄漏对象的GC Root最短强引用路径

GC Root  


registerActivityLifecycleCallbacks  Activity

onActivityDestroyed

RefWatcher.watch 


 至此,LeakCanary框架实现原理分析完毕,最后我们再总结一下:LeakCanary是通过在Application的registerActivityLifecycleCallbacks方法实现对Activity销毁监听的,该方法主要用来统一管理所有activity的生命周期。所有Activity在销毁时在其OnDestory方法中都会回调ActivityLifecycleCallbacks#onActivityDestroyed方法,而LeakCanary要做的就是在该方法中调用RefWatcher#watch方法实现对activity进行内存泄漏监控。那么,LeakCanary是如何判断某个Activity可能会发生内存泄漏呢?答案是:WeakReference和ReferenceQueue,即LeakCanary利用了Java的WeakReference和ReferenceQueue,通过将Activity包装到WeakReference中,被WeakReference包装过的Activity对象如果能够被回收,则说明引用可达,垃圾回收器就会将该WeakReference引用存放到ReferenceQueue中。假如我们要监视某个activity对象,LeakCanary就会去ReferenceQueue找这个对象的引用,如果找到了,说明该对象是引用可达的,能被GC回收,如果没有找到,说明该对象有可能发生了内存泄漏。最后,LeakCanary会将Java堆转储到一个.hprof文件中,再使用Shark(堆分析工具)分析.hprof文件并定位堆转储中“滞留”的对象,并对每个"滞留"的对象找出 GC roots 的最短强引用路径,并确定是否是泄露,如果泄漏,建立导致泄露的引用链。最后,再将分析完毕的结果以通知的形式展现出来。

.hprof   GC roots  Shark Shark工具
并对每隔滞留的对象找出GCroots的最短强引用路径。

.23

Class Loader SubS Runtime


VM Stack】    Native 

Stack Frame 

reference类型 returnAddress类型


OufOfMemoryError出现在如果虚拟机栈可以动态扩展,但是扩展后仍然无法申请到足够的内存。

-XX:MaxMetaspaceSzie来指定元数据区的大小。

Direct Memory
DirectByteBuffer

23
Java 堆  GC
Gc Roots      


Android性能优化(1):常见内存泄漏与优化(一)

https://blog.csdn.net/AndrExpert/article/details/102467011

GC Roots


Eden和Survivor区域 Minor GC

-XX:PretenureSizeThreshold参数,使得大于这个设置值得对象直接在老年代内存区域分配,这样做的目的在于避免在Eden区及两个Survivor区之间发生大量的内存复制。

MaxTenuringThreshold参数设置老年代年龄阈值。


HandlePromation

JVM的类加载机制

优化/避免内存泄漏原则:

涉及到使用Context时,尽量使用Application的Context;
对于非静态内部类、匿名内部类,需将其独立出来或者改为静态类;
在静态内部类中持有外部类(非静态)的对象引用,使用弱引用来处理;
不再使用的资源(对象),需显示释放资源(对象置为null),如果是集合需要清空;
保持对对象生命周期的敏感,尤其注意单例、静态对象、全局性集合等的生命周期

23
在剖析Handler消息机制原理一文中我们知道


由于主线程的Looper对象会随着应用进程一直存在的且Java类中的非静态内部类和匿名内部类默认持有外部类的引用

成内存泄漏。优化:将Handler类独立出来,或者使用静态内部类,因为静态内部类不持有外部类的引用。


Looper MessageQueue Message Handler

WeakReference  


弱引用

用来描述非必须的对象,比软引用更弱一些,由WeakReference类实现。被弱引用的对象只能生产到下一次垃圾收集发生之前,无论当前内存是否足够。


线程(非静态内部类或匿名内部类)造成的内存泄漏


new MyAsyncTask this.execute

new Thread MyRunnable.start


非静态内部类和匿名内部类默认持有外部类的引用;

GC、Major


泄漏。优化:将MyRunnable和MyAsyncTask独立出来,或使用静态内部类,因为静态内部类不持有外部类的引用


4) 静态实例造成的内存泄漏


StaticInstanceActivity

优化:使用单例模式实现SomeResources,或者将其改成静态内部类。如果需要传入Context参数,必须使用Application的Context。


(5) 资源未关闭或监听器未移除(注销)引起的内存泄露情况


OOM


资源未关闭或监听器未移除(注销)引起的内存泄露情况


BroadcastReceiver 


TypedArray obtainStyledAttributes     AttrDeclareView_background_color

getColor       recycle

 除了上述常见的5种内存泄漏外,还有包括无限循环动画、使用ListView、
使用集合容器以及使用WebView也会造成内存泄漏,其中,
无限循环动画造成泄漏的原因是没有再Activity的onDestory中停止动画;
使用ListView造成泄漏的原因是构造Adapter时没有使用缓存的convertView;
使用集合容器造成泄漏的原因是在不使用相关对象时,没有清理掉集合中存储的对象引用。
在优化时,在退出程序之前将集合中的元素(引用)全部清理掉,再置为null;
使用WebView造成泄漏的原因是在不使用WebView时没有调用其destory方法来销毁它,
导致其长期占用内存且不能被回收。在优化时,可以为WebView开启另外一个进程,
通过AIDL与主线程进行通信,便于WebVIew所在的进程可以根据业务需要选择合适的时机进行销毁。

使用Proguard混淆代码打造APP安全第一层防护
https://blog.csdn.net/AndrExpert/article/details/55002749

ProGuard


class

-keepattributes Signature
-keepattributes EnclosingMethod
-dontwarn -keep .**

-libraryjars
-dontwarn  
-keep class  Serializable


AndroidMainfest中的类及其子类不能混淆,包括四大组件、Application的子类等;
23(Application)

Parcelable Creator

-keep class * implements android.os.Parcelable{ 

  public static final android.os.Parcelable$Creator *; 

}

-keep classcom.xxx.xxx.** { *; }

-keep classcom.** {*;}

-keepclasseswithmembernamesclass * {
native;
}

ProGuard

-dontshrink
-23
=proguard-project

    
指定混淆时采用的算法,后面的参数是一个过滤器

-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

!class/merging/*
class_specification -keep [,modifier,..]


native methods
-libraryjars


!**.gif,images/**,匹配images目录下所有除gif格式文件

!**.gif,images/**,匹配images目录下所有除gif格式文件


project.properties

-keepattributes *Annotation*

-keepclasseswithmembers

Context, AttributeSet
-keepclassmembers
**.R$*
32
static ;

Parcelable&Creator  CREATOR


jeb.exe 23
       通过jeb.exe或者dex2jar、jd-gui.exe查看混淆后的apk或classes.class文件


ProGuard
libraryjars  libraryjars

Android性能优化(2):常见内存泄漏与优化(二)
https://blog.csdn.net/AndrExpert/article/details/102956524


JVM与Dalvik区别 
Dalvik ART
Android Profiler 

Allocation Tracker  Heap Dump

MAT      LeakCanary23

.class ->.dex DEX工具
er

JVM  Dalvik ART

 JVM运行的.class文件,Dalvik运行的是.dex(即Dalvik Executable)文件。''


.class .jar 


23 String ClassNotFoundException异常
Const Pool
Field45d
Dalvik经过优化,允许在有限的内存中同时运行多个进程,或说同时运行多个Dalvik虚拟机的实例。


Dalvik Zygote
Dalvik拥有Zygote进程与共享机制

.dex->(.odex文件)
.dex->(23).oat native

 Dalvik虚拟机的运行时堆使用标记--清除(Mark--Sweep)算法进行GC,
Mark-Sweep GC
Zygote Space


4个Space分别是Zygote Space、Allocation Space、Image Space以及Large Object Space

[email protected]

[email protected]

Allocation Stack Live Mark Stack idel  JIT

init.rc、init

class AppRuntime : public AndroidRuntime
{};
//AppRuntime

runtime.start(23);


startVm &mJavaVM,&env,zygote

jclass startClass

  jclass startClass = env->FindClass(slashClassName);


env->CallStaticVoidMethod    Zygote    fork

  persist.sys
  
  
  get_library_system_property
  
  
 Android Profiler Android Monitor的Memory
  
  
  于Android Monitor的Memory Monitor
  
  Memory Profiler
  
  
  View-Tool
  
  
  saved stop -(destroy)
  Native Graphics:图像缓存等,包括GL Stack Code:用于处理代码和资源(如
  
  
  Code:用于处理代码和资源(如
  
  Allocation Tracker 
  
  Android Monitor ->Memory Profiler 
  
  Live 34 Dump Java hea
  
  Arrange Arrange by Package
  
  
  Filters  Regex Match Case。红色方框中其他选项意义:
  
  Instance View  
    Allocation Call Stack标签    Stack
  
  
  Head Dump 
  
  Dump Java Heap
  
  对象所分配到的调用堆栈(Android
  
  Depth:从任意 Native Shallow Retained 
  
  
  Allocations Depth:从任意
  (2) 黄色方框

Depth:从任意 GC root 到所选实例的最短 hop 数。 hop root
  
 hprof  hprof
  
  MAT
  
  
  Leak Suspects Systems Overview
  Leaks
  
  standar.hprof  standar.hprof
  
  
  Leak Suspects Report Suspects reo//
  
  
  Historgram:列出每个类的所有对象。从类的角度进行分析,注重量的分析;
  
  Dorminitor 23 Tree = ;
  
  Leak 
  
  
  Shallow Heap Retained Heap就是当前对象被GC后,从Heap上总共能释放掉多大的内存空间,这部分内存空间被称之为Retained
 
 GC 掉
  
  
  Dorminitor Tree 
  Tree,可以清晰地得到一个对象的直接支配的对象,如果直接支配对象中出现了不该有的对象,就说明发生了内存泄漏。
  
   从上图可知,被选中的SingleInstanceActivity对象的直接支配对象出现了不该有的CommonUtils对象,因为SingleInstanceActivity是要被回收的。换句话说,CommonUtils持有SingleInstanceActivity对象的引用,导致SingleInstanceActivity对象无法被正常回收,从而导致了内存泄漏。
  
  CommonUtils SingleActivity
  
  
  Android性能优化(4):UI渲染机制以及优化 -Android
  https://blog.csdn.net/AndrExpert/article/details/103156318

Overdraw
Profile GPU Rendering

卡顿优化
SysTrace    TraceView

 

SysTrace     TraceView

Rasterization

Poly Texture

View Hierachy   Display List Drawables等都是一起打包到统一的纹理 Ph


Draw Phase


VSync        CPU  GPU OpenGL

Refresh Frame

VSYNC 

卡顿现象
布局Layout过于复杂,无法在16ms内完成渲染;
同一时间动画执行的次数过多,导致CPU或GPU负载过重;
View过度绘制,导致某些像素在同一帧时间内被绘制多次
在UI线程中做了很多耗时的操作;
GC回收时暂停时间过长或者频繁GC产生大量的暂停时间(内存抖动);


内存抖动

GC回收时暂停时间过长或者频繁GC产生大量的暂停时间

Memory Churn

Yong Generation  Yong Generation

Display List23)

主要有Profile GPU Rendering、SysTrace以及TraceView等。

SysTrace TraceView


在XML布局中,控件有重叠且都有设置背景;

View的onDraw在同一区域绘制多次;

View 的onDraw 在同一区域绘制多次;

Show GPU overdraw

Show GPU overdraw

Profile GPU Rendering

overdraw ->Rendering

TraceView Hierachy Viewer,前者能够详细分析问题原因;后者能够查看布局的层次和每个View所花费的时间。Systrace

总之,我们应尽量从以下几个方面避免过度绘制。

移除无用的背景图;
减少视图层级,尽量使用扁平化布局,比如Relativeayout;
减轻自定义控件复杂度,重叠区域可以使用canvas.clipRect方法指定绘制区域;`

34

canvas.clipRect方法指定绘制区域;`


对于UI显示性能,比如动画播放不流畅、渲染卡顿等问题提供了分析数据

Measure 

Layout  Record Execute的数据计算工作,以将其计算成的Polygons

Rasterization GPU         SCREEN 

Draw Phase 

Convert to GPU description 
Cache as Display List

Execute Phase

VSYNC

Chrome商店下载 trace.html

Alerts、

Frames    Kernel CPU

Frames和Kernel 

animator 

DrawFrame

Interactions Alerts、 CPU usage

用Chrome分析trace.html -> Chrome trace.html


Frames -(16.66)

deliverInputEvent
Frames   animator .elevation
opacity

RenderThread 


自动场景暂时注销->成员

Kernel CPU

HeapTaskDeamon

Running thread Start 
Duration https://img-blog.csdnimg.cn/20191120093738233.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9qaWFuZ2RnLmJsb2cuY3Nkbi5uZXQ=,size_16,color_FFFFFF,t_70

2.2.2 TraceView


start Method Profiling

.invoke       dispatchMessage

MemoryIntArry.nativeSize 

是因为在主线程的FibonacciActivity中递归调用了
computeFubonacci()方法,
导致CPU被长期占用,
从而导致渲染线程无法获得CPU资源出现无法正常渲染的性能问题。


.nativePollOnce
.next      
.run         
.doFrame         
CallbackRecord.run

从Android 6.0源码的角度剖析View的绘制原理

https://blog.csdn.net/AndrExpert/article/details/100519701

requestlayout
scheduleTraversals
getLooper.getQueue.removeSyncBarrier

doTraversal

ActivityStackSuper();

View绘制过程分析


getSuggestedMinimumHeight
getSuggestedMinimumWidth


specMode specSize


34

specSize由View本身LayoutParams和父容器的MeasureSpec共同决定,
它可能是一个精确的数值,也可能是父容器的大小。具体操作如下所示:
455

自身Layoutparams   父容器MeasureSpec specSize


setMeasuredDimensionRaw

mMeasureWidth 
mMeasuredHeight

MeasureSpec和View的measureSpec获取

MODE_MASK &


即View的MeasureSpec创建受父容器和本身LayoutParams的影响,
在测量的过程中,系统会将View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,然后再根据这个MeasureSpec来测量出View的宽高。

其中,这个父容器对于顶层View来说就是Window,

对于普通View来说,就是ViewGroup。View -> Window View - > ViewGroup

performMeasure     
makeMeasureSpec

普通View
 对于普通View来说,它的父容器是ViewGroup,构建普通View的measureSpec是通过ViewGroup的measureChildWithMargins方法实现的。在该方法中又调用了ViewGroup的getChildMeasureSpec方法,这个方法接收三个参数,即父容器的parentWidthMeasureSpec、父容器的padding属性值+子View的margin属性值(left+right)以及子View的LayoutParams的width属性值。(同理height)


getRootMeasureSpec

  measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, 
                                                      MeasureSpec.EXACTLY);
                                                      makeMeasureSpec
rootDimension= ();

measureChildWithMargins = measureChildWithMargins

measureChildWithMargins =
getChildMeasureSpec

child.measure childWidthMeasureSpec ,childHeightMeasureSpec


childDimension    MeasureSpec.EXACTLY

resultSize = size

sUseZeroUnspecifiedMeasureSpec ? 0:size

.makeMeasureSpec

childDimension = LayoutParams.MATCH_PARENT

34
specMode = MeasureSpec.UNSPECIFIED

childDimension = lp.width lp.height

2. ViewGroup的measure过程


2. ViewGroup的measure过程
 ViewGroup继承于View,是用于装载多个子View的容器,由于它是一个抽象类,不同的视图容器表现风格有所区别,因此,ViewGroup并没有重写View的onMeasure方法来测量ViewGroup的大小,
而是将其具体的测量任务交给它的子类,以便子类实现其特有的功能属性。

这两个参数是测量LinearLayout尺寸的MeasureSpec,由其自身的LayoutParams和父容器计算得出。LinearLayout.onMeasure()源码如下:

MeasureSpec -> LayoutParams 和父容器

getVirtualChildCount

hasDividerBeforeChildAt
divider setDraw


measureChildBeforeLayout

mBaselineChildTop = mTotalLength

lp.width = LayoutParams.MATCH_PARENT

widthMode != MeasureSpec.EXACTLY

setMeasuredDimension

resolveSizeAndState

  // 计算heightMeasureSpec
   int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);


   childMeasuredState result

2.4 Layout过程

于Layout过程的作用是ViewGroup用来确定子元素的位置

ViewGroup OnLayout View layout

onLayoutChange
如果我们自定义一个ViewGroup,就必须要重写onLayout方法,以便确定子元素的位置

absoluteGravity & HORIZONTAL_GRAVITY_MASK

CENTER_HORIZONTAL

即mLeft/mTop/mRight/mBottom - >setFrame

private void setChildFrame(View child, int left, int top, int width, int height) {        
    // 设置子View位置
    // 其中,width和height为测量得到的大小值
    child.layout(left, top, left + width, top + height);
}

setChildFrame 

2.4 Draw过程

dispatchDraw

canvas.translate 

draw 
            scrollX
translate -scrollX


// View.dispatchDraw()
// 该方法是一个空方法,由View的子类实现,比如ViewGroup
// View绘制过程的传递就是通过该方法实现,它会遍历调用所有子元素的draw方法
protected void dispatchDraw(Canvas canvas) {

}
ii 

measure->onMeasure        layout->onLayout draw->onDraw View

 

Android性能优化(5):APK瘦身优化

https://blog.csdn.net/AndrExpert/article/details/103483635


ProGuard    AndResProguard

Android Lint tinypng WebP       libs

assets&res lib MERA-INF目录:保存应用程序的签名信息,签名信息可以验证APK文件的完整性。
res    AndroidManifest.xml classes.dex resource.arsc

JSON配置文件、二进制数据、HTML5等。系统在编译时不会编译该目录下的资源,因此不会像res目录样能被直接通过R

R.xxx.xxx () res/raw目录存储的也是原生的资源文件,但是它能够被Android映射成R
 
 BitmapFactory.debug版、release版 .decodeStream

openRawResource R.raw.logo


MERA-INF目录:保存应用程序的签名信息,签名信息可以验证APK文件的完整性。


MANIFEST.MF

CERT.SF:该文件保存了MANIFEST

CERT.RSA:该文件保存了APK包的签名和证书的公钥信息;

classes.dex (分包处理)

多个dex文件

Resources.arsc

优化dex文件

ProGuard)

minifyEnabled true 
proguardFiles getDefaultProguardFile
('
    
')


ProGuard工作原理

Shrink Optimize      (Obfuscate)   Preverify

确保
class文件 是可执行的


-verbose  -dontshrink

-optimizations   !field/,!class/merging/

!code/simplification/arithmetic
artimetic  

-keepattributes SourceFile,LineNumberTable抛出异常保留代码行号


.**{*;}
-keepclasseswithmembers class_specification[]
[,modifier,]

-printseeds
-libraryjars libs/alipaysdk.jar


File Filters(../)
!.gif/,images/,匹配images目录下所有除gif格式文件

2.1.2 AndResGuard

AndResGuard
安装包解压 极限压缩


buildscript {
    repositories {
        jcenter
        google
    }
}

AndResProguard

tencent.mm.AndResGuard-gradle-plugin


mappingFile = file  keep

keepRoot = false    

mergeDuplicatedRes = true  

specsName ,keep


whiteList= [
R.drawable.icon 
R.string.google_app_id 
R.string.gcm_defaultSenderId
]

sevenzip {

}
artifact

assemble [BuildType | Flavor]

finalApkBackupPath = ${ project.rootDir}  /final.apk


project.rootDir /final.apk


AndResGuard_app


andreguard (23)

java - jar andResGuard.jar

-zipalign -7zip


java -jar andResGuard

2.2 优化资源文件大小
2.2.1 Android Lint Android Lint


metadata(元数据,从

Converting Images to WebP

transparency/alpha channel


Encoding quality用于设置压缩质量;

alpha  minSdkVersion

.webp格式图片占用体积相较于jpg图片大约减少40%,相较于无损png图片大约减少30%。目前移动端Android4

.webp

帧动画

尽量不要使用帧动画,因为一个帧动画会包含多张图片;
将较大的资源文件放到服务端,APP启动后自动下载使用;
针对所有屏幕密度,尽量使用一套图片资源、一套布局、多套dimens.xml文件,在使用最小资源的情况下实现多分辨率的适配;
去除无用或功能上重复的第三方库,因为这些库也包含了自身的资源文件;
对assets资源目录作一定取舍,因为该目录资源保存的是原始资源。

多分辨率的适配

assets&res

//移
shrinkResources true


proguardFiles

andreguard
buildTypes

release


2.3 优化libs目录大小
2.3.1 裁剪libs目录

libs  

armeabi  

Android性能优化(6):浅析类加载机制与热修复技术
https://blog.csdn.net/AndrExpert/article/details/103756056
 

Java 中的ClassLoader

Android 中的ClassLoader

Tinker

Robust

AndFix


.class java.lang.Class


Link JRE     Initialize    JVM加载类机制


Bootstap ClassLoader

Extension ClassLoader

java.lang.ClassLoader

.class.getClassLoader


java.class.path

ExtClassLoader
BootstrapClassLoader 

BootClassLoader、PathClassLoader和DexClassLoader,

URLClassLoader

SecureClassLoader


Frament Dialog
DownLoadManager

ZygoteInit main  BootClassLoader

FindBugsSuppressWarnings

DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED


findClass String name 

Class
//根据指定全类路径创建其Class对象
Class.classForName name,false,null

PathClassLoader

BaseDexClassLoader

SystemServer

optimizedDirectory

BootClassLoader、PathClassLoader和DexClassLoader,

不支持自定义解压dex文件存储路径
解压

/data/dalvik-cache

optimizedDirectory


DexClassLoader extends BaseDexClassLoader

.class.getClassLoader .getParent


PathClassLoader确实是用于加载已经安装了的dex或apk文件,

1.1.2 双亲委托模式


JVM Android Class


其中查找的位置为该层类加载器指定指定加载类的目录

ApplicationClassLoader
java -classpath n java.class.path类和jar包 


Extension ClassLoader
java.ext.dirs jar
 jre/lib/ext
Bootstap   ClassLoader
jre/lib
-XBootclassPath


AppClassLoader BootstrapClassLoader
ExtensionClassLoader

它的字节码内容(静态数据)会被转换成方法区的运行时数据结构

java.lang.Class


1.1.3 ClassLoader的加载过程


loadClassBinaryName defineClass 


loadClassBinaryName
defineClass

DexPathList  DexFile

Class c
findLoadedClass name


findBootstrapClassOrNull


JVM  classpath Dalvik/ART利用

DexPathList

.dex .jar .zip

nativeLibraryDirectories

systemNativeLibraryDirectories


Element element dexElements
element.findClass

dexElements  dex class

Element 封装了 dex文件路径和 DexFile

findClass   string name,ClassLoader definingContext,


dexFile.
loadClassBinaryName

defineClass  

过程,即首先会遵循双亲委托模式检查此前是否已经加载过传入的类(指的是该类的.class文件),如果没有检查到就调用ClassLoader的findClass方法进行查找流程,Java层最终会调用DexFile的defineClassNative方法来执行查找流程。

ClassLoader
DexFile
defineClassNative

.class文件


DexFile defineClassNative


1.1.4 类的链接
JRE都为其保留一个不变的Class类型的对象,一个Class对象包含了特定某个结构

把类的二进制数据合并到JRE中


加载 验证 

primitive/type/void/[]

class getClass


Class clazz = ClassLoader.loadClass("com.jiangdg.test.Person");

ClassLoader.loadClass

Class.forName

1.2 Java反射机制

Java 反射机制

Class Field Method Constructor类

.java->.class     
.class->.java

getName getSimpleName  getPackage 包名

getDeclaredField   setAccessible true


invoke   对象名 方法名 

getParameter


getConstructors


2. 热修复技术  hotfix Patch


类加载机制 底层替换机制和Instant Run热插拔机制三种方式实现,并且这些热修复框架的主要提供代码修复、资源修复和动态链接库修复三种核心技术


tinker QZone 


不在将patch.dex增加到elements数组中

而是以差量的方式给出patch.dex,然后将patch.dex与应用的classes.dex合并,然后整体替换掉旧的DEX,达到修复的目的。

Base.apk

classes.dex Fix.apk classes.dex

QZone超级补丁基于dex分包(multidex dex)技术实现QZone超级补丁基于dex分包(multidex dex)技术实现

multidex dex 

把BUG方法修复以后,放到一个单独的dex补丁文件,让程序在运行期间加载dex补丁,即将dex文件插入到dexElements数组的最前面,然后再让虚拟机执行修复后的方法。


ClassLoader Key.class Patch.dex
Element Classes.dex

 AndFix基于在native层动态替换java层的方法技术实现,它是一种方法替换方案,能够及时生效,无需重启APP


AndFix native java 

native
ClassObject setup方法  public setFieldFlag方法

replaceMethod方法  AndFix

Instant Run热插拔机制三种方式实现,并且这些热修复框架的主要提供代码修复、资源修复和动态链接库修复三种核心技术

2.2 热修复实战
 俗话说:“纵使有千言万语,却抵不过认真撸一次“。为了加深对热修复技术的理解,本小将以基于multidex方案为例,实现一个简化版的热修复框架。该框架实现流程如下:

multidex

dexElements Element[]

patch.dex classes.dex

PathClassLoader  DexPathList  pathList  dexElements 

path.dex

patches.add patchFile

odex          MODE_PRIVATE    
getClassLoader pathClassLoader

pathListField =  pathClassLoader,pathList

optimizedDirectory  =  dex->odex

makePathElementsMethod

List.class    File.class  List.class

Class ..paramters

   Object[] patchElements = (Object[]) makePathElementsMthod.
                invoke(pathList, patches, mOdexDirectory, suppressedExceptions);

            Array.    newInstance

    dexElementsField.set(pathList, newElements);

dexElementsField.set pathList,newElements


编译  启动阶段

dx.bat工具生成补丁包,该工具位于Android

Android SDK 

dx.bat工具生成补丁包,该工具位于Android

dx --dex --output= patch.jar  

Github项目地址:HotFix

 最后,对目前主流的热修复框架作个小结:Android平台的热修复框架种类繁多,
根据实现原理大体可分为三类,即基于multidex、
基于native hook方案以及基于Instant Run热插拔机制。
其中,基于multidex的热修复框架,如NuWa、Tinker、Qzone超级补丁等,
这种方案兼容性高,但是需要反射更改DexElements,改变Dex的加载顺序,
这使得patch需要在下次启动时才能生效,实时性就受到了影响;
基于native hook的热修复框架,如Andfixd等,
这种方法能够实时生效且几乎无性能损耗,
但是需要针对dalvik虚拟机和art虚拟机做适配,
需要考虑指令集的兼容问题,需要native代码支持,兼容性上会有一定的影响。
基于Instant Run热插拔机制,
如Robust,这种方案兼容性高且实时生效,但是会增加包体积,
且暂时不支持so文件和资源替换。

multidex native hook方案以及基于Instant  Run热插拔机制。

 

你可能感兴趣的:(Android内存优化)