android代码保护永不闪退

在我们开发的过程中,再厉害的程序员也无法保证写的代码没有错误,而这里面最严重的错误,对于android开发来说,毫无疑问就是app闪退了.特别是在开发第三方SDK的时候,假设因为SDK里面报的问题,导致对方App崩溃了,这对SDK而言的打击是非常严重的,有的时候我们甚至希望SDK即使无法很好的工作,也不要引发对接方App的崩溃.

下面我就来介绍两种,可以根据代码判断,只会代码所在线程死掉,但不会引发崩溃的方法:

  1. hook Thread.UncaughtExceptionHandler 的方式
  2. 代理 Thread.UncaughtExceptionHandler 的方式

hook Thread.UncaughtExceptionHandler 的方式

第一种方式就是,我们在程序启动的时候,先使用Thread.getDefaultUncaughtExceptionHandler();拿到系统的错误处理Handler对象,再次之上,我们自己创建一个 Thread.UncaughtExceptionHandler 的继承类,包裹住系统的错误处理Handler对象之后,把我们自己创建的Thread.UncaughtExceptionHandler 传递回去.
系统的Thread.UncaughtExceptionHandler的实现类是com.android.internal.os.RuntimeInit$UncaughtHandler

hook代码如下:

    public void hook() {
        Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();

        if (ueh == null) {
            mSystemUeh = null;
            mOtherUeh = null;
            Thread.setDefaultUncaughtExceptionHandler(CrashHookImpl2.this);
            return;
        }

        String androidUeh = "com.android.internal.os.RuntimeInit$UncaughtHandler";
        String selfUeh = getClass().getName();
        String currUeh = ueh.getClass().getName();

        //系统的
        if (androidUeh.equals(currUeh)) {
            mSystemUeh = ueh;
            mOtherUeh = null;
            Thread.setDefaultUncaughtExceptionHandler(CrashHookImpl2.this);
        } else if (selfUeh.equals(currUeh)) {
            //自己人 不处理
        } else {
            // bugly的 -> currUeh.contains("com.tencent.bugly.crashreport.crash")
            mSystemUeh = null;
            mOtherUeh = ueh;
            Thread.setDefaultUncaughtExceptionHandler(CrashHookImpl2.this);
        }
        return;
    }

其中,CrashHookImpl2是我们继承自Thread.UncaughtExceptionHandler 自己的实现类, 其中uncaughtException实现如下:

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        //-----------自己的处理,拦截除0异常-------------------------------
        StackTraceElement[] elements = e.getStackTrace();
                for (int i = 0; i < elements.length; i++) {
                    if (e.getClass().getName().equals("java.lang.ArithmeticException")) {
                        return;
                    }
                }
        //-----------下面的系统的uncaughtException不走,既不会崩溃---------

        if (mOtherUeh != null) {
            mOtherUeh.uncaughtException(t, e);
        }
        if (mSystemUeh != null) {
            mSystemUeh.uncaughtException(t, e);
        }
    }

这时候, 我们可以在程序中写一个除以0 的异常,则是不会引发崩溃的,只会引发线程的停止:

        CrashHookImpl.getInstance().hook();
        CrashHookImpl.getInstance().addCutOffExceptionHandler(new CrashHook.CutOffExceptionHandler() {
            @Override
            public boolean onCutOffException(Thread t, Throwable e) {
                StackTraceElement[] elements = e.getStackTrace();
                for (int i = 0; i < elements.length; i++) {
                    if (e.getClass().getName().equals("java.lang.ArithmeticException")) {
                        return true;
                    }
                }
                return false;
            }
        });
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(100 / 0 + "hook");
                    String nullp = null;
                    int a = nullp.length();
                }
            }
        }.start();

实现类全部代码:


import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;

import java.util.ArrayList;
import java.util.List;

public class CrashHookImpl2 implements Thread.UncaughtExceptionHandler, CrashHook {
    private static volatile CrashHookImpl2 instance = null;

    private Thread.UncaughtExceptionHandler mSystemUeh;
    private Thread.UncaughtExceptionHandler mOtherUeh;
    private List<CutOffExceptionHandler> exceptionHandlers;

    private CrashHookImpl2() {
        exceptionHandlers = new ArrayList<>();
    }

