Android之崩潰日誌管理(原創)

文章大綱

一、Android崩潰日誌管理簡介
二、崩潰日誌管理實戰
三、項目源碼下載

 

一、Android崩潰日誌管理簡介

1. 什麼是android崩潰日誌管理

  開發中有些地方未注意可能造成異常拋出未能caught到,然後彈出系統對話框強制退出。這種交互不好,而且開發者也不能及時獲取到底哪裏出問題。因此我們可以使用android的UncaughtExceptionHandler來處理這種異常。

2. 操作邏輯

用戶端(出現崩潰)
  我們會封裝一個通用的jar包,該jar包包括日誌打印、捕獲異常信息邏輯、網絡傳輸、設置Debug和Release模式、獲取本機的相關信息等,當出現異常時,將異常信息以文件方式保存在用戶手機中,並且發送到後臺,當後臺接收成功時,自動刪除用戶手機的崩潰信息文件,若接收失敗,在下次發生崩潰時,將歷史發送失敗的崩潰一同發送。

接收端(後臺)
  我們會編寫一個地址,用於接收異常的具體信息,並儲存在本地文件中,以此作爲日誌進行管理。

二、崩潰日誌管理實戰

1. 後臺端

  在該實戰中,我以簡單的servlet進行講解,實際項目中,可以以ssm或spring boot等框架進行操作。

/**
 * 接收崩潰信息,並進行打印(實際項目中,需要以文件形式歸檔)
 * @author wxc
 *
 */
public class Test extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        doPost(request, response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        //獲取客戶端傳送過來的信息流
        BufferedReader in=new BufferedReader(new InputStreamReader(request.getInputStream()));
        
        StringBuilder sb = new StringBuilder();   
           
        String line = null; 
        
        while ((line = in.readLine()) != null) {   
            
                //將信息流進行打印
               System.out.println(line);  
        } 

        

    }

}

2. 客戶端通用項目

網絡請求相關的配置管理類:HttpManager.java

/**
 * 
 * 網絡請求相關的配置管理
 * 
 * @author 吳曉暢
 *
 */
public class HttpManager {

    private static final int SET_CONNECTION_TIMEOUT = 5 * 1000;
    private static final int SET_SOCKET_TIMEOUT = 20 * 1000;

    private static final String BOUNDARY = getBoundry();// UUID.randomUUID().toString();
    private static final String MP_BOUNDARY = "--" + BOUNDARY;
    private static final String END_MP_BOUNDARY = "--" + BOUNDARY + "--";
    private static final String LINEND = "\r\n";
    
    private static final String CHARSET = "UTF-8";

    public static String uploadFile(String url, HttpParameters params,
            File logFile) throws IOException{
        
        HttpClient client = getHttpClient();

        HttpPost post = new HttpPost(url);
        
        ByteArrayOutputStream bos = null;
        
        FileInputStream logFileInputStream = null;
        
        String result = null;

        try {
            
            bos = new ByteArrayOutputStream();
            
            if(params != null){
                String key = "";
                for (int i = 0; i < params.size(); i++) {
                    key = params.getKey(i);
                    StringBuilder temp = new StringBuilder(10);
                    temp.setLength(0);
                    temp.append(MP_BOUNDARY).append(LINEND);
                    temp.append("content-disposition: form-data; name=\"").append(key)
                            .append("\"").append(LINEND + LINEND);
                    temp.append(params.getValue(key)).append(LINEND);
                    bos.write(temp.toString().getBytes());
                }
            }
            
            StringBuilder temp = new StringBuilder();
            temp.append(MP_BOUNDARY).append(LINEND);
            temp.append(
                    "content-disposition: form-data; name=\"logfile\"; filename=\"")
                    .append(logFile.getName()).append("\"").append(LINEND);
            temp.append("Content-Type: application/octet-stream; charset=utf-8").append(LINEND + LINEND);
            bos.write(temp.toString().getBytes());
            logFileInputStream = new FileInputStream(logFile);
            byte[] buffer = new byte[1024*8];//8k
            while(true){
                int count = logFileInputStream.read(buffer);
                if(count == -1){
                    break;
                }
                bos.write(buffer, 0, count);
            }
            
            bos.write((LINEND+LINEND).getBytes());
            bos.write((END_MP_BOUNDARY+LINEND).getBytes());
            
            ByteArrayEntity formEntity = new ByteArrayEntity(bos.toByteArray());
            post.setEntity(formEntity); 
            HttpResponse response = client.execute(post);
            StatusLine status = response.getStatusLine();
            int statusCode = status.getStatusCode();
            
            Log.i("HttpManager", "返回結果爲"+statusCode);
            if(statusCode == HttpStatus.SC_OK){
                result = readHttpResponse(response);
            }
            
        } catch (IOException e) {
            throw e;
        }finally{
            if(bos != null){
                try {
                    bos.close();
                } catch (IOException e) {
                    throw e;
                }
            }
            if(logFileInputStream != null){
                try {
                    logFileInputStream.close();
                } catch (IOException e) {
                    throw e;
                }
            }
        }
        
        return result;
    }
    
    private static String readHttpResponse(HttpResponse response){
        String result = null;
        HttpEntity entity = response.getEntity();
        InputStream inputStream;
        
        try {
            inputStream = entity.getContent();
            ByteArrayOutputStream content = new ByteArrayOutputStream();
            int readBytes = 0;
            byte[] sBuffer = new byte[512];
            while ((readBytes = inputStream.read(sBuffer)) != -1) {
                content.write(sBuffer, 0, readBytes);
            }
            result = new String(content.toByteArray(), CHARSET);
            return result;
            
        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
        
    }

    private static HttpClient getHttpClient() {

        try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore
                    .getDefaultType());
            trustStore.load(null, null);
            SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
            sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            HttpParams params = new BasicHttpParams();

            HttpConnectionParams.setConnectionTimeout(params, 10000);
            HttpConnectionParams.setSoTimeout(params, 10000);

            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
            HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

            SchemeRegistry registry = new SchemeRegistry();
            registry.register(new Scheme("http", PlainSocketFactory
                    .getSocketFactory(), 80));
            registry.register(new Scheme("https", sf, 443));

            ClientConnectionManager ccm = new ThreadSafeClientConnManager(
                    params, registry);

            HttpConnectionParams.setConnectionTimeout(params,
                    SET_CONNECTION_TIMEOUT);
            HttpConnectionParams.setSoTimeout(params, SET_SOCKET_TIMEOUT);
            HttpClient client = new DefaultHttpClient(ccm, params);
            return client;
        } catch (Exception e) {
            // e.printStackTrace();
            return new DefaultHttpClient();
        }
    }

    private static class MySSLSocketFactory extends SSLSocketFactory {

        SSLContext sslContext = SSLContext.getInstance("TLS");

        public MySSLSocketFactory(KeyStore truststore)
                throws NoSuchAlgorithmException, KeyManagementException,
                KeyStoreException, UnrecoverableKeyException {
            super(truststore);

            TrustManager tm = new X509TrustManager() {

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    // TODO Auto-generated method stub
                    return null;
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain,
                        String authType) throws CertificateException {
                    // TODO Auto-generated method stub

                }

                @Override
                public void checkClientTrusted(X509Certificate[] chain,
                        String authType) throws CertificateException {
                    // TODO Auto-generated method stub

                }
            };

            sslContext.init(null, new TrustManager[] { tm }, null);
        }

        @Override
        public Socket createSocket() throws IOException {
            return sslContext.getSocketFactory().createSocket();
        }

        @Override
        public Socket createSocket(Socket socket, String host, int port,
                boolean autoClose) throws IOException, UnknownHostException {
            return sslContext.getSocketFactory().createSocket(socket, host,
                    port, autoClose);
        }

    }

    private static String getBoundry() {
        StringBuffer _sb = new StringBuffer();
        for (int t = 1; t < 12; t++) {
            long time = System.currentTimeMillis() + t;
            if (time % 3 == 0) {
                _sb.append((char) time % 9);
            } else if (time % 3 == 1) {
                _sb.append((char) (65 + time % 26));
            } else {
                _sb.append((char) (97 + time % 26));
            }
        }
        return _sb.toString();
    }
}

文件上傳相關類:UploadLogManager.java

package com.qihoo.linker.logcollector.upload;

import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;

import com.qihoo.linker.logcollector.capture.LogFileStorage;

import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

/**
 * 
 * @author 吳曉暢
 *
 */
public class UploadLogManager {
    
    private static final String TAG = UploadLogManager.class.getName();
    
    private static UploadLogManager sInstance;
    
    private Context mContext;
    
    private HandlerThread mHandlerThread;
    
    private static volatile MyHandler mHandler;
    
    private volatile Looper mLooper;
    
    private volatile boolean isRunning = false;
    
    private String url;
    
    private HttpParameters params;
    
    private UploadLogManager(Context c){
        mContext = c.getApplicationContext();
        mHandlerThread = new HandlerThread(TAG + ":HandlerThread");
        mHandlerThread.start();
        
        
    }

    //初始化UploadLogManager類
    public static synchronized UploadLogManager getInstance(Context c){
        if(sInstance == null){
            sInstance = new UploadLogManager(c);
        }
        return sInstance;
    }
    
    /**
     * 執行文件上傳具體操作
     * 
     * @param url
     * @param params
     */
    public void uploadLogFile(String url , HttpParameters params){
        this.url = url;
        this.params = params;
        
        mLooper = mHandlerThread.getLooper();
        mHandler = new MyHandler(mLooper);
        if(mHandlerThread == null){
            return;
        }
        if(isRunning){
            return;
        }
        mHandler.sendMessage(mHandler.obtainMessage());
        isRunning = true;
    }
    
    //用於uploadLogFile方法調用的線程
    private final class MyHandler extends Handler{

        public MyHandler(Looper looper) {
            super(looper);
            // TODO Auto-generated constructor stub
        }

        @Override
        public void handleMessage(Message msg) {
            File logFile = LogFileStorage.getInstance(mContext).getUploadLogFile();
            if(logFile == null){
                isRunning = false;
                return;
            }
            try {
                String result = HttpManager.uploadFile(url, params, logFile);
                
                Log.i("UpLoad", "服務端返回數據爲"+result);
                if(result != null){
                    Boolean isSuccess = LogFileStorage.getInstance(mContext).deleteUploadLogFile();
                    Log.i("UpLoad", "刪除文件結果爲"+isSuccess);
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }finally{
                isRunning = false;
            }
        }
        
    }
    
}

客戶端崩潰日誌文件的刪除,保存等操作類:LogFileStorage.java
文件保存在Android/data/包名/Log/下

package com.qihoo.linker.logcollector.capture;

import java.io.File;
import java.io.FileOutputStream;

import com.qihoo.linker.logcollector.utils.LogCollectorUtility;
import com.qihoo.linker.logcollector.utils.LogHelper;

import android.content.Context;
import android.util.Log;

/**
 * 
 * 客戶端崩潰日誌文件的刪除,保存等操作
 * 
 * @author 吳曉暢
 *
 */
public class LogFileStorage {

    private static final String TAG = LogFileStorage.class.getName();

    public static final String LOG_SUFFIX = ".log";

    private static final String CHARSET = "UTF-8";

    private static LogFileStorage sInstance;

    private Context mContext;

    private LogFileStorage(Context ctx) {
        mContext = ctx.getApplicationContext();
    }

    public static synchronized LogFileStorage getInstance(Context ctx) {
        if (ctx == null) {
            LogHelper.e(TAG, "Context is null");
            return null;
        }
        if (sInstance == null) {
            sInstance = new LogFileStorage(ctx);
        }
        return sInstance;
    }
    
    public File getUploadLogFile(){
        File dir = mContext.getFilesDir();
        File logFile = new File(dir, LogCollectorUtility.getMid(mContext)
                + LOG_SUFFIX);
        if(logFile.exists()){
            return logFile;
        }else{
            return null;
        }
    }
    
    //刪除客戶端中崩潰日誌文件
    public boolean deleteUploadLogFile(){
        File dir = mContext.getFilesDir();
        File logFile = new File(dir, LogCollectorUtility.getMid(mContext)
                + LOG_SUFFIX);
        Log.i("Log",
                LogCollectorUtility.getMid(mContext)
                + LOG_SUFFIX);
        return logFile.delete();
    }

    
    //保存文件
    public boolean saveLogFile2Internal(String logString) {
        try {
            File dir = mContext.getFilesDir();
            if (!dir.exists()) {
                dir.mkdirs();
            }
            File logFile = new File(dir, LogCollectorUtility.getMid(mContext)
                    + LOG_SUFFIX);
            FileOutputStream fos = new FileOutputStream(logFile , true);
            fos.write(logString.getBytes(CHARSET));
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
            LogHelper.e(TAG, "saveLogFile2Internal failed!");
            return false;
        }
        return true;
    }

    public boolean saveLogFile2SDcard(String logString, boolean isAppend) {
        if (!LogCollectorUtility.isSDcardExsit()) {
            LogHelper.e(TAG, "sdcard not exist");
            return false;
        }
        try {
            File logDir = getExternalLogDir();
            if (!logDir.exists()) {
                logDir.mkdirs();
            }
            
            File logFile = new File(logDir, LogCollectorUtility.getMid(mContext)
                    + LOG_SUFFIX);
            /*if (!isAppend) {
                if (logFile.exists() && !logFile.isFile())
                    logFile.delete();
            }*/
            LogHelper.d(TAG, logFile.getPath());
            
            FileOutputStream fos = new FileOutputStream(logFile , isAppend);
            fos.write(logString.getBytes(CHARSET));
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, "saveLogFile2SDcard failed!");
            return false;
        }
        return true;
    }

    private File getExternalLogDir() {
        File logDir = LogCollectorUtility.getExternalDir(mContext, "Log");
        LogHelper.d(TAG, logDir.getPath());
        return logDir;
    }
}

UncaughtExceptionHandler實現類:CrashHandler.java
  當出現異常時,會進入public void uncaughtException(Thread thread, Throwable ex) 方法中。

/**
 * 
 * 如果需要捕獲系統的未捕獲異常(如系統拋出了未知錯誤,這種異常沒有捕獲,這將導致系統莫名奇妙的關閉,使得用戶體驗差),
 * 可以通過UncaughtExceptionHandler來處理這種異常。
 * 
 * @author 吳曉暢
 *
 */
public class CrashHandler implements UncaughtExceptionHandler {

    private static final String TAG = CrashHandler.class.getName();

    private static final String CHARSET = "UTF-8";

    private static CrashHandler sInstance;

    private Context mContext;

    private Thread.UncaughtExceptionHandler mDefaultCrashHandler;

    String appVerName;

    String appVerCode;

    String OsVer;

    String vendor;

    String model;

    String mid;

    //初始化該類
    private CrashHandler(Context c) {
        mContext = c.getApplicationContext();
        // mContext = c;
        appVerName = "appVerName:" + LogCollectorUtility.getVerName(mContext);
        appVerCode = "appVerCode:" + LogCollectorUtility.getVerCode(mContext);
        OsVer = "OsVer:" + Build.VERSION.RELEASE;
        vendor = "vendor:" + Build.MANUFACTURER;
        model = "model:" + Build.MODEL;
        mid = "mid:" + LogCollectorUtility.getMid(mContext);
    }

    //初始化該類
    public static CrashHandler getInstance(Context c) {
        if (c == null) {
            LogHelper.e(TAG, "Context is null");
            return null;
        }
        if (sInstance == null) {
            sInstance = new CrashHandler(c);
        }
        return sInstance;
    }

    public void init() {

        if (mContext == null) {
            return;
        }

        boolean b = LogCollectorUtility.hasPermission(mContext);
        if (!b) {
            return;
        }
        mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 發生異常時候進來這裏
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        //
        handleException(ex);
        //
        ex.printStackTrace();

        if (mDefaultCrashHandler != null) {
            mDefaultCrashHandler.uncaughtException(thread, ex);
        } else {
            Process.killProcess(Process.myPid());
            // System.exit(1);
        }
    }

    //將異常信息保存成文件
    private void handleException(Throwable ex) {
        String s = fomatCrashInfo(ex);
        // String bes = fomatCrashInfoEncode(ex);
        LogHelper.d(TAG, s);
        // LogHelper.d(TAG, bes);
        //LogFileStorage.getInstance(mContext).saveLogFile2Internal(bes);
        LogFileStorage.getInstance(mContext).saveLogFile2Internal(s);
        if(Constants.DEBUG){
            LogFileStorage.getInstance(mContext).saveLogFile2SDcard(s, true);
        }
    }

    private String fomatCrashInfo(Throwable ex) {

        /*
         * String lineSeparator = System.getProperty("line.separator");
         * if(TextUtils.isEmpty(lineSeparator)){ lineSeparator = "\n"; }
         */

        String lineSeparator = "\r\n";

        StringBuilder sb = new StringBuilder();
        String logTime = "logTime:" + LogCollectorUtility.getCurrentTime();

        String exception = "exception:" + ex.toString();

        Writer info = new StringWriter();
        PrintWriter printWriter = new PrintWriter(info);
        ex.printStackTrace(printWriter);
        
        String dump = info.toString();
        String crashMD5 = "crashMD5:"
                + LogCollectorUtility.getMD5Str(dump);
        
        String crashDump = "crashDump:" + "{" + dump + "}";
        printWriter.close();
        

        sb.append("&start---").append(lineSeparator);
        sb.append(logTime).append(lineSeparator);
        sb.append(appVerName).append(lineSeparator);
        sb.append(appVerCode).append(lineSeparator);
        sb.append(OsVer).append(lineSeparator);
        sb.append(vendor).append(lineSeparator);
        sb.append(model).append(lineSeparator);
        sb.append(mid).append(lineSeparator);
        sb.append(exception).append(lineSeparator);
        sb.append(crashMD5).append(lineSeparator);
        sb.append(crashDump).append(lineSeparator);
        sb.append("&end---").append(lineSeparator).append(lineSeparator)
                .append(lineSeparator);

        return sb.toString();

    }

    private String fomatCrashInfoEncode(Throwable ex) {

        /*
         * String lineSeparator = System.getProperty("line.separator");
         * if(TextUtils.isEmpty(lineSeparator)){ lineSeparator = "\n"; }
         */

        String lineSeparator = "\r\n";

        StringBuilder sb = new StringBuilder();
        String logTime = "logTime:" + LogCollectorUtility.getCurrentTime();

        String exception = "exception:" + ex.toString();

        Writer info = new StringWriter();
        PrintWriter printWriter = new PrintWriter(info);
        ex.printStackTrace(printWriter);

        String dump = info.toString();
        
        String crashMD5 = "crashMD5:"
                + LogCollectorUtility.getMD5Str(dump);
        
        try {
            dump = URLEncoder.encode(dump, CHARSET);
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        String crashDump = "crashDump:" + "{" + dump + "}";
        printWriter.close();
        

        sb.append("&start---").append(lineSeparator);
        sb.append(logTime).append(lineSeparator);
        sb.append(appVerName).append(lineSeparator);
        sb.append(appVerCode).append(lineSeparator);
        sb.append(OsVer).append(lineSeparator);
        sb.append(vendor).append(lineSeparator);
        sb.append(model).append(lineSeparator);
        sb.append(mid).append(lineSeparator);
        sb.append(exception).append(lineSeparator);
        sb.append(crashMD5).append(lineSeparator);
        sb.append(crashDump).append(lineSeparator);
        sb.append("&end---").append(lineSeparator).append(lineSeparator)
                .append(lineSeparator);

        String bes = Base64.encodeToString(sb.toString().getBytes(),
                Base64.NO_WRAP);

        return bes;

    }

}

項目調用封裝類:LogCollector.java

/**
 * 
 * 執行文件上傳相關的類
 * 
 * 
 * @author 吳曉暢
 *
 */
public class LogCollector {

private static final String TAG = LogCollector.class.getName();
    
    private static String Upload_Url;
    
    private static Context mContext;
    
    private static boolean isInit = false;
    
    private static HttpParameters mParams;

    
    //初始化文件上傳的url,數據等內容
    public static void init(Context c , String upload_url , HttpParameters params){
        
        if(c == null){
            return;
        }
        
        if(isInit){
            return;
        }
        
        Upload_Url = upload_url;
        mContext = c;
        mParams = params;
        
        //初始化自己定義的異常處理
        CrashHandler crashHandler = CrashHandler.getInstance(c);
        
        crashHandler.init();
        
        isInit = true;
        
    }
    
    
    /**
     * 執行文件上傳的網路請求   
     * 
     *  if(isWifiOnly && !isWifiMode){
            return;
        }表示只在wifi狀態下執行文件上傳
     * 
     * @param isWifiOnly
     */
    public static void upload(boolean isWifiOnly){
        if(mContext == null || Upload_Url == null){
            Log.d(TAG, "please check if init() or not");
            return;
        }
        if(!LogCollectorUtility.isNetworkConnected(mContext)){
            return;
        }
        
        boolean isWifiMode = LogCollectorUtility.isWifiConnected(mContext);
        
        if(isWifiOnly && !isWifiMode){
            return;
        }
        
        UploadLogManager.getInstance(mContext).uploadLogFile(Upload_Url, mParams);
    }
    
    /**
     * 用於設置是否爲測試狀態     
     * 
     * @param isDebug   true爲是,false爲否      如果是,能看到LOG日誌,同時能夠在將文件夾看到崩潰日誌
     */
    public static void setDebugMode(boolean isDebug){
        
        Constants.DEBUG = isDebug;
        
        LogHelper.enableDefaultLog = isDebug;
        
    }
}

3. 客戶端接入使用

爲通用項目設置is Library模式

 
 

實際android項目使用

添加Library

 
 

在Application子類中進行初始化


public class MyApplication extends Application {
    
    //後臺地址地址
    private static final String UPLOAD_URL = "http://192.168.3.153:8080/bengkuitest/servlet/Test";

    @Override
    public void onCreate() {
        super.onCreate();
        boolean isDebug = true;

        //設置是否爲測試模式,如果是,同時能夠在將文件夾看到崩潰日誌
        LogCollector.setDebugMode(isDebug);
        
        //params的數據可以爲空      初始化LogCollector的相關數據,用於文件上傳到服務器
        LogCollector.init(getApplicationContext(), UPLOAD_URL, null);
    }

    
}

編寫異常並上傳異常

public class MainActivity extends Activity implements OnClickListener {

    private Button btn_crash;

    private Button btn_upload;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btn_crash = (Button) findViewById(R.id.button1);
        btn_upload = (Button) findViewById(R.id.button2);
        btn_crash.setOnClickListener(this);
        btn_upload.setOnClickListener(this);

        
    }
    
    //產生異常
    private void causeCrash(){
        String s = null;
        s.split("1");
    }
    
    //上傳文件
    private void uploadLogFile(){
        
        //設置爲只在wifi下上傳文件
        boolean isWifiOnly = true;//only wifi mode can upload
        
        //執行文件上傳服務器 
        LogCollector.upload(isWifiOnly);//upload at the right time
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.button1:
            
            causeCrash();
            break;
        case R.id.button2:
            
            //上傳文件
            uploadLogFile();
            break;

        default:
            break;
        }
    }

}

運行結果如下圖所示


--No1Qr4Tu7Wx

content-disposition: form-data; name="logfile"; filename="c5c63fec3651fdebdd411582793fa40c.log"
Content-Type: application/octet-stream; charset=utf-8

&start---
logTime:2019-04-07 10:54:47
appVerName:1.0
appVerCode:1
OsVer:5.1.1
vendor:samsung
model:SM-G955F
mid:c5c63fec3651fdebdd411582793fa40c
exception:java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String[] java.lang.String.split(java.lang.String)' on a null object reference
crashMD5:74861b8fb97ef57b82a87a826ab6b08f
crashDump:{java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String[] java.lang.String.split(java.lang.String)' on a null object reference
    at com.jiabin.logcollectorexample.MainActivity.causeCrash(MainActivity.java:32)
    at com.jiabin.logcollectorexample.MainActivity.onClick(MainActivity.java:45)
    at android.view.View.performClick(View.java:4780)
    at android.view.View$PerformClick.run(View.java:19866)
    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:5293)
    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:903)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
}
&end---




--No1Qr4Tu7Wx--

三、項目源碼下載

鏈接:https://pan.baidu.com/s/1kEGfJ3PSoDnsyulCAoimjg
密碼:xy0l

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章