天天看点

Android常见异常及解决方法异常概念Android中全局异常捕获开发中的总结:

异常概念

       异常是指在程序的运行过程中所出现的错误,这些错误会干扰到指令的正常执行,从而造成程序的异常退出,常见的场景如:文件找不到、网络连接错误、非法参数等等

    在java中,所有的异常都继承于Throwable:

Android常见异常及解决方法异常概念Android中全局异常捕获开发中的总结:

java异常

Error(错误)

程序无法处理的错误,表示运行应用程序中较严重问题

Exception(异常)

程序本身可以处理的异常,分为两类:

unchecked exception(非检查异常):也称运行时异常(RuntimeException),比如常见的NullPointerException、IndexOutOfBoundsException。对于运行时异常,java编译器不要求必须进行异常捕获处理或者抛出声明,由程序员自行决定。

checked exception(检查异常,编译异常):也称非运行时异常(运行时异常以外的异常就是非运行时异常),java编译器强制我们必须进行捕获处理,比如常见的IOExeption、SQLException、JSONException等等,对于非运行时异常如果不进行捕获或者抛出声明处理,编译都不会通过

异常和错误的区别:异常能被程序本身可以处理,错误是无法处理

Android中全局异常捕获

为了方便我们对应用的优化,可以使用Android系统提供的 UncaughtExceptionHandler,对我们应用的错误进行收集

/**
 * 全局异常处理
 */
public class GlobalExceptionCaught implements UncaughtExceptionHandler {

    private Context mContext;
    private static GlobalExceptionCaught mInstance;

    private UncaughtExceptionHandler mDefaultUncaughtExceptionHandler;

    // 存储设备信息
    private Map<String, String> mInfo = new HashMap<>();

    // 日期格式
    private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);

    private GlobalExceptionCaught() {
    }

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

    /**
     * @param context context
     */
    public void init(Context context) {
        mContext = context;
        mDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        // 对错误信息经常处理
        if (handleException(e)) {
            // 人为处理
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            // 1秒之后杀掉当前进程
            Process.killProcess(Process.myPid());
            System.exit(1);
        } else {
            // 未处理,调用系统的, 及弹出讨厌的程序崩溃框
            if (mDefaultUncaughtExceptionHandler != null) {
                mDefaultUncaughtExceptionHandler.uncaughtException(t, e);
            }
        }
    }


    /**
     * 是否认自己理异常
     *
     * @param e 异常
     * @return true 处理   false 未处理
     */
    private boolean handleException(Throwable e) {
        if (e != null) {
            collectErrorInfo();
            saveErrorInfo(e);
            return true;
        } else {
            return false;
        }
    }

    // 收集错误信息
    private void collectErrorInfo() {
        try {
            PackageManager pm = mContext.getPackageManager();
            PackageInfo packageInfo = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
            if (packageInfo != null) {
                String versionName = packageInfo.versionName;
                int versionCode = packageInfo.versionCode;
                mInfo.put("versionName", versionName);
                mInfo.put("versionCode", versionCode + "");
            }
            Field[] fields = Build.class.getFields();
            if (fields.length > 0) {
                for (Field field : fields) {
                    field.setAccessible(true); // 信息全部可用
                    try {
                        mInfo.put(field.getName(), field.get(new Object()).toString());
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }

    // 保存错误信息
    private void saveErrorInfo(Throwable e) {
        StringBuilder stringBuilder = new StringBuilder();
        for (Map.Entry<String, String> entry : mInfo.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            stringBuilder.append(key).append("=").append(value).append("\n");
        }
        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        e.printStackTrace(printWriter);
        Throwable cause = e.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = e.getCause();
        }
        printWriter.close();
        String result = writer.toString();
        stringBuilder.append(result);
        long curTime = System.currentTimeMillis();
        String date = dateFormat.format(new Date());
        String fileName = "crash-" + date + "-" + curTime + ".log";
        // 判断是否有sd卡
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            String path = "/sdcard/crash/";
            File dir = new File(path);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(path + fileName);
                fileOutputStream.write(stringBuilder.toString().getBytes());
            } catch (FileNotFoundException e1) {
                e1.printStackTrace();
            } catch (IOException e1) {
                e1.printStackTrace();
            } finally {
                try {
                    if (fileOutputStream != null) {
                        fileOutputStream.close();
                    }
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }
}
           

自定义一个类,继承Application类,复写onCreate()方法

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        GlobalExceptionCaught globalExceptionCaught = GlobalExceptionCaught.getInstance();
        globalExceptionCaught.init(this);
    }
}
           

在清单文件进行配置:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.itstudy.example">

    <application
        android:name=".App"
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name="com.itstudy.example.EntryActivity"
            android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <action android:name="android.intent.action.VIEW"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        .......    
    </application>
</manifest>
           

开发中的总结:

1>  可以使用第三方的bug收集,如腾讯的bugly,友盟的bug统计.

2>  平时我们在测试阶段,方便我们找错,可以使用如下方法:

       当应用程序出现错误时,我们打开一个exception显示界面,显示我们程序中出现的异常

Android常见异常及解决方法异常概念Android中全局异常捕获开发中的总结:

异常示例图

具体步骤如下:

   第一步: 声明全局异常处理类

public class ExceptionCaught implements UncaughtExceptionHandler {
    private UncaughtExceptionHandler uncaughtExceptionHandler;

    public ExceptionCaught(UncaughtExceptionHandler uncaughtExceptionHandler) {
        this.uncaughtExceptionHandler = uncaughtExceptionHandler;
    }

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        ShowExceptionActivity.showException(ex);
        if (uncaughtExceptionHandler != null) {
            uncaughtExceptionHandler.uncaughtException(thread, ex);
        }
    }
}
           

   第二步:全局显示界面配置 

/**
 * 崩溃日志显示界面
 */
public class ShowExceptionActivity extends BaseActivity {
    private TextView exceptionView;

    public static void showException(Throwable throwable) {
        App applicationContext = App.getInstance();
        if (applicationContext != null && BuildConfig.DEBUG) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            throwable.printStackTrace(new PrintStream(byteArrayOutputStream));
            String msg = new String(byteArrayOutputStream.toByteArray());

            Intent intent = new Intent(applicationContext, ShowExceptionActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.putExtra("msg", msg);
            applicationContext.startActivity(intent);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_show_exception);
        exceptionView = findViewById(R.id.show_exception_view);
        handlerIntent(getIntent());
    }

    /**
     *  处理异常
     */
    private void handlerIntent(Intent intent) {
        String msg = intent.getStringExtra("msg");
        if (msg != null)
            exceptionView.append(msg);
        }
    }
}
           
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="崩溃日志"
        android:textSize="20sp"/>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/show_exception_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="#DDFF0000"/>
    </ScrollView>

</LinearLayout>
           

  第三步:自定义类,继承Application类,复写onCreate()方法

public class App extends Application {

        @Override
        public void onCreate() {
            super.onCreate();
            if (BuildConfig.DEBUG) {
                Thread.UncaughtExceptionHandler handler
                        = Thread.getDefaultUncaughtExceptionHandler();
                ExceptionCaughtAdapter exceptionCaughtAdapter = new ExceptionCaughtAdapter(handler);
                Thread.setDefaultUncaughtExceptionHandler(exceptionCaughtAdapter);
            }
        }
    }
           

第三步:在android清单文件中进行配置

<?xml version="1.0" encoding="utf-8"?>
<manifest 
        xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.itstudy.example">

    <application
        android:name=".App"
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".ui.EntryActivity"
            android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <action android:name="android.intent.action.VIEW"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <activity
            android:name=".ui.ShowExceptionActivity"
            android:exported="false"
            android:launchMode="singleInstance"
            android:process=":exception"
            android:screenOrientation="sensor"/>

</manifest>
           

以上是我对android中异常的理解

继续阅读