在集成了统计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;
}
}
}