1. 当crash发生时,系统就会调用UncaughtExceptionHandler的uncaughtException方法,可以读取到异常信息;
实现UncaughtExceptionHandler接口,在uncaughtException(Thread t, Throwable ex),ex.printStackTrace(PrintWriter pw)读取异常信息;
public void iinit(Context context) {
mDefaultCrashHandler = Thread.getDefaultUnCaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
mContext = context.getApplicationContext();
}
在Application初始化时设置CrashHandler
public class TestApp extends Application {
private static TestApp sInstance;
public void onCreate() {
super.onCreate();
sInstance = this;
CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init(this);
}
public static TestApp getInstance(){
return sInstance;
}
}
2. 使用multidex解决方法数越界
1)dexopt是一个程序,在安装时会使用dexopt来优化dex文件,dexopt采用固定大小的缓冲区来存储应用中所有方法的信息,LinearAlloc,大小为8M或者16M,上限为65536个方法数。
2)修改工程app目录下的build.gradle文件,在defaultConfig中添加multiDexEnabled true,在dependencies中添加multidex依赖;
3)在代码中支持multiDex,在manifest文件中指定Application为MultiDexApplication,
或者让应用的Application继承MultiDexApplication,或者重写Application.attachBaseContext方法,加入MultiDex.install(this)
4)可以指定主dex文件中包含的类,通过--main-dex-list设置加载列表,其中multidex的jar包中的9个类必须要打包到主dex中;
5)局限性:应用启动速度会降低,所以要避免产生较大的dex文件,可能有兼容性问题,比如产生大量内存消耗等;
3. 动态加载技术
解决资源访问、Activity生命周期管理和ClassLoader的管理。宿主指的是普通的apk,插件多采用经过特殊处理的apk,插件Activity的启动大多数是借助一个代理Activity来实现的。
1)Activity的工作主要是通过ContextImpl来完成的,其中有AssetManager getAssets()和Resources getResources()两个方法需要实现;首先在loadResources()中通过反射调用AssetManager的addAssetPath方法,将apk中的资源加载到Resources对象中,接着在代理Activity中实现getAssets和getResources;
2)采用反射或者接口的方式管理Activity的生命周期,然后在代理Activity中去调用插件Activity的对应生命周期的方法;反射方式代码复杂,性能有开销;
3)同一个插件采用同一个ClassLoader去加载类,避免多个ClassLoader加载同一个类时引发的类型转换错误,将不同插件的ClassLoader存储在HashMap中。
4. 反编译初步
1)Dex2jar可以将dex文件转化为jar包,再通过jd-gui查看jar包中的源码;
2)使用apktool对apk二次打包;
5. JNI开发
1)在Java中声名native方法
public native String get();
public native void set(String str);
2)javac编译java原文件得到class文件,再通过javah导出JNI头文件;其中JNIEnv *表示一个指向JNI环境的指针,可以访问各种接口方法,jobject表示Java对象中的this。
3)实现Java中的native方法:引用之前的JNI头文件,创建C++文件,实现函数定义;
4)编译C++文件生成动态链接库,在Java的静态初始化函数中加载:System.loadLibrary("jni-test");
5)JNI调用Java方法的流程是先通过类名找到类,再根据方法名找到方法id,再调用该方法;如果调用的是非静态方法,需要构造出类的对象后才能调用它。
6. Android性能优化
1)布局优化:不增加嵌套层级的情况下用LinearLayout代替RelativeLayout,用include标签加载布局文件,merge标签和include一起使用减少布局层级;ViewStub继承了View且宽高都是0,用来按需加载所需的布局文件,当调用它的setVisibility或者inflate方法加载后,ViewStub就会被它内部的布局替换掉;
2)绘制优化:View的onDraw不要创建新的局部对象和进行大量的操作,不要做耗时任务,每一帧保持在16ms以内;静态变量或者单例的引用和无限循环动画导致内存不能释放;
3)在/data/anr目录下保存着ANR的日志;
4)ListView和Bitmap优化:采用ViewHolder并避免在getView中执行耗时操作,快速滑动时不适合开启大量异步任务,开启硬件加速,Bitmap的加载用inSampleSize修改采样率;
5)用线程池代替大量线程,避免创建过多对象,常量用static final修饰,用尽量采用静态内部类,适当使用软引用和弱引用,采用内存和磁盘缓存,使用安卓自带数据结构如SparseArray和Pair等。
6)MAT内存泄露分析