[2016-06-30]最新的全局異常處理DRCrashHandler已經集成在DR_support_lib庫中
具體請看: https://coding.net/u/wrcold520/p/DR_support_lib/git/tree/master
[2016-06-28] 1 增加log4j的支持
[2016-06-28] 2 增加全局異常處理(可自定義程序崩潰提示消息,自定義發送錯誤報告到服務器)
[2016-06-28] 3 增加兩種應用退出方法:① appExit,結束掉所有Acitivity的生命周期,正常退出;② appKill,結束掉所有Acitivity的生命周期,殺掉程序進程后退出。
[2016-06-29] 4 增加透明狀態欄和導航欄(默認開啟,藍色背景)
在Android程序中,我們通常會在可能發生錯誤的地方加上try {} catch (Exception e) {}語句來捕獲異常,但是,我們無法保證程序就一定不會出錯,比如,你玩Android游戲的過程中經常會碰到黑屏或者直接退出的情況,那么這些異常就應該是沒有被捕獲到(一般獲取到都會提示用戶錯誤,並提示用戶是否將錯誤上傳至服務器),那么現在我們來看下如何捕獲全局異常,然后自己處理。
1、新建DRCrashHandler.java(這是一個抽象類,我放在了DR_supprot_lib庫中,以便以后再寫應用程序的時候直接使用,里面用到了一些變量是DRConstants類中的,這是一個常量類,用來定義常用的常量的)
默認Toast彈窗提示用戶(消息可以自定義)
package cn.dr.lib.app; import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Field; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; import org.apache.log4j.Logger; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; import android.os.Looper; import android.widget.Toast; import cn.dr.lib.common.DRConstants; import cn.dr.lib.utils.DateUtil; import cn.dr.lib.utils.FileUtil; /** * DarkRanger的全局異常處理類 * * @author DarkRanger * */ public abstract class DRCrashHandler implements UncaughtExceptionHandler { /** log4j **/ private static final Logger log = Logger.getLogger(DRCrashHandler.class); /** 系統默認的UncaughtException處理類 **/ private Thread.UncaughtExceptionHandler mDefaultHandler; /** 程序context **/ private DRApplication mContext; /** 存儲設備信息和異常信息 **/ private Map<String, String> mInfos = new HashMap<String, String>(); /** 程序出錯提示信息 **/ private String mDRTipMsg = "抱歉,程序異常,3s后退出!"; /** 設置crash文件位置 **/ private String mDRCrashFilePath = DRConstants.CRASH_FILE_PATH; /** 生成的log文件 **/ private File logFile; /** 生成的crash文件 **/ private File crashFile; /** * 初始化 * * @param context */ public void init(DRApplication context) { log.info("DRCrashHandler is Ready For Application! "); // 1、上下文 mContext = context; // 2、獲取系統默認的UncaughtException處理器 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); // 3、初始化參數 initParams(); // 4、設置當前CrashHandler為默認處理異常類 Thread.setDefaultUncaughtExceptionHandler(this); } /** * 3.1 初始化參數 <br/> * <br/> * * {@link #setTipMsg(String)} setTipMsg("this is crash tip msg!!!"); <br/> * {@link #setCrashFilePath(String)} * setCrashFilePath(Constants.CRASH_FILE_PATH); <br/> * <br/> * * 如果想使用自己的CrashHandler,則復寫initParams()方,然后設置參數<br/> * * <code> * public class MyCrashHandler extends DRCrashHandler {<br/> * private static final Logger log = Logger.getLogger(MyCrashHandler.class);<br/> * * @Override<br/> * public void initParams() {<br/> * log.trace("MyCrashHandler: initParams()");<br/> * * setDRTipMsg("MyCrashHandler tip msg!!!");<br/> * setDRCrashFilePath(Constants.CRASH_FILE_PATH);<br/> * }<br/> * }<br/> * </code> */ public abstract void initParams(); @Override public void uncaughtException(Thread thread, Throwable ex) { log.info("DRCrashHandler dispatcher uncaughtException! "); if (mDefaultHandler != null && !handlerException(ex)) { mDefaultHandler.uncaughtException(thread, ex); } else { // 程序休眠3s后退出 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } ((DRApplication) mContext.getApplicationContext()).appExit(); } } /** * 5、處理異常<br> * <br> * * 5.1 收集設備參數信息<br> * 5.2 彈出窗口提示信息<br> * 5.3 保存log和crash到文件<br> * 5.4 發送log和crash到服務器<br> * * @param ex * @return 是否處理了異常 */ protected boolean handlerException(Throwable ex) { log.info("DRCrashHandler is handling Exception! "); if (ex == null) { return false; } else { // 5.1 收集設備參數信息 collectDeviceInfo(mContext); // 5.2 彈出窗口提示信息 new Thread(new Runnable() { public void run() { log.info("DRCrashHandler is ready send crash-info to device!"); Looper.prepare(); Toast.makeText(mContext, getDRTipMsg(), Toast.LENGTH_SHORT).show(); Looper.loop(); } }).start(); // 5.3 保存log和crash到文件 saveLogAndCrash(ex); // 5.4 發送log和crash到服務器 sendLogAndCrash(); return true; } } /** * 5.1 收集設備信息 * * @param ctx */ protected void collectDeviceInfo(Context ctx) { log.info("DRCrashHandler is collecting DeviceInfo! "); try { PackageManager pm = ctx.getPackageManager(); PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES); if (pi != null) { String versionName = pi.versionName == null ? "null" : pi.versionName; String versionCode = pi.versionCode + ""; mInfos.put("versionName", versionName); mInfos.put("versionCode", versionCode); } } catch (NameNotFoundException e) { log.error("An error occured when collect package info, Error: " + e); } Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields) { try { field.setAccessible(true); mInfos.put(field.getName(), field.get(null).toString()); } catch (Exception e) { log.error("An error occured when collect crash info, Error: " + e); } } } /** * 5.3 保存log和crash到文件 * * @param ex */ protected void saveLogAndCrash(Throwable ex) { log.info("DRCrashHandler is saving Log! "); StringBuffer sb = new StringBuffer(); sb.append("[DateTime: " + DateUtil.date2String(new Date()) + "]\n"); sb.append("[DeviceInfo: ]\n"); // 遍歷infos for (Map.Entry<String, String> entry : mInfos.entrySet()) { String key = entry.getKey().toLowerCase(Locale.getDefault()); String value = entry.getValue(); sb.append(" " + key + ": " + value + "\n"); } // 將錯誤手機到writer中 Writer writer = new StringWriter(); PrintWriter pw = new PrintWriter(writer); ex.printStackTrace(pw); Throwable cause = ex.getCause(); while (cause != null) { cause.printStackTrace(pw); cause = cause.getCause(); } pw.close(); String result = writer.toString(); sb.append("[Excetpion: ]\n"); sb.append(result); // 將異常寫入日志文件 log.error(result); // 5.3.1 記錄異常到特定文件中 saveToCrashFile(sb.toString()); } /** * 5.3.1寫入文本 * * @param crashText */ protected void saveToCrashFile(String crashText) { log.info("DRCrashHandler is writing crash-info to CrashFile(" + this.mDRCrashFilePath + ")! "); crashFile = new File(mDRCrashFilePath); // 創建文件(自己寫的操作文件相關的工具類) FileUtil.createFileAndFolder(crashFile); // 追加文本(自己寫的操作文件相關的工具類) FileUtil.appendToFile(crashFile, crashText); } /** * 5.4 發送log和crash到服務器 */ protected void sendLogAndCrash() { logFile = new File(mContext.getDrLogHelper().getLog4jFilePath()); crashFile = new File(getDRCrashFilePath()); // 5.4.1 sendToServer(logFile, crashFile); } /** * 5.4.1 將錯誤報告發送到服務器 * * @param crashFile * @param logFile */ protected abstract void sendToServer(File logFile, File crashFile); public String getDRTipMsg() { return mDRTipMsg; } /** * 設置程序崩潰提示信息 * * @param mDRTipMsg */ public void setDRTipMsg(String mDRTipMsg) { this.mDRTipMsg = mDRTipMsg; } public String getDRCrashFilePath() { return mDRCrashFilePath; } /** * 設置記錄崩潰信息的文件位置 * * @param mDRCrashFilePath */ public void setDRCrashFilePath(String mDRCrashFilePath) { this.mDRCrashFilePath = mDRCrashFilePath; } }
2、新建DRApplication.java(抽象類,里面用到了Log4j,上一篇有講到如果在Android中集成Log4j,這里在Application中使用DRLogHelper來初始化log4j)
package cn.dr.lib.app; import java.io.File; import java.util.LinkedList; import java.util.List; import org.apache.log4j.Logger; import android.app.Activity; import android.app.Application; import cn.dr.lib.log.DRLogHelper; import cn.dr.lib.ui.DRAcitivity; /** * DarkRanger的Application * * @author DarkRanger * */ public abstract class DRApplication extends Application { // 當前類的log private static Logger log; // Activity列表 private List<DRAcitivity> mActivityList = new LinkedList<DRAcitivity>(); // drLog實例 public DRLogHelper mDRLogHelper = DRLogHelper.getInstance(); // 全局異常處理類的實例 public DRCrashHandler mDRCrashHandler = new DRCrashHandler() { @Override public void initParams() { } @Override public void sendToServer(File logFile, File crashFile) { } }; @Override public void onCreate() { super.onCreate(); // 1、初始化DRLog參數 initDRLogHelperParam(); // 2、初始化DRLog initDRLogHelper(); // 3、初始化全局異常處理類 initCrashHandler(); } /** * 1、初始化DRLog參數,如: <br/> * * {@link DRApplication#getDrlog()} DRLog drLog = getDrlog(); <br/> * {@link DRLogHelper#setLog4jFilePath(String)} * drLog.setLog4jFilePath(Constants.LOG4J_FILE_PATH); <br/> * {@link DRLogHelper#setType(cn.dr.lib.log.DRLogHelper.LogType)} * drLog.setType(LogType.TYPE_LOG4J); <br/> * * 只有在子類中完成initDRLogParam參數設置以后才能使用log */ protected abstract void initDRLogHelperParam(); /** * 2、初始化DRLog */ private void initDRLogHelper() { getDrLogHelper().init(); log = Logger.getLogger(DRApplication.class); } /** * 3、初始化CrashHandler */ private void initCrashHandler() { log.trace("DRApplication: initCrashHandler()"); // 3.1 setHandler(); // 3.2 getDRCrashHandler().init(this); } /** * * 3.1 調用以下方法為mCrashHandler設置實例<br/> * <br/> * * {@link #setCrashHandler(DRCrashHandler)} */ public abstract void setHandler(); /** * 獲取DRLog單例 * * @return */ public DRLogHelper getDrLogHelper() { return mDRLogHelper; } public DRCrashHandler getDRCrashHandler() { return mDRCrashHandler; } public void setDRCrashHandler(DRCrashHandler mCrashHandler) { this.mDRCrashHandler = mCrashHandler; } /** * 添加activity到App的mActivityList * * @param activity */ public void addActivity(DRAcitivity activity) { this.mActivityList.add(activity); } /** * 遍歷mActivityList,結束每一個activity的聲明周期 */ private void finishActivityList() { for (Activity activity : mActivityList) { activity.finish(); } } @Override public void onTerminate() { log.trace("DRApplication: onTerminate()"); super.onTerminate(); appExit(); } /** * 完全退出程序 */ public void appExit() { log.trace("DRApplication: appExit()"); finishActivityList(); // 正常退出 System.exit(0); } /** * 出現異常殺掉進程 */ public void appKill() { log.trace("DRApplication: appKill()"); finishActivityList(); android.os.Process.killProcess(android.os.Process.myPid()); System.exit(1); } }
3、
首先要說明的是:前面兩個類我已經集成在DR_supprot_lib庫中,在新的應用程序中只需要繼承這兩個類,分類初始化一些參數即可。
寫個App程序測試:
3.1 新建MyCrashHandler.java繼承DRCrashHandler,復寫兩個抽象方法initParams()和sendToServer()
package cn.darkranger.test; import java.io.File; import org.apache.log4j.Logger; import android.os.Environment; import cn.dr.lib.app.DRCrashHandler; public class MyCrashHandler extends DRCrashHandler { private static final Logger log = Logger.getLogger(MyCrashHandler.class); String crashFilePath = Environment.getExternalStorageDirectory() + File.separator + "MyApp" + File.separator + "Crash.txt"; @Override public void initParams() { // 設置程序崩潰提示信息 setDRTipMsg("this is my msg - -"); // 設置程序崩潰的文件位置 setDRCrashFilePath(crashFilePath); } @Override protected void sendToServer(File logFile, File crashFile) { log.info("logFile: " + logFile + "; crashFile: " + "\naction:sendToServer - -"); // 這里要寫上傳錯誤信息到服務器的代碼,暫時不寫,看需求,自己實現 } }
3.2 新建MyApplication.java繼承DRApplication,復寫兩個抽象方法initDRLogHelperParam()和setHandler()
package cn.darkranger.test; import java.io.File; import org.apache.log4j.Logger; import android.os.Environment; import cn.dr.lib.app.DRCrashHandler; public class MyCrashHandler extends DRCrashHandler { private static final Logger log = Logger.getLogger(MyCrashHandler.class); String crashFilePath = Environment.getExternalStorageDirectory() + File.separator + "MyApp" + File.separator + "Crash.txt"; @Override public void initParams() { // 設置程序崩潰提示信息 setDRTipMsg("this is my msg - -"); // 設置程序崩潰的文件位置 setDRCrashFilePath(crashFilePath); } @Override protected void sendToServer(File logFile, File crashFile) { log.info("logFile: " + logFile + "; crashFile: " + "\naction:sendToServer - -"); // 這里要寫上傳錯誤信息到服務器的代碼,暫時不寫,看需求,自己實現 } }
3.3 新建MainActivity,這里暫時不繼承DRActivity,繼承AppCompatActivity,兩個testview,點擊第一個,調用DRApplication的退出,點擊第二個,拋出異常,我們自己捕獲。
package cn.darkranger.test; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.TextView;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView exitTv = (TextView) findViewById(R.id.id_exit); exitTv.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { ((MyApplication) getApplication()).appExit(); } }); TextView testCrashTv = (TextView) findViewById(R.id.id_testCrash); testCrashTv.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { throw new IllegalArgumentException("this is an IllegalArgumentException! "); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
3.4 更改AndroidManifest.xml(添加讀寫文件的權限,設置application為:cn.darkranger.test.MyApplication)
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.darkranger.test" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="21" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:name="cn.darkranger.test.MyApplication" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
測試:
運行,點擊第一個按鈕,退出程序,查看log文件:
[2016-06-30 22:19:09][Class: cn.dr.lib.log.DRLogHelper.initLog4j(DRLogHelper.java:89)]
[Level: INFO ] - Msg: Log4j is Ready For DRApplication!
[2016-06-30 22:19:09][Class: cn.dr.lib.app.DRApplication.initCrashHandler(DRApplication.java:87)]
[Level: TRACE] - Msg: DRApplication: initCrashHandler()
[2016-06-30 22:19:09][Class: cn.dr.lib.app.DRCrashHandler.init(DRCrashHandler.java:65)]
[Level: INFO ] - Msg: DRCrashHandler is Ready For Application!
[2016-06-30 22:19:12][Class: cn.dr.lib.app.DRApplication.appExit(DRApplication.java:152)]
[Level: TRACE] - Msg: DRApplication: appExit()
[2016-06-30 22:19:58][Class: cn.dr.lib.log.DRLogHelper.initLog4j(DRLogHelper.java:89)]
[Level: INFO ] - Msg: Log4j is Ready For DRApplication!
[2016-06-30 22:19:58][Class: cn.dr.lib.app.DRApplication.initCrashHandler(DRApplication.java:87)]
[Level: TRACE] - Msg: DRApplication: initCrashHandler()
[2016-06-30 22:19:58][Class: cn.dr.lib.app.DRCrashHandler.init(DRCrashHandler.java:65)]
[Level: INFO ] - Msg: DRCrashHandler is Ready For Application!
[2016-06-30 22:20:02][Class: cn.dr.lib.app.DRApplication.appExit(DRApplication.java:152)]
[Level: TRACE] - Msg: DRApplication: appExit()
運行,點擊第二個按鈕,拋出異常,查看log文件和crash文件:
Log.txt
[2016-06-30 22:19:09][Class: cn.dr.lib.log.DRLogHelper.initLog4j(DRLogHelper.java:89)]
[Level: INFO ] - Msg: Log4j is Ready For DRApplication!
[2016-06-30 22:19:09][Class: cn.dr.lib.app.DRApplication.initCrashHandler(DRApplication.java:87)]
[Level: TRACE] - Msg: DRApplication: initCrashHandler()
[2016-06-30 22:19:09][Class: cn.dr.lib.app.DRCrashHandler.init(DRCrashHandler.java:65)]
[Level: INFO ] - Msg: DRCrashHandler is Ready For Application!
[2016-06-30 22:19:12][Class: cn.dr.lib.app.DRApplication.appExit(DRApplication.java:152)]
[Level: TRACE] - Msg: DRApplication: appExit()
[2016-06-30 22:19:58][Class: cn.dr.lib.log.DRLogHelper.initLog4j(DRLogHelper.java:89)]
[Level: INFO ] - Msg: Log4j is Ready For DRApplication!
[2016-06-30 22:19:58][Class: cn.dr.lib.app.DRApplication.initCrashHandler(DRApplication.java:87)]
[Level: TRACE] - Msg: DRApplication: initCrashHandler()
[2016-06-30 22:19:58][Class: cn.dr.lib.app.DRCrashHandler.init(DRCrashHandler.java:65)]
[Level: INFO ] - Msg: DRCrashHandler is Ready For Application!
[2016-06-30 22:20:02][Class: cn.dr.lib.app.DRApplication.appExit(DRApplication.java:152)]
[Level: TRACE] - Msg: DRApplication: appExit()
[2016-06-30 22:20:48][Class: cn.dr.lib.log.DRLogHelper.initLog4j(DRLogHelper.java:89)]
[Level: INFO ] - Msg: Log4j is Ready For DRApplication!
[2016-06-30 22:20:48][Class: cn.dr.lib.app.DRApplication.initCrashHandler(DRApplication.java:87)]
[Level: TRACE] - Msg: DRApplication: initCrashHandler()
[2016-06-30 22:20:48][Class: cn.dr.lib.app.DRCrashHandler.init(DRCrashHandler.java:65)]
[Level: INFO ] - Msg: DRCrashHandler is Ready For Application!
[2016-06-30 22:20:50][Class: cn.dr.lib.app.DRCrashHandler.uncaughtException(DRCrashHandler.java:109)]
[Level: INFO ] - Msg: DRCrashHandler dispatcher uncaughtException!
[2016-06-30 22:20:50][Class: cn.dr.lib.app.DRCrashHandler.handlerException(DRCrashHandler.java:139)]
[Level: INFO ] - Msg: DRCrashHandler is handling Exception!
[2016-06-30 22:20:50][Class: cn.dr.lib.app.DRCrashHandler.collectDeviceInfo(DRCrashHandler.java:175)]
[Level: INFO ] - Msg: DRCrashHandler is collecting DeviceInfo!
[2016-06-30 22:20:50][Class: cn.dr.lib.app.DRCrashHandler.saveLogAndCrash(DRCrashHandler.java:206)]
[Level: INFO ] - Msg: DRCrashHandler is saving Log!
[2016-06-30 22:20:50][Class: cn.dr.lib.app.DRCrashHandler.run(DRCrashHandler.java:151)]
[Level: INFO ] - Msg: DRCrashHandler is ready send crash-info to device!
[2016-06-30 22:20:50][Class: cn.dr.lib.app.DRCrashHandler.saveLogAndCrash(DRCrashHandler.java:233)]
[Level: ERROR] - Msg: java.lang.IllegalArgumentException: this is an IllegalArgumentException!
at cn.darkranger.test.MainActivity$2.onClick(MainActivity.java:33)
at android.view.View.performClick(View.java:4792)
at android.view.View$PerformClick.run(View.java:19936)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5595)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:964)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:759)
[2016-06-30 22:20:50][Class: cn.dr.lib.app.DRCrashHandler.saveToCrashFile(DRCrashHandler.java:246)]
[Level: INFO ] - Msg: DRCrashHandler is writing crash-info to CrashFile(/storage/emulated/0/MyApp/Crash.txt)!
[2016-06-30 22:20:50][Class: cn.dr.lib.utils.FileUtil.createFileAndFolder(FileUtil.java:444)]
[Level: INFO ] - Msg: File[/storage/emulated/0/MyApp/Crash.txt] was created successfully!
[2016-06-30 22:20:50][Class: cn.darkranger.test.MyCrashHandler.sendToServer(MyCrashHandler.java:26)]
[Level: INFO ] - Msg: logFile: /storage/emulated/0/MyApp/Log.txt; crashFile:
action:sendToServer - -
[2016-06-30 22:20:53][Class: cn.dr.lib.app.DRApplication.appExit(DRApplication.java:152)]
[Level: TRACE] - Msg: DRApplication: appExit()
Crash.txt
[DateTime: 2016-06-30 22:20:50]
[DeviceInfo: ]
supported_64_bit_abis: [Ljava.lang.String;@16d078ef
versioncode: 1
board: mozart
bootloader: unknown
type: user
matchers: [Ljava.lang.String;@3d5b7685
id: HUAWEIM2-801W
time: 1437332391000
brand: HUAWEI
tag: Build
serial: TJF4C15804003585
hardware: hi3635
supported_abis: [Ljava.lang.String;@33767ffc
no_hota: false
cpu_abi: arm64-v8a
radio: unknown
is_debuggable: false
replacements: [Ljava.lang.String;@2dec2cda
manufacturer: HUAWEI
supported_32_bit_abis: [Ljava.lang.String;@3271c1ce
tags: ota-rel-keys,release-keys
cpu_abi2:
unknown: unknown
user: huawei
fingerprint: HUAWEI/M2/HWMozart:5.1.1/HUAWEIM2-801W/C233B009:user/release-keys
host: WUH1000005635
product: M2
versionname: 1.0
display: M2-801WV100R001C233B009
hide_product_info: false
model: HUAWEI M2-801W
device: HWMozart
[Excetpion: ]
java.lang.IllegalArgumentException: this is an IllegalArgumentException!
at cn.darkranger.test.MainActivity$2.onClick(MainActivity.java:33)
at android.view.View.performClick(View.java:4792)
at android.view.View$PerformClick.run(View.java:19936)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5595)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:964)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:759)