欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android之崩溃日志管理

程序员文章站 2023-11-16 12:30:04
文章大纲 一、Android崩溃日志管理简介二、崩溃日志管理实战三、项目源码下载 一、Android崩溃日志管理简介 1. 什么是android崩溃日志管理 开发中有些地方未注意可能造成异常抛出未能caught到,然后弹出系统对话框强制退出。这种交互不好,而且开发者也不能及时获取到底哪里出问题。因此 ......

文章大纲

一、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之崩溃日志管理
 
Android之崩溃日志管理

实际android项目使用

添加library

 
Android之崩溃日志管理
 
Android之崩溃日志管理

在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