zoukankan      html  css  js  c++  java
  • 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 = "
    ";
        
        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 = "
    "; }
             */
    
            String lineSeparator = "
    ";
    
            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 = "
    "; }
             */
    
            String lineSeparator = "
    ";
    
            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

     
  • 相关阅读:
    Android蓝牙通信 .[转]
    通过VS2010性能分析来查找代码中那些地方最损耗资源 [转]
    【百度地图API】如何区分地址解析和智能搜索?
    Windows 程序员必备的知识和工具
    NUnit详细使用方法
    Android 蓝牙开发浅析 [转]
    软件工程的国家标准下载链接
    android布局属性详解
    Android之Service相关
    Android 实现布局动态加载
  • 原文地址:https://www.cnblogs.com/WUXIAOCHANG/p/10665002.html
Copyright © 2011-2022 走看看