关于异常崩溃是每一个App都要面对的,平时开发还好,在调试状态下遇到的问题,能够经过LogCat打印的异常日志信息进行分析处理,可是一旦App上线后,大量用户安装了你的应用,每一个用户的手机大小、传感器、SDK版本都不尽相同,可能你在测试机上跑的稳稳的应用,到了客户手机上就会出现一些莫名其妙的异常,若是只是一些内存泄露的问题可能还好,最起码不会瞬间崩溃,可是若是遇到一些能够致使手机崩溃Bug的话,你让出问题的用户来复现Bug是不可能的,因此,全局异常捕获就显得很重要了,而DhccCrashLib就是一个全局异常捕获的组件。javascript
Github地址java
使用方法仍是比较简单的,首先在项目的根目录下的build.gradle中加入Jcenter仓库:node
repositories {
jcenter()
}
复制代码
而后在你的项目的build.gradle中添加依赖:android
implementation 'com.dhcc.crashlib:CrashLib:1.0.3'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.github.zhaokaiqiang.klog:library:1.6.0'
implementation "com.sun.mail:android-mail:1.6.0"
复制代码
这四个依赖都须要加,由于担忧版本冲突,因此我在组件中使用的依赖方式是compileOnly,那么你在你的项目中若是有引用除了CrashLib外的这三个依赖的话,就能够换成你本身的版本号便可。webpack
使用方式 在你项目的自定义Application中:git
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
initCrash();
}
/** * 初始化崩溃采集服务 */
private void initCrash() {
EmailConfigBean emailConfigBean = new EmailConfigBean("你的发送邮箱", "你的接收邮箱", "你的发送邮箱密码");
Configuration configuration=Configuration.getInstance()
//你的邮件配置实例
.setEmailConfig(emailConfigBean)
//是否经过邮件发送异常
.setSendWithEmail(true)
//是否经过邮件发送异常并将本地存储的异常已附件的形式发送
.setSendEmailWithFile(true)
//异常服务器的API
.setCrashServerUrl("http://111.222.333.444:9999/api/crashs")
//是否给服务器发送异常信息
.setSendWithNet(true)
//异常的描述信息
.setCrashDescription("测试异常~~")
//捕获异常后退出App的等待时间 毫秒
.setExitWaitTime(5000)
;
LogCenter.getLogCenter("进程名", configuration)
//能够自定义异常 只要实现ICollector 并传入网络提交时所须要的key便可
.strategy(new TestCollectInfo(), "网络属性的Key")
.init(this);
}
}
复制代码
就这么简单,首先先把你的发送邮箱和接收邮箱的相关信息都配置到EmailConfigBean中去,而后再调用LogCenter初始化相关参数便可,不过这里有一个细节须要讲一下,注意看TestCollectInfo()这个方法:github
public class TestCollectInfo implements ICollector {
@Override
public String collectInfo(Context context) {
return "这是一条测试采集异常信息";
}
}
复制代码
因为每一个项目不一样,可能须要采集的异常信息外的其余一些手机信息都不尽相同,我这里在源码中只设计了Key为deviceInfo和Key为exceptionInfo的两种捕获信息,deviceInfo主要是为了捕获手机信息的而exceptionInfo就是捕获异常崩溃信息的了,若是你的项目中还须要捕获其余类型的信息,能够经过实现ICollector接口来定义本身想提交的采集信息便可,记得在初始化时调用.strategy(new TestCollectInfo(), "网络属性的Key")将采集信息传入便可。web
在网上能够看到不少相似于全局捕获异常发送服务器或者发送邮件给指定邮箱的功能,可是这些文章都没有实际的深刻场景,只是写出了逻辑代码,这样就会面临到一个很实际的问题:express
异常发生时,咱们要作的是将异常信息和一些其余捕获到的手机信息或上传服务器或经过邮件发送给指定邮箱,可是若是这个时间过长,致使App已经退出,进程退出后,此进程的线程也不复存在,那么若是你要作的逻辑操做还没作完,那么你此次异常的捕获就是失败的。json
基于这个缘由,我在异常发生时作的操做是这样的:
因为是子进程启动的Service进行的业务逻辑操做,就算主进程已经退出,也不会影响子进程的耗时操做,问题也就随之解决了。
你可能会纳闷了,什么是Express?这文件是干吗的?
看过前面的部分后,你可能知道了这个组件是能够将异常信息发送给服务器的,而看这篇文章的不少可能都是移动端的开发人员,不必定懂服务端,就算懂,也未必能很快的搭建一个能够接受异常信息的服务端来测试,那么为了你们测试方便,我就把个人Express文件分享出来,若是你还不知道什么是Express或者Node.js,建议你先看这篇:
以后将你Nodejs根目录下的app.js改成:
var fs = require('fs');
var path = require('path');
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
var CRASH_FILE = path.join(__dirname, 'api/crashs.json'); // user.json文件的路径
app.set('port', (process.env.PORT || 9999));
app.use('/', express.static(path.join(__dirname, 'public')));
//使用body-parser中间件
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(function(req, res, next) {
// Set permissive CORS header - this allows this server to be used only as
// an API server in conjunction with something like webpack-dev-server.
res.setHeader('Access-Control-Allow-Origin', '*');
// Disable caching so we'll always get the latest comments.
res.setHeader('Cache-Control', 'no-cache');
next();
});
//处理/api/crashs的POST请求
app.post('/api/crashs', function(req, res) {
fs.readFile(CRASH_FILE, function(err, data) {
if (err) {
console.error(err);
process.exit(1);
}
var crashs = JSON.parse(data);
//控制post提交的参数类型
var crash = {
deviceinfo: req.body.deviceInfo,
exceptioninfo: req.body.exceptionInfo,
testinfo:req.body.testInfo
};
//将user加入到users中去。
crashs.push(crash);
fs.writeFile(CRASH_FILE, JSON.stringify(crashs, null, 4), function(err) {
if (err) {
console.error(err);
process.exit(1);
}
//请求成功后返回的提示json
res.json("{code: 200, message: 'upload crash successful.'}");
});
});
});
app.listen(app.get('port'), function() {
console.log('Server started: http://localhost:' + app.get('port') + '/');
});
复制代码
而且在同目录的文件夹:api中放入crashs.json:
[
{
"deviceinfo": "手机信息异常===========================================<br>DISPLAY=Flyme 6.8.3.31R beta<br>REGION=CN<br>SERIAL=d4aa09c3<br>BOOTLOADER=unknown<br>SOFT_VERSION=Y.30<br>SUPPORTED_64_BIT_ABIS=[Ljava.lang.String;@e6de412<br>PERMISSIONS_REVIEW_REQUIRED=false<br>AUTO_TEST_ONEPLUS=false<br>ID=NMF26F<br>TAG=Build<br>HOST=xs-MacBookPro<br>TAGS=test-keys<br>TIME=1522481855000<br>TYPE=user<br>USER=xs<br>BOARD=QC_Reference_Phone<br>BRAND=OnePlus<br>MODEL=ONEPLUS A3010<br>RADIO=unknown<br>SUPPORTED_ABIS=[Ljava.lang.String;@833c7e3<br>MANUFACTURER=OnePlus<br>PRODUCT=OnePlus3<br>UNKNOWN=unknown<br>versionCode=1<br>versionName=1.0<br>IS_EMULATOR=false<br>FINGERPRINT=OnePlus/OnePlus3/OnePlus3T:7.1.1/NMF26F/builder.20180331153735_R:user/test-keys<br>HARDWARE=qcom<br>SUPPORTED_32_BIT_ABIS=[Ljava.lang.String;@b31279d<br>IS_BETA_ROM=true<br>CPU_ABI2=<br>CPU_ABI=arm64-v8a<br>IS_DEBUGGABLE=false<br>DEBUG_ONEPLUS=false<br>DEVICE=OnePlus3T<br>===========================================<br>",
"exceptioninfo": "Time:Fri May 10 14:23:32 GMT+08:00 2019 [Thread(id:3321, name:pool-2-thread-1, priority:5, groupName:main): LogCenter.java:184 run java.lang.RuntimeException: 测试CrashLib\n\tat com.dhcc.test.MainActivity$1.onClick(MainActivity.java:18)\n\tat android.view.View.performClick(View.java)\n\tat android.view.View$PerformClick.run(View.java:22549)\n\tat android.os.Handler.handleCallback(Handler.java:751)\n\tat android.os.Handler.dispatchMessage(Handler.java:95)\n\tat android.os.Looper.loop(Looper.java:154)\n\tat android.app.ActivityThread.main(ActivityThread.java)\n\tat java.lang.reflect.Method.invoke(Native Method)\n\tat com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)\n\tat com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)\n ] - 测试异常~~",
"testinfo": "这是一条测试采集异常信息"
}
]
复制代码
而后启动服务:
在Cmd下输入:
> node app.js
复制代码
以后在移动端,咱们就能够设置成一下代码来捕获咱们的异常信息了:
...
.setCrashServerUrl("http://你的ip:9999/api/crashs")
...
LogCenter.getLogCenter("com.dhcc.crashInfo", configuration)
//能够自定义异常 只要实现ICollector 并传入网络提交时所须要的key便可
.strategy(new TestCollectInfo(), "testInfo")
.init(this);
复制代码
这里注意.strategy(new TestCollectInfo(), "testInfo")的testInfo,其实就是app.js中的req.body.testInfo和crashs.json中的testInfo字段。
源码就不细说了,你们能够本身去看,有什么问题能够给我留言,谢谢你看完。