    @Override
    public void hook() {
        Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();

        if (ueh == null) {
            mSystemUeh = null;
            mOtherUeh = null;
            Thread.setDefaultUncaughtExceptionHandler(CrashHookImpl2.this);
            return;
        }

        String androidUeh = "com.android.internal.os.RuntimeInit$UncaughtHandler";
        String selfUeh = getClass().getName();
        String currUeh = ueh.getClass().getName();

        //系统的
        if (androidUeh.equals(currUeh)) {
            mSystemUeh = ueh;
            mOtherUeh = null;
            Thread.setDefaultUncaughtExceptionHandler(CrashHookImpl2.this);
        } else if (selfUeh.equals(currUeh)) {
            //自己人 不处理
        } else {
            // bugly的 -> currUeh.contains("com.tencent.bugly.crashreport.crash")
            mSystemUeh = null;
            mOtherUeh = ueh;
            Thread.setDefaultUncaughtExceptionHandler(CrashHookImpl2.this);
        }
        return;
    }

    public static CrashHookImpl2 getInstance() {
        if (instance == null) {
            synchronized (CrashHookImpl2.class) {
                if (instance == null) {
                    instance = new CrashHookImpl2();
                }
            }
        }
        return instance;
    }

    @Override
    public void unHook() {
        if (mSystemUeh != null) {
            Thread.setDefaultUncaughtExceptionHandler(mSystemUeh);
            return;
        }
        if (mOtherUeh != null) {
            Thread.setDefaultUncaughtExceptionHandler(mSystemUeh);
            return;
        }
        Thread.setDefaultUncaughtExceptionHandler(null);
        return;
    }

    @Override
    public void addCutOffExceptionHandler(CrashHook.CutOffExceptionHandler cutOffExceptionHandler) {
        if (cutOffExceptionHandler == null) {
            return;
        }
        exceptionHandlers.add(cutOffExceptionHandler);
    }

    @Override
    public void removeCutOffExceptionHandler(CrashHook.CutOffExceptionHandler cutOffExceptionHandler) {
        if (cutOffExceptionHandler == null) {
            return;
        }
        exceptionHandlers.remove(cutOffExceptionHandler);
    }

    public void tryHook(final long time) {
        if (time < 0) {
            return;
        }
        final HandlerThread thread = new HandlerThread("hook_ch");
        thread.start();
        final Handler handler = new Handler(thread.getLooper());
        handler.post(new Runnable() {
            private int size = 0;

            @Override
            public void run() {
                if (size <= (time / 200)) {
                    size++;
                    hook();
                    handler.postDelayed(this, 200);
                } else {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                        thread.quitSafely();
                    } else {
                        thread.quit();
                    }
                }
            }
        });
        return;
    }


    @Override
    public void uncaughtException(Thread t, Throwable e) {
        for (int i = 0; i < exceptionHandlers.size(); i++) {
            boolean consumed = exceptionHandlers.get(i).onCutOffException(t, e);
            if (consumed) {
                return;
            }
        }

        if (mOtherUeh != null) {
            mOtherUeh.uncaughtException(t, e);
        }
        if (mSystemUeh != null) {
            mSystemUeh.uncaughtException(t, e);
        }
    }


}

代理 Thread.UncaughtExceptionHandler 的方式

实际上也是Hook默认的 Thread.UncaughtExceptionHandler , 但这种方式的好处是,我们自己无需创建实现Thread.UncaughtExceptionHandler接口的实现类,而只需要使用Proxy.newProxyInstance代理系统默认的Thread.UncaughtExceptionHandler就好了,这种方式比起上面一种来说,除了效果一致以外,好处可能就是可以避免一些代码的检查.

代理方式的hook代码:

    public void hook() {
        if (hooked) {
            return;
        }
        Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();
        if (ueh == null) {
            return;
        }
        //代理逻辑
        class Px implements InvocationHandler {
            private Thread.UncaughtExceptionHandler handler;
            private Method uncaughtExceptionMethod;

            private Px(Thread.UncaughtExceptionHandler handler) {
                this.handler = handler;
                try {
                    uncaughtExceptionMethod = Thread.UncaughtExceptionHandler.class.getMethod("uncaughtException", Thread.class, Throwable.class);
                } catch (Throwable e) {
                }
            }

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) {
                //先走自己的
                try {
                    if (uncaughtExceptionMethod != null && uncaughtExceptionMethod.equals(method)) {
                        if (args != null && args.length == 2) {
                            Thread thread = (Thread) args[0];
                            Throwable throwable = (Throwable) args[1];
                            for (int i = 0; i < exceptionHandlers.size(); i++) {
                                boolean cutoff = exceptionHandlers.get(i).onCutOffException(thread, throwable);
                                if (cutoff) {
                                    //截断
                                    return null;
                                }
                            }
                        }
                    }
                } catch (Throwable ignored) {
                }
                try {
                    //原来的
                    method.invoke(handler, args);
                } catch (Throwable ignored) {
                }
                return null;
            }
        }
        Thread.UncaughtExceptionHandler hookedUeh = (Thread.UncaughtExceptionHandler) Proxy.newProxyInstance(
                ueh.getClass().getClassLoader(),
                ueh.getClass().getInterfaces(),
                new Px(ueh));
        Thread.setDefaultUncaughtExceptionHandler(hookedUeh);
        hooked = true;
    }

本种方式的全部实现代码:


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

public class CrashHookImpl implements CrashHook {
    private List<CutOffExceptionHandler> exceptionHandlers;

    private static volatile CrashHookImpl instance = null;
    private boolean hooked = false;

    private CrashHookImpl() {
        exceptionHandlers = new ArrayList<>();
    }

    public static CrashHookImpl getInstance() {
        if (instance == null) {
            synchronized (CrashHookImpl.class) {
                if (instance == null) {
                    instance = new CrashHookImpl();
                }
            }
        }
        return instance;
    }

    @Override
    public void hook() {
        if (hooked) {
            return;
        }
        Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();
        if (ueh == null) {
            return;
        }
        class Px implements InvocationHandler {
            private Thread.UncaughtExceptionHandler handler;
            private Method uncaughtExceptionMethod;

            private Px(Thread.UncaughtExceptionHandler handler) {
                this.handler = handler;
                try {
                    uncaughtExceptionMethod = Thread.UncaughtExceptionHandler.class.getMethod("uncaughtException", Thread.class, Throwable.class);
                } catch (Throwable e) {
                }
            }

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) {
                try {
                    if (uncaughtExceptionMethod != null && uncaughtExceptionMethod.equals(method)) {
                        if (args != null && args.length == 2) {
                            Thread thread = (Thread) args[0];
                            Throwable throwable = (Throwable) args[1];
                            for (int i = 0; i < exceptionHandlers.size(); i++) {
                                boolean cutoff = exceptionHandlers.get(i).onCutOffException(thread, throwable);
                                if (cutoff) {
                                    //截断
                                    return null;
                                }
                            }
                        }
                    }
                } catch (Throwable ignored) {
                }
                try {
                    //原来的
                    method.invoke(handler, args);
                } catch (Throwable ignored) {
                }
                return null;
            }
        }
        Thread.UncaughtExceptionHandler hookedUeh = (Thread.UncaughtExceptionHandler) Proxy.newProxyInstance(
                ueh.getClass().getClassLoader(),
                ueh.getClass().getInterfaces(),
                new Px(ueh));
        Thread.setDefaultUncaughtExceptionHandler(hookedUeh);
        hooked = true;
    }

    @Override
    public void unHook() {
        if (!hooked) {
            return;
        }
        Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();
        if (ueh == null) {
            return;
        }
        Thread.setDefaultUncaughtExceptionHandler(ueh);
        hooked = false;
    }

    @Override
    public void addCutOffExceptionHandler(CutOffExceptionHandler cutOffExceptionHandler) {
        if (cutOffExceptionHandler == null) {
            return;
        }
        exceptionHandlers.add(cutOffExceptionHandler);
    }

    @Override
    public void removeCutOffExceptionHandler(CutOffExceptionHandler cutOffExceptionHandler) {
        if (cutOffExceptionHandler == null) {
            return;
        }
        exceptionHandlers.remove(cutOffExceptionHandler);
    }
}

结语

对于崩溃,这两种方法只是避险的手段,而在我们的开发中,通过严谨的代码审查和测试,在上线之前解决问题,才是最好的选择.以上方法除了保护崩溃意外,还可以统计错误上报:
使用方法:

 CrashHookImpl.getInstance().hook();
        //保护所有的除零崩溃
        CrashHookImpl.getInstance().addCutOffExceptionHandler(new CrashHook.CutOffExceptionHandler() {
            @Override
            public boolean onCutOffException(Thread t, Throwable e) {
                if (e.getClass().getName().equals("java.lang.ArithmeticException")) {
                    //返回true,截断崩溃
                    return true;
                }
                return false;
            }
        });
        //统计上报
        CrashHookImpl.getInstance().addCutOffExceptionHandler(new CrashHook.CutOffExceptionHandler() {
            @Override
            public boolean onCutOffException(Thread t, Throwable e) {
                StackTraceElement[] elements = e.getStackTrace();
                for (int i = 0; i < elements.length; i++) {
                   String msg= elements[i].getClassName()+elements[i].getMethodName()+elements[i].getLineNumber();
                   //上报错误
                }
                //返回false 不截断
                return false;
            }
        });
发布了46 篇原创文章 · 获赞 62 · 访问量 30万+

猜你喜欢

转载自blog.csdn.net/qq_27512671/article/details/103323323