主要代码来自:http://blog.csdn.net/xiaanming/article/details/9344703
我想实现:
- 闪退后抓取日志文件,然后开启socket client发送信息给我的自动化平台socket server,然后我的自动化平台读取手机存的日志文件
先看下项目结构
Paste_Image.png
1.我们新建一个CrashHandler类 实现UncaughtExceptionHandler接口,重写回调方法void uncaughtException(Thread thread, Throwable ex)
package com.shikun.crashhandler;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
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.Environment;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
public class CrashHandler implements UncaughtExceptionHandler {
private static final String TAG = "Activity";
private Context mContext;
private static final String SDCARD_ROOT = Environment.getExternalStorageDirectory().toString();
private static CrashHandler mInstance = new CrashHandler();
private CrashHandler(){
System.out.print(SDCARD_ROOT);
Log.i("sd", SDCARD_ROOT);
}
/**
* 单例模式,保证只有一个CustomCrashHandler实例存在
* @return
*/
public static CrashHandler getInstance(){
return mInstance;
}
/**
* 异常发生时,系统回调的函数,我们在这里处理一些操作
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
savaInfoToSD(mContext, ex);
//提示用户程序即将退出
showToast(mContext, "很抱歉,程序遭遇异常,即将退出!");
try {
thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// android.os.Process.killProcess(android.os.Process.myPid());
// System.exit(1);
//完美退出程序方法
ExitAppUtils.getInstance().exit();
}
/**
* 为我们的应用程序设置自定义Crash处理
*/
public void setCustomCrashHanler(Context context){
mContext = context;
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
* 显示提示信息,需要在线程中显示Toast
* @param context
* @param msg
*/
private void showToast(final Context context, final String msg){
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
Looper.loop();
}
}).start();
}
/**
* 获取一些简单的信息,软件版本,手机版本,型号等信息存放在HashMap中
* @param context
* @return
*/
private HashMap<String, String> obtainSimpleInfo(Context context){
HashMap<String, String> map = new HashMap<String, String>();
PackageManager mPackageManager = context.getPackageManager();
PackageInfo mPackageInfo = null;
try {
mPackageInfo = mPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
map.put("versionName", mPackageInfo.versionName);
map.put("versionCode", "" + mPackageInfo.versionCode);
map.put("MODEL", "" + Build.MODEL);
map.put("SDK_INT", "" + Build.VERSION.SDK_INT);
map.put("PRODUCT", "" + Build.PRODUCT);
return map;
}
/**
* 获取系统未捕捉的错误信息
* @param throwable
* @return
*/
private String obtainExceptionInfo(Throwable throwable) {
StringWriter mStringWriter = new StringWriter();
PrintWriter mPrintWriter = new PrintWriter(mStringWriter);
throwable.printStackTrace(mPrintWriter);
mPrintWriter.close();
Log.e(TAG, mStringWriter.toString());
return mStringWriter.toString();
}
/**
* 保存获取的 软件信息,设备信息和出错信息保存在SDcard中
* @param context
* @param ex
* @return
*/
private String savaInfoToSD(Context context, Throwable ex) {
String fileName = null;
StringBuffer sb = new StringBuffer();
for (Map.Entry<String, String> entry : obtainSimpleInfo(context).entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key).append(" = ").append(value).append("\n");
}
sb.append(obtainExceptionInfo(ex));
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
File dir = new File(SDCARD_ROOT + File.separator + "crash" + File.separator);
if(! dir.exists()){
dir.mkdir();
}
try{
fileName = dir.toString() + File.separator + paserTime(System.currentTimeMillis()) + ".log";
FileOutputStream fos = new FileOutputStream(fileName);
fos.write(sb.toString().getBytes());
fos.flush();
fos.close();
}catch(Exception e){
e.printStackTrace();
}
}
String [] arr = new String[2];
arr[0] = fileName.toString();
arr[1] = Build.DEVICE;
String str = "";
for(String s : arr){
str+=","+s;
}
str = str.substring(1);
Log.i("socket_client", str);
new Thread(new Thread2(str)).start();
return fileName;
}
/**
* 将毫秒数转换成yyyy-MM-dd-HH-mm-ss的格式
* @param milliseconds
* @return
*/
private String paserTime(long milliseconds) {
System.setProperty("user.timezone", "Asia/Shanghai");
TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
TimeZone.setDefault(tz);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
String times = format.format(new Date(milliseconds));
return times;
}
}
- 完美解决闪退问题代码
package com.shikun.crashhandler;
import java.util.LinkedList;
import java.util.List;
import android.app.Activity;
/**
* android退出程序的工具类,使用单例模式
* 1.在Activity的onCreate()的方法中调用addActivity()方法添加到mActivityList
* 2.你可以在Activity的onDestroy()的方法中调用delActivity()来删除已经销毁的Activity实例
* 这样避免了mActivityList容器中有多余的实例而影响程序退出速度
* @author xiaanming
*
*/
public class ExitAppUtils {
/**
* 转载Activity的容器
*/
private List<Activity> mActivityList = new LinkedList<Activity>();
private static ExitAppUtils instance = new ExitAppUtils();
/**
* 将构造函数私有化
*/
private ExitAppUtils(){};
/**
* 获取ExitAppUtils的实例,保证只有一个ExitAppUtils实例存在
* @return
*/
public static ExitAppUtils getInstance(){
return instance;
}
/**
* 添加Activity实例到mActivityList中,在onCreate()中调用
* @param activity
*/
public void addActivity(Activity activity){
mActivityList.add(activity);
}
/**
* 从容器中删除多余的Activity实例,在onDestroy()中调用
* @param activity
*/
public void delActivity(Activity activity){
mActivityList.remove(activity);
}
/**
* 退出程序的方法
*/
public void exit(){
for(Activity activity : mActivityList){
activity.finish();
}
System.exit(0);
}
}
- 我自己重新封装了一个线程,启动socket client
package com.shikun.crashhandler;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import android.util.Log;
class Thread2 implements Runnable{
String name;
public Thread2(String name) {
this.name = name;
}
@Override
public void run() {
String host = "192.168.1.38"; //要连接的服务端IP地址
int port = 50006; //要连接的服务端对应的监听端口
//与服务端建立连接
Socket client;
try {
Log.i("socket_client", "开始连接服务器");
client = new Socket(host, port);
Log.i("socket_client", "连接服务器成功");
OutputStream os=client.getOutputStream();//字节输出流
PrintWriter pw=new PrintWriter(os);//将输出流包装为打印流
Log.i("socket_client", name);
pw.write(name);
pw.flush();
client.shutdownOutput();//关闭输出流
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e("socket_client", e.getMessage());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e("socket_client", e.getMessage());
}
}
}
将上面代码打包成jar
Paste_Image.png
Paste_Image.png
给其他app引用
- 首先给权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
- 设置application的值
<application android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:name=".CrashApplication">
- 此app 需要在自己的application引用
import android.app.Application;
import com.shikun.crashhandler.*; #引用打包的jar
public class CrashApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
CrashHandler mCrashHandler = CrashHandler.getInstance();
mCrashHandler.setCustomCrashHanler(getApplicationContext());
}
}
- 自己随便制造一个闪退
运行程序,你会发现一句很友好的“很抱歉,程序遭遇异常,即将退出”代替了冷冰冰的警告框,我们打开DDMS,在mnt/sdcard/Crash/目录下面发现了有一个文件,打开文件,我们可以看到
versionCode = 1
PRODUCT = sdk
MODEL = sdk
versionName = 1.0
SDK_INT = 8
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.customcrash/com.example.customcrash.MainActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2663)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679)
at android.app.ActivityThread.access$2300(ActivityThread.java:125)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4627)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at com.example.customcrash.MainActivity.onCreate(MainActivity.java:15)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)
... 11 more