综合技术:Crash信息捕获、multidex解决方法越界、动态加载技术、反编译初步

一、Crash信息捕获

我们写程序难免遇到Crash状况,如果知道了程序crash的原因就可以修复,但是有时我们发布了产品,产品在极少数机型下可能会发生未知的错误,导致crash这时我们不知道用户到底啥原因引起的,怎能解决?这里我们就总结下如何捕获用户的crash信息

1、相关api
  • UncaughtExceptionHandler 接口
    我们实现此接口在他的实现方法uncaughtException中处理异常即可
  • Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler ueh)
    静态方法参数UncaughtExceptionHandler 实现类对象即可
2、栗子

/**
 * Create by SunnyDay on 2019/04/19
 * 崩溃处理
 */
public class CrashHandler implements Thread.UncaughtExceptionHandler {

    private static final String TAG = "CrashHandler";
    private Thread.UncaughtExceptionHandler mDefaultCrashHandler;
    private Context mContext;

    private static CrashHandler sInstance = new CrashHandler(); //单例

    private CrashHandler() {
    }// 私有构造 提供单例

    /**
     * 单例写法
     */
    public static CrashHandler getInstance() {
        return sInstance;
    }

    /**
     * 初始化工作
     *
     * 就两步工作
     */
    public void init(Context context) {
        // 1、获得异常对象
        mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 2、设置默认的异常处理器
        Thread.setDefaultUncaughtExceptionHandler(this);// 参数UncaughtExceptionHandler实现类对象
        mContext = context.getApplicationContext();
    }


    /**
     * @param thread    未捕获异常的线程
     * @param throwable 未捕获异常
     * @function 当程序中有未被捕获的异常,系统将会自动调用此方法
     */
    @Override
    public void uncaughtException(Thread thread, Throwable throwable) {

        try {
            // 保存到本地
            Log.i(TAG, "uncaughtException: "+throwable.getMessage());
            saveToDisk();
            // 上传服务器
            upLoadToServer();

            //交给默认异常处理器处理,异常处理器为空时 直接手动结束程序
            if (mDefaultCrashHandler!=null){
                mDefaultCrashHandler.uncaughtException(thread,throwable);
            }else{
                Process.killProcess(Process.myPid());
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

    }


    private void saveToDisk() throws PackageManager.NameNotFoundException {
        printPhoneInfo();// 打印手机信息
        // TODO 保存到本地即可
    }

    /**
     * 打印发生crash的手机相关信息
     */
    private void printPhoneInfo() throws PackageManager.NameNotFoundException {
        PackageManager pm = mContext.getPackageManager();
        PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
        StringBuffer sb = new StringBuffer();
        sb.append("App Version: ");
        sb.append(pi.versionName);
        sb.append('_');
        sb.append(pi.versionCode);


        //android版本号
        sb.append("OS Version: ");
        sb.append(Build.VERSION.RELEASE);
        sb.append('_');
        sb.append(Build.VERSION.SDK_INT);

        //手机制造商
        sb.append("Vendor: ");
        sb.append(Build.MANUFACTURER);


        //手机型号
        sb.append("Model: ");
        sb.append(Build.MODEL);


        //cpu架构
        sb.append("CPU ABI: ");
        sb.append(Build.CPU_ABI);

        Log.i(TAG, "printPhoneInfo: "+sb.toString());

    }

    private void upLoadToServer() {
        // TODO 吧异常上传服务器即可
    }
}

1、定义个异常类实现了借口
2、初始化工作做中设置为默认异常处理器
3、重写接口 处理异常

调用:

/**
 * Create by SunnyDay on 2019/04/19
 */
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        // 未知的异常捕获
        CrashHandler crashHandler = CrashHandler.getInstance();
        crashHandler.init(this);
    }
}

测试:

    public void doClick(View view) {
       throw new RuntimeException("手动抛出异常");
    }

1、手机crash
2、打印log:
CrashHandler: uncaughtException: Could not execute method for android:onClick
CrashHandler: printPhoneInfo: App Version: 1.0_1OS Version: 5.1.1_22Vendor: Xiaomi Model: MI 6 CPU ABI: x86

二、方法越界

1、安卓中单个dex文件所能包含的方法的最大数为65536个,这其中包括安卓framwork、以来的jar包、以及应用本身代码中写的方法。这三者加起来不能超过65536个方法。一般来说一个简单的应用不会超过,但是对于一些大型的应用来说会超过。这时应用编译会报错。
2、另外一种情况,方法数目没有达到65536个但是编译也成功了,就是安装在低版本手机时报如下错误:

dalvikvm:Optimization failed
installed:dexopt failed on  xxxxxxxx

其实dexopt是一个程序,应用安装时,系统会通过dexopt来优化dex文件,在优化过程中dexopt采用一个固定大小的缓冲区(linearAlloc)来存储应用的所有方法信息。linearAlloc缓冲区再新版本安卓系统中大小为8M或者16M,但是再旧版本中(安卓2.3安卓2.2)只有5M,当待安装的apk方法比较多时,尽管没有达到65535这个上限,但是他的存储空间可能超过5M这时dexopt就会报错,导致安装失败。(这种场景一把发生在安卓2.x版本手机上,情况可以忽略了,现在这种手机几乎没人用了)

1、方法越界的解决方案
  • 删除无用的代码和第三方库
    这种情况下,多数会无效。毕竟你删除了,随着项目的变大,最终方法还会是到达这个数。

  • 采用插件化
    之前很多应用都考虑使用插件化的动态机制来动态加载部分dex文件,通过将一个dex拆分成两个或者多个dex文件,就在一定程度上解决了方法越界问题。
    使用缺点:插件化是重量级的技术解决方案。并且兼容性问题很多。

  • 2014年google提出的multidex解决方案
    android5.0开始安卓默认支持multidex,他可以从apk中加载多个dex文件。

3、multidex的基本使用

在这里插入图片描述

如图很简单:
1、defaultConfig 中添加 multiDexEnabled = true
2、dependencies中引入implementation 'com.android.support:multidex:1.0.3’依赖即可
3、代码中支持(如下)

代码中支持有三种方案(任选其一):
(1)manifest的application节点添加

 android:name="android.support.multidex.MultiDexApplication"

(2)如果你有了自己的Application在application节点下注册过了,则让你的应用继承MultiDexApplication 如:

public class MyApplication extends MultiDexApplication 

(3)如果不想使用方案(2)自己的类继承了Application则重写Application的attachBaseContext在这里面初始化也行:

public class MyApplication extends Application {
xxxxx
  @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);
    }


}
4、采用MultiDex带来弊端

(1)应用启动速度会降低,由于应用启动时会额外加载dex文件,这将导致应用启动速度降低,甚至会ANR。尤其是其他dex文件较大时,因此要避免生成较大的dex文件。
(2)由于 Dalvik LinearAlloc的bug可能导致MultiDex的应用无法在android4.0以前运行,还有可能运行了,但是运行中产生大量内存消耗,导致应用崩溃。
ps:(1)的问题客观存在,(2)很少发生

三、安卓动态加载技术

动态加载及插件化:框架参考

反编译初步

1、工具下载:

(1)dex2.jar(将dex文件转化为jar包的工具)

(2)jd-gui 将jar包装换为java代码

使用jadx-jui这个工具也可以完成如上两个功能。

(3)apktool 上面两个工具无法反编译到apk中的二进制数据源采用apktool就可以。apktool 还有一个功能就是二次打包。
apktool反编译得到smali文件和资源文件,我们修改smali文件,二次打包,就是市场所说山寨apk。
参考文章
下载地址:https://www.softpedia.com/get/Programming/Debuggers-Decompilers-Dissasemblers/ApkTool.shtml

2、反编译

如果想反编译混淆加固的apk需要了解smali代码等语言。这里不再总结。我们可以自行找资料学习。

3、使用apktool反编译简单栗子:

在这里插入图片描述
打开cmd执行

apktool d E://Apktool/aa.apk

结果:
在这里插入图片描述

在这里插入图片描述

多出来了个反编译生成的文件里面就是反编译得到的资源,我们可以修改得到smali代码,再二次打包就行了。

四、小结

反编译,也是安卓方向之一,有兴趣可以搞这一行,插件化也是高级必备以后再深入。

The end

本文来自<安卓开发艺术探索>笔记总结

猜你喜欢

转载自blog.csdn.net/qq_38350635/article/details/89396961