在集成了统计SDK(友盟统计,百度统计等)之后,有一个非常有利于测试的功能:错误分析!此功能能够将程序在运行中碰到的崩溃(runtimeException)问题反馈到服务器,帮助开发者改善产品,多适配机器。然而在公司android开发中不集成这些SDK,那应该怎么实现这样的功能呢?下面让我们来看下如何使用UncaughtExceptionHandler来捕获异常。
在Android开发中,常常会出现uncheched Exception 导致程序的crash,为了提供良好的用户体验,并对出错的信息进行收集,以便对程序进行改进,提高程序的健壮性。因此,常使用Thread.UncaughtExceptionHandler来进行处理。
首先在CrashHandlerApplication中进行初始化
CrashHandler crashHandler = CrashHandler.getInstance(); crashHandler.init(this);
然后我们看看在CrashHandler做了什么操作?
public void init(Context context) { mContext = context; // 获取handler mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); // 实现接口 Thread.setDefaultUncaughtExceptionHandler(this); }
Android系统的“程序异常退出”,给应用的用户体验造成不良影响。为了捕获应用运行时异常并给出友好提示,便可继承UncaughtExceptionHandler类来处理。通过Thread.setDefaultUncaughtExceptionHandler()方法将异常处理类设置到线程上即可。
在本例中第一个界面启动第二个界面之后,在第二个界面有一个bug如下,没有setContentView(R.layout.activity_XXXX);
super.onCreate(savedInstanceState); findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(SecondAty.this, "onClick", Toast.LENGTH_SHORT) .show(); } });
所以启动项目后,点击进入第二个界面,会有崩溃现象,此刻,在CrashHandler就可以捕获全局异常,捕获之后,会在接口方法uncaughtException中去做处理操作,那么我们是怎么处理的呢?
1、mDefaultHandler.uncaughtException(thread, ex);是说如果用户没有处理,则让系统默认的异常处理器来处理
2、我们会对异常做哪些处理操作?存到sd卡手机log信息,然后将log信息封装为json,然后传入到服务器,去做反馈收集
3、处理完之后呢?一般app退出会给用户良好体验,如果直接退出会感觉很low,我们可以友好的定位到某一个界面或者重启app
// android.os.Process.killProcess(android.os.Process.myPid()); // System.exit(1); // 重新启动程序 Intent intent = new Intent(); intent.setClass(mContext, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); android.os.Process.killProcess(android.os.Process.myPid());
4、然后我们回过头来,看看如何进行log日志处理的?
(4-1)我这个例子是封装为json字符串,传给服务器的,首先根据需求,我们需要构造请求头、请求体
private JsonParameters headParameters = null; private JsonParameters bodyParameters = null;
(4-2)JsonParameters中就是构造键值对信息,我们这个例子就简单收集下手机信息即可
if (devicemodel != null) headParameters.add("devicemodel", CrashHandlerApplication .getInstance().getDevice()); if (appversion != null) headParameters.add("appversion", CrashHandlerApplication .getInstance().getVersion()); if (osversion != null) headParameters.add("osversion", CrashHandlerApplication .getInstance().getOsVersion());
(4-3)log产生后,可以将log日子,输出,在保存到sd卡的同时,进行封装请求体信息
以下代码是将log信息,输出字符串
Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); // 输出 printStackTrace 堆栈信息 ex.printStackTrace(printWriter); Throwable cause = ex.getCause(); // 循环着把所有的异常信息写入writer中 while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.close(); // 将log信息输出字符串 String result = writer.toString();
如下就是本例子的错误日志输出结果:
result==>java.lang.RuntimeException: Unable to start activity * ComponentInfo * {com.example.crashhandler/com.example.crashhandler.SecondAty}: * java.lang.NullPointerException: Attempt to invoke virtual method * 'void android.view.View.setOnClickListener(android.view. * View$OnClickListener)' on a null object reference
(4-4)然后存到sd卡saveCrashToFile方法就不介绍了
(4-5)然后就封装为json的项目需要格式,工具类就不介绍了,一下就是日志log的Json格式
String requestJson = ProtocolUtil.buildJSONPacketBody(headParameters, bodyParameters);
* handljson===:{ * "Request": { * "head": { 07-15 * "devicemodel": * "AOSP on HammerHead", 07-15 14:51:19.698: * "appversion": "1.0", 07-15 * "osversion": "5.1.1" * }, * "body": { * "context": * "java.lang.RuntimeException: Unable to start activity * ComponentInfo{com * .example.crashhandler\/com.example.crashhandler.SecondAty}: * java.lang.NullPointerException: Attempt to invoke virtual method * 'void android.view.View.setOnClickListener(android.view. * View$OnClickListener)' on a null object reference\n\tat * android.app.ActivityThread * .performLaunchActivity(ActivityThread.java:2325)\n\tat * android.app.ActivityThread * .handleLaunchActivity(ActivityThread.java:2387)\n\tat * android.app.ActivityThread.access$800(ActivityThread.java:151)\n\tat * android * .app.ActivityThread$H.handleMessage(ActivityThread.java:1303)\n\tat * android.os.Handler.dispatchMessage(Handler.java:102)\n\tat * android.os.Looper.loop(Looper.java:135)\n\tat * android.app.ActivityThread.main(ActivityThread.java:5254)\n\tat * java.lang.reflect.Method.invoke(Native Method)\n\tat * java.lang.reflect.Method.invoke(Method.java:372)\n\tat * com.android.internal * .os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)\n\tat * com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)\n\tat * de.robv.android.xposed.XposedBridge.main(Unknown Source)\nCaused by: * java.lang.NullPointerException: Attempt to invoke virtual method * 'void android.view.View.setOnClickListener(android.view. * View$OnClickListener)' on a null object reference\n\tat * com.example.crashhandler.SecondAty.onCreate(SecondAty.java:14)\n\tat * android.app.Activity.performCreate(Activity.java:5990)\n\tat * android.app * .Instrumentation.callActivityOnCreate(Instrumentation.java: * 1106)\n\tat * android.app.ActivityThread.performLaunchActivity(ActivityThread * .java:2278)\n\t... 11 more\njava.lang.NullPointerException: Attempt * to invoke virtual method 'void * android.view.View.setOnClickListener(android * .view.View$OnClickListener)' on a null object reference\n\tat * com.example.crashhandler.SecondAty.onCreate(SecondAty.java:14)\n\tat * android.app.Activity.performCreate(Activity.java:5990)\n\tat * android.app * .Instrumentation.callActivityOnCreate(Instrumentation.java: * 1106)\n\tat * android.app.ActivityThread.performLaunchActivity(ActivityThread * .java:2278)\n\tat * android.app.ActivityThread.handleLaunchActivity(ActivityThread * .java:2387)\n\tat * android.app.ActivityThread.access$800(ActivityThread.java:151)\n\tat * android * .app.ActivityThread$H.handleMessage(ActivityThread.java:1303)\n\tat * android.os.Handler.dispatchMessage(Handler.java:102)\n\tat * android.os.Looper.loop(Looper.java:135)\n\tat * android.app.ActivityThread.main(ActivityThread.java:5254)\n\tat * java.lang.reflect.Method.invoke(Native Method)\n\tat * java.lang.reflect.Method.invoke(Method.java:372)\n\tat * com.android.internal * .os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)\n\tat * com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)\n\tat * de.robv.android.xposed.XposedBridge.main(Unknown Source)\n", * "md5_value": * "911fcae78ea4891892967ffd3d6034a8" * } * } * }
(4-6)在JSONUtil才是封装为json对象,而ProtocolUtil只是我公司代码中的数据格式
(4-7)最后就传服务器,判断网络,接受服务器反馈成功即可
######################################以下代码########################################
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.crashhandler.MainActivity" > <TextView android:id="@+id/tv" android:gravity="center" android:layout_width="100dp" android:layout_height="100dp" android:background="#ccc" android:text="first pager" /> </RelativeLayout>
activity_sec.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.crashhandler.MainActivity" > <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="second pager" /> </RelativeLayout>
SecondAty
package com.example.crashhandler; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Toast; public class SecondAty extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(SecondAty.this, "onClick", Toast.LENGTH_SHORT) .show(); } }); } }
MainActivity
package com.example.crashhandler; public class Crash { private String name; private int size; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public Crash(String name, int size) { super(); this.name = name; this.size = size; } public Crash() { super(); // TODO Auto-generated constructor stub } }
package com.example.crashhandler; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import android.R.integer; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Toast; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "onClick", Toast.LENGTH_SHORT) .show(); startActivity(new Intent(MainActivity.this, SecondAty.class)); } }); Crash crash = new Crash("1", 1); Crash crash2 = new Crash("2", 2); /** * 对象转json object2json:{"name":"1","size":1} */ String object2json = JSONUtil.object2json(crash); LogUtils.i("Safly", "object2json:" + object2json); /** * bean2json:{"name":"1","size":1} * */ String bean2json = JSONUtil.bean2json(crash); LogUtils.i("Safly", "bean2json:" + bean2json); /** * list集合转json list2json:[{"name":"1","size":1},{"name":"2","size":2}] */ ArrayList<Crash> crashs = new ArrayList<Crash>(); crashs.add(crash); crashs.add(crash2); String list2json = JSONUtil.list2json(crashs); LogUtils.i("Safly", "list2json:" + list2json); /** * 数组转json array2json:[{"name":"1","size":1},{"name":"2","size":2}] */ Crash[] crashsArr = new Crash[] { crash, crash2 }; String array2json = JSONUtil.array2json(crashsArr); LogUtils.i("Safly", "array2json:" + array2json); /** * map转json * map2json:{"1":{"name":"1","size":1},"2":{"name":"2","size":2}} */ Map<Integer, Crash> crashMap = new HashMap<Integer, Crash>(); crashMap.put(1, crash); crashMap.put(2, crash2); String map2json = JSONUtil.map2json(crashMap); LogUtils.i("Safly", "map2json:" + map2json); /** * set转换为json set2json:[{"name":"1","size":1},{"name":"2","size":2}] */ HashSet<Crash> set = new HashSet<Crash>(); set.add(crash); set.add(crash2); String set2json = JSONUtil.set2json(set); LogUtils.i("Safly", "set2json:" + set2json); /** * objToMap * key= size and value= 1 * key= name and value= 1 */ Map<String, Object> objToMap = JSONUtil.objToMap(crash); for (String key : objToMap.keySet()) { LogUtils.i("Safly", "key= " + key + " and value= " + objToMap.get(key)); } } }
CrashHandlerApplication
package com.example.crashhandler; import android.app.Application; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; public class CrashHandlerApplication extends Application { private static CrashHandlerApplication instance; @Override public void onCreate() { super.onCreate(); this.instance = this; CrashHandler crashHandler = CrashHandler.getInstance(); crashHandler.init(this); } /** * 手机的版本 比如5.1.1 */ public String getOsVersion() { return Build.VERSION.RELEASE; } /** * 手机名称 比如"devicemodel": "AOSP on HammerHead", * * @return */ public String getDevice() { return Build.MODEL; } public static CrashHandlerApplication getInstance() { return instance; } /** * 项目version * * @return */ public String getVersion() { String version = "0.0.0"; try { PackageInfo packageInfo = instance.getPackageManager() .getPackageInfo(instance.getPackageName(), 0); version = packageInfo.versionName; } catch (NameNotFoundException e) { e.printStackTrace(); } return version; } }
CrashHandler
package com.example.crashhandler; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.lang.Thread.UncaughtExceptionHandler; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import android.content.Context; import android.content.Intent; import android.os.Environment; import android.os.Looper; import android.util.Log; import android.widget.Toast; public class CrashHandler implements UncaughtExceptionHandler { public static final String TAG = "CrashHandler"; private Context mContext; private JsonParameters headParameters = null; private JsonParameters bodyParameters = null; private String devicemodel = null; private String appversion = null; private String osversion = null; private CrachToServer crachToServer = null; private UncaughtExceptionHandler mDefaultHandler; // 单例模式 private static CrashHandler INSTANCE = new CrashHandler(); private CrashHandler() { } public static CrashHandler getInstance() { return INSTANCE; } private SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd-HH-mm-ss");// public void init(Context context) { mContext = context; // 获取handler mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); // 实现接口 Thread.setDefaultUncaughtExceptionHandler(this); } /** * 实现接口方法 */ @Override public void uncaughtException(Thread thread, Throwable ex) { /** * 07-14 22:44:49.198: E/Safly(20806): mainjava.lang.RuntimeException: * Unable to start activity ComponentInfo{com * .example.crashhandler/com.example.crashhandler.MainActivity}: * java.lang.NullPointerException: Attempt to invoke virtual method * 'void android.view.View.setOnClickListener(android.view. * View$OnClickListener)' on a null object reference */ Log.e(TAG, thread.getName() + ex.toString()); if (!handleException(ex) && mDefaultHandler != null) { // 如果用户没有处理则让系统默认的异常处理器来处理 mDefaultHandler.uncaughtException(thread, ex); } else { try { Thread.sleep(3000); } catch (InterruptedException e) { LogUtils.e(TAG, "error : " + e); } // 退出程序 // android.os.Process.killProcess(android.os.Process.myPid()); // System.exit(1); // 重新启动程序 Intent intent = new Intent(); intent.setClass(mContext, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); android.os.Process.killProcess(android.os.Process.myPid()); } } /** * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成 * * @param ex * @return */ private boolean handleException(Throwable ex) { if (ex == null) { return false; } new Thread() { @Override public void run() { Looper.prepare(); Toast.makeText(mContext, "很抱歉,程序出现异常,正在收集日志,即将退出。", Toast.LENGTH_LONG).show(); Looper.loop(); } }.start(); // 請求头 headParameters = new JsonParameters(); collectDeviceInfo(mContext); // 请求体 bodyParameters = new JsonParameters(); saveCrashInfo2FileToServer(ex); return true; } /** * 收集手机的信息 * * @param ctx */ public void collectDeviceInfo(Context ctx) { devicemodel = CrashHandlerApplication.getInstance().getDevice(); appversion = CrashHandlerApplication.getInstance().getVersion(); osversion = CrashHandlerApplication.getInstance().getOsVersion(); if (devicemodel != null) headParameters.add("devicemodel", CrashHandlerApplication .getInstance().getDevice()); if (appversion != null) headParameters.add("appversion", CrashHandlerApplication .getInstance().getVersion()); if (osversion != null) headParameters.add("osversion", CrashHandlerApplication .getInstance().getOsVersion()); } /** * 存储sd卡,封装json,并且传服务器 * * @param ex * @return */ private void saveCrashInfo2FileToServer(Throwable ex) { /** * 将log信息日志输出 */ Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); // 输出 printStackTrace 堆栈信息 ex.printStackTrace(printWriter); Throwable cause = ex.getCause(); // 循环着把所有的异常信息写入writer中 while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.close(); // 将log信息输出字符串 String result = writer.toString(); LogUtils.i(TAG, "result==>" + result.toString()); /** * 存储sd卡 */ saveCrashToFile(result); bodyParameters.add("context", result); /** * 返回实现指定摘要算法的 MessageDigest 对象。 用于MD5加密的 */ MessageDigest md = null; try { md = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } // md.digest() 该函数返回值为存放哈希值结果的byte数组 byte[] b = md.digest(result.getBytes()); String res = ""; for (int i = 0; i < b.length; i++) { // 为了显示一个byte型的单字节十六进制(两位十六进制表示)的编码 String tmp = Integer.toHexString(b[i] & 0xFF); if (tmp.length() == 1) { res += "0" + tmp; } else { res += tmp; } } // res===:e3ab6e147ebbfc0dcc6bf072a92b63bc LogUtils.i(TAG, "md5_value===:" + res); bodyParameters.add("md5_value", res); /** * 封装json对象 */ String requestJson = ProtocolUtil.buildJSONPacketBody(headParameters, bodyParameters); LogUtils.i(TAG, "handljson===:" + requestJson); if (requestJson != null) { crachToServer = new CrachToServer(requestJson); new Thread() { @Override public void run() { if (IntenetUtil.getNetworkState(mContext) != 0) { LogUtils.i(TAG, "getNetworkState===conected:"); crachToServer.uploadFile(); } } }.start(); } } /** * 存储sd * * @param logResult */ public void saveCrashToFile(String logResult) { long timetamp = System.currentTimeMillis(); String time = format.format(new java.util.Date()); String fileName = "crash-" + time + "-" + timetamp + ".log"; if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { try { File dir = new File(Environment.getExternalStorageDirectory() .getAbsolutePath() + File.separator + "crash"); Log.i(TAG, "dir:" + dir.toString()); // dir:/storage/emulated/0/crash if (!dir.exists()) dir.mkdir(); FileOutputStream fos = new FileOutputStream(new File(dir, fileName)); fos.write(logResult.toString().getBytes()); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } }
JsonParameters 键值对类
package com.example.crashhandler; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class JsonParameters { private List<String> list = null; private Map<String, Object> map = null; public JsonParameters() { list = new ArrayList<String>(); map = new HashMap<String, Object>(); } public void add(String key, Object value) { if(list.contains(key)){ list.remove(key); } list.add(key); map.put(key, value); } public String getKey(int id) { return list.get(id); } public Object getValue(String key) { return map.get(key); } public int size(){ return list.size(); } public boolean validate(){ return list.size() == map.size(); } public void clear(){ list.clear(); map.clear(); list = null; map = null; } }
构造特定格式的json类
ProtocolUtil
package com.example.crashhandler; import android.annotation.SuppressLint; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; /** * simple tools to handle protocol for apps. */ public class ProtocolUtil { public static final String COLON = ": "; public static final String LEFT_ANGLE_BRACKET = "{"; public static final String RIGHT_ANGLE_BRACKET = "}"; /** * @param headValues * @param bodyValues */ public static String buildJSONPacketBody(JsonParameters headValues, JsonParameters bodyValues) { if ((headValues != null && !headValues.validate()) || (bodyValues != null && !bodyValues.validate())) { throw new IllegalArgumentException(); } StringBuffer sb = new StringBuffer(LEFT_ANGLE_BRACKET + "\r\n"); sb.append("\t\"Request\"" + COLON + LEFT_ANGLE_BRACKET + "\r\n"); sb.append("\t\t\"head\"" + COLON + LEFT_ANGLE_BRACKET + "\r\n"); for (int i = 0; i < headValues.size(); i++) { sb.append( "\t\t\t\"" + headValues.getKey(i).toLowerCase() + "\"" + COLON).append( JSONUtil.object2json(headValues.getValue(headValues .getKey(i)))); if (i != headValues.size() - 1) { sb.append(","); } sb.append("\r\n"); } sb.append("\t\t" + RIGHT_ANGLE_BRACKET + ",\r\n"); sb.append("\t\t\"body\"" + COLON + LEFT_ANGLE_BRACKET + "\r\n"); for (int i = 0; i < bodyValues.size(); i++) { sb.append( "\t\t\t\"" + bodyValues.getKey(i).toLowerCase() + "\"" + COLON).append( JSONUtil.object2json(bodyValues.getValue(bodyValues .getKey(i)))); if (i != bodyValues.size() - 1) { sb.append(","); } sb.append("\r\n"); } sb.append("\t\t" + RIGHT_ANGLE_BRACKET + "\r\n"); sb.append("\t" + RIGHT_ANGLE_BRACKET + "\r\n"); sb.append(RIGHT_ANGLE_BRACKET); headValues.clear(); bodyValues.clear(); return sb.toString(); } }
json分装类
JSONUtil
package com.example.crashhandler; import java.lang.reflect.Field; import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.google.gson.Gson; public class JSONUtil { /** 对象转换为json */ public static String object2json(Object obj) { StringBuilder json = new StringBuilder(); if (obj == null) { json.append("\"\""); } else if (obj instanceof String || obj instanceof Integer || obj instanceof Float || obj instanceof Boolean || obj instanceof Short || obj instanceof Double || obj instanceof Long || obj instanceof BigDecimal || obj instanceof BigInteger || obj instanceof Byte) { json.append("\"").append(string2json(obj.toString())).append("\""); } else if (obj instanceof Object[]) { json.append(array2json((Object[]) obj)); } else if (obj instanceof List) { json.append(list2json((List<?>) obj)); } else if (obj instanceof Map) { json.append(map2json((Map<?, ?>) obj)); } else if (obj instanceof Set) { json.append(set2json((Set<?>) obj)); } else { json.append(bean2json(obj)); } return json.toString(); } /** 对象转换为json */ public static String bean2json(Object bean) { Gson gson = new Gson(); return gson.toJson(bean); } /** List转换为json */ public static String list2json(List<?> list) { StringBuilder json = new StringBuilder(); json.append("["); if (list != null && list.size() > 0) { for (Object obj : list) { json.append(object2json(obj)); json.append(","); } json.setCharAt(json.length() - 1, ']'); } else { json.append("]"); } return json.toString(); } /** 数组转换为json */ public static String array2json(Object[] array) { StringBuilder json = new StringBuilder(); json.append("["); if (array != null && array.length > 0) { for (Object obj : array) { json.append(object2json(obj)); json.append(","); } json.setCharAt(json.length() - 1, ']'); } else { json.append("]"); } return json.toString(); } /** map转换为json */ public static String map2json(Map<?, ?> map) { StringBuilder json = new StringBuilder(); json.append("{"); if (map != null && map.size() > 0) { for (Object key : map.keySet()) { json.append(object2json(key)); json.append(":"); json.append(object2json(map.get(key))); json.append(","); } json.setCharAt(json.length() - 1, '}'); } else { json.append("}"); } return json.toString(); } /** set转换为json */ public static String set2json(Set<?> set) { StringBuilder json = new StringBuilder(); json.append("["); if (set != null && set.size() > 0) { for (Object obj : set) { json.append(object2json(obj)); json.append(","); } json.setCharAt(json.length() - 1, ']'); } else { json.append("]"); } return json.toString(); } public static String string2json(String s) { if (s == null) return ""; StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); switch (ch) { case '"': sb.append("\\\""); break; case '\\': sb.append("\\\\"); break; case '\b': sb.append("\\b"); break; case '\f': sb.append("\\f"); break; case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\t': sb.append("\\t"); break; case '/': sb.append("\\/"); break; default: if (ch >= '\u0000' && ch <= '\u001F') { String ss = Integer.toHexString(ch); sb.append("\\u"); for (int k = 0; k < 4 - ss.length(); k++) { sb.append('0'); } sb.append(ss.toUpperCase()); } else { sb.append(ch); } } } return sb.toString(); } /** * 对象转map * * @param obj * @return */ public static Map<String, Object> objToMap(Object obj) { Map<String, Object> map = new HashMap<String, Object>(); try { /* * BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass()); * PropertyDescriptor[] propertyDescriptors = beanInfo * .getPropertyDescriptors(); for (PropertyDescriptor property : * propertyDescriptors) { String key = property.getName(); // * 过滤class属性 if (!key.equals("class")) { // 得到property对应的getter方法 * Method getter = property.getReadMethod(); Object value = * getter.invoke(obj); map.put(key, value); } } */ Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { String key = field.getName(); boolean accessFlag = field.isAccessible(); field.setAccessible(true); Object val = field.get(obj); if (val == null) { val = ""; } map.put(key, val); field.setAccessible(accessFlag); } } catch (Exception e) { e.printStackTrace(); } return map; } }
CrachToServer网络请求
package com.example.crashhandler; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.Socket; import java.net.URL; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.http.conn.ssl.SSLSocketFactory; import android.util.Log; /** * Created by Safly on 2016/7/5. * */ public class CrachToServer { public static final String TAG = "CrashHandler"; private String urlString = "https://10.0.5.66:7771/ExceptionLog"; private String content = null; public CrachToServer(String requestJson) { this.content = requestJson; } /* 上传文件至Server的方法 */ public void uploadFile() { try { LogUtils.d(TAG, "uploadFile"); byte[] requestStringBytes = content.getBytes("UTF-8"); URL url = new URL(urlString); initHttps(); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5 * 1000); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-length", "" + requestStringBytes.length); conn.setRequestProperty( "Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*"); conn.setRequestProperty("Accept-Language", "zh-CN"); conn.setRequestProperty("Referer", urlString); conn.setRequestProperty("Charset", "UTF-8"); conn.setRequestProperty( "User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"); conn.setRequestProperty("Connection", "Keep-Alive"); Log.e(TAG, "HttpsURLConnection"); conn.connect(); OutputStream outputStream = conn.getOutputStream(); outputStream.write(requestStringBytes); outputStream.close(); /* 服务器返回的响应码 */ int code = conn.getResponseCode(); if (code == 200) { LogUtils.d(TAG, "response code:" + code); } else { LogUtils.d(TAG, "response code:" + code); } } catch (Exception e) { e.printStackTrace(); } } private void initHttps() { try { KeyStore trustStore = KeyStore.getInstance(KeyStore .getDefaultType()); trustStore.load(null, null); SSLSocketFactoryEx sf = new SSLSocketFactoryEx(trustStore); HttpsURLConnection.setDefaultSSLSocketFactory(sf.getSSLContext() .getSocketFactory()); HttpsURLConnection .setDefaultHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); } catch (Exception e) { } } static class SSLSocketFactoryEx extends SSLSocketFactory { SSLContext sslContext = SSLContext.getInstance("TLS"); public SSLSocketFactoryEx(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { super(truststore); TrustManager tm = new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted( java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException { } @Override public void checkServerTrusted( java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException { } }; sslContext.init(null, new TrustManager[] { tm }, null); } @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); } @Override public Socket createSocket() throws IOException { return sslContext.getSocketFactory().createSocket(); } public SSLContext getSSLContext() { return sslContext; } } }
最后还有网络状态类,和log日志类
IntenetUtil
package com.example.crashhandler; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.telephony.TelephonyManager; public class IntenetUtil { // 没有网络连接 public static final int NETWORN_NONE = 0; // wifi连接 public static final int NETWORN_WIFI = 1; // 手机网络数据连接类型 public static final int NETWORN_2G = 2; public static final int NETWORN_3G = 3; public static final int NETWORN_4G = 4; public static final int NETWORN_MOBILE = 5; /** * 获取当前网络连接类型 * * @param context * @return */ public static int getNetworkState(Context context) { // 获取系统的网络服务 ConnectivityManager connManager = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); // 如果当前没有网络 if (null == connManager) return NETWORN_NONE; // 获取当前网络类型,如果为空,返回无网络 NetworkInfo activeNetInfo = connManager.getActiveNetworkInfo(); if (activeNetInfo == null || !activeNetInfo.isAvailable()) { return NETWORN_NONE; } // 判断是不是连接的是不是wifi NetworkInfo wifiInfo = connManager .getNetworkInfo(ConnectivityManager.TYPE_WIFI); if (null != wifiInfo) { NetworkInfo.State state = wifiInfo.getState(); if (null != state) if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) { return NETWORN_WIFI; } } // 如果不是wifi,则判断当前连接的是运营商的哪种网络2g、3g、4g等 NetworkInfo networkInfo = connManager .getNetworkInfo(ConnectivityManager.TYPE_MOBILE); if (null != networkInfo) { NetworkInfo.State state = networkInfo.getState(); String strSubTypeName = networkInfo.getSubtypeName(); if (null != state) if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) { switch (activeNetInfo.getSubtype()) { // 如果是2g类型 case TelephonyManager.NETWORK_TYPE_GPRS: // 联通2g case TelephonyManager.NETWORK_TYPE_CDMA: // 电信2g case TelephonyManager.NETWORK_TYPE_EDGE: // 移动2g case TelephonyManager.NETWORK_TYPE_1xRTT: case TelephonyManager.NETWORK_TYPE_IDEN: return NETWORN_2G; // 如果是3g类型 case TelephonyManager.NETWORK_TYPE_EVDO_A: // 电信3g case TelephonyManager.NETWORK_TYPE_UMTS: case TelephonyManager.NETWORK_TYPE_EVDO_0: case TelephonyManager.NETWORK_TYPE_HSDPA: case TelephonyManager.NETWORK_TYPE_HSUPA: case TelephonyManager.NETWORK_TYPE_HSPA: case TelephonyManager.NETWORK_TYPE_EVDO_B: case TelephonyManager.NETWORK_TYPE_EHRPD: case TelephonyManager.NETWORK_TYPE_HSPAP: return NETWORN_3G; // 如果是4g类型 case TelephonyManager.NETWORK_TYPE_LTE: return NETWORN_4G; default: // 中国移动 联通 电信 三种3G制式 if (strSubTypeName.equalsIgnoreCase("TD-SCDMA") || strSubTypeName.equalsIgnoreCase("WCDMA") || strSubTypeName.equalsIgnoreCase("CDMA2000")) { return NETWORN_3G; } else { return NETWORN_MOBILE; } } } } return NETWORN_NONE; } }
LogUtils
package com.example.crashhandler; import android.util.Log; public class LogUtils { private static final String HEAD_TAG = "GLauncher_"; public static void v(Class<?> clazz,String logInfo){ i(clazz.getSimpleName(),logInfo); } public static void v(String tag,String logInfo){ printLogInfo(tag,logInfo,Log.VERBOSE); } public static void i(Class<?> clazz,String logInfo){ i(clazz.getSimpleName(),logInfo); } public static void i(String tag,String logInfo){ printLogInfo(tag,logInfo,Log.INFO); } public static void d(Class<?> clazz,String logInfo){ d(clazz.getSimpleName(),logInfo); } public static void d(String tag,String logInfo){ printLogInfo(tag,logInfo,Log.DEBUG); } public static void w(Class<?> clazz,String logInfo){ w(clazz.getSimpleName(),logInfo); } public static void w(String tag,String logInfo){ printLogInfo(tag,logInfo,Log.WARN); } public static void e(Class<?> clazz,String logInfo){ e(clazz.getSimpleName(),logInfo); } public static void e(String tag,String logInfo){ printLogInfo(tag,logInfo,Log.ERROR); } public static void printLogInfo(String tag,String logInfo,int style){ switch (style) { case Log.VERBOSE: Log.v(HEAD_TAG + tag,logInfo); break; case Log.INFO: Log.i(HEAD_TAG + tag,logInfo); break; case Log.DEBUG: Log.d(HEAD_TAG + tag,logInfo); break; case Log.ERROR: Log.e(HEAD_TAG + tag,logInfo); break; case Log.WARN: Log.w(HEAD_TAG + tag,logInfo); break; } } }