为了保证线上项目的稳定性,减少或者消灭项目的崩溃,需要我们提前对项目的异常进行处理。Java异常主要分为主线程异常、子线程异常、未捕获异常。其中也包含activity与fragment的生命周期异常。我们需要对这几个地方的事件进行try catch处理,基本可以捕获到所有Java异常,当然 还有其它三方自己对异常处理可能会有一点影响,友盟的异常捕捉CrashApi就有可能直接crash而不会被捕捉到,所以如果有集成友盟的这里需要自己单独处理一下。
Bundle args = new Bundle();
args.putBoolean("enableJavaLog", false);
args.putBoolean("mDebug", false);
args.putBoolean("enableNativeLog", true);
args.putBoolean("mEncryptLog", false);
args.putBoolean("mZipLog", true);
args.putBoolean("enableUnexpLog", false);
CrashApi.createInstanceEx(context, AppConstants.Push.UM_KEY, false, args);
复制代码
话不多说,上代码。我们创建一个异常捕获的文件CatchService
class CatchService {
private lateinit var application: Application
private lateinit var retrofit: Retrofit
private var mOkHttpClient: OkHttpClient? = null
private var apiService: ApiService? = null
//appToken
private var appToken: String? = null
//主线程异常id
private var mainThreadID: Int? = null
//生命周期异常id`
private var lifeID: Int? = null
//子线程异常id
private var childThreadID: Int? = null
//其他异常id
private var otherID: Int? = null
private var handler: Handler? = null
private var isLifeChange = false
companion object {
@Volatile
private var instance: CatchService? = null
fun getInstance() =
instance ?: synchronized(this) {
instance ?: CatchService().also { instance = it }
}
fun getExceptionStr(e: Throwable): String {
try {
val writer: Writer = StringWriter()
val printWriter = PrintWriter(writer)
e.printStackTrace(printWriter)
var cause = e.cause
while (cause != null) {
//异常链
cause.printStackTrace();
cause = cause.cause;
}
printWriter.close()
return e.toString() + "\n" + writer.toString()
} catch (t: Throwable) {
return ""
}
}
}
/**
*此处id为自主定义 为了在后端做区分 isOpenCrashProtected参数true代表拦截上传 false代表不拦截异常,建议debug环境下设置为false,生产环境为true
*
*/
fun init(
application: Application,
appToken: String?,
mainThreadID: Int?,
lifeID: Int?,
childThreadID: Int?,
otherID: Int?,
isOpenCrashProtected: Boolean
) {
try {
this.application = application
this.appToken = appToken
this.mainThreadID = mainThreadID
this.lifeID = lifeID
this.childThreadID = childThreadID
this.otherID = otherID
val builder = OkHttpClient.Builder()
builder.connectTimeout(
10000,
TimeUnit.MILLISECONDS
).readTimeout(10000, TimeUnit.MILLISECONDS)
.writeTimeout(10000, TimeUnit.MILLISECONDS)
.retryOnConnectionFailure(false)
mOkHttpClient = builder.build()
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(mOkHttpClient)
.build()
apiService = retrofit.create(ApiService::class.java)
if (isOpenCrashProtected) {
openCrashProtected()
}
handler = Handler()
} catch (t: Throwable) {
}
}
/**
* 上传日志
*/
fun uploadLog(logId: Int, content: String, title: String) {
try {
//此处为自己定义的上传参数,可根据自己的需求与后端协调
var logRequest = LogRequest(
appToken, content, title, 1, IntArray(1).apply { set(0, logId) })
GlobalScope.launch {
withContext(Dispatchers.IO) {
var response = apiService?.uploadLog(logRequest)
Log.d("response", response!!.string())
}
}
} catch (t: Throwable) {
Log.d("response", t.message.toString() + "\n" + t.printStackTrace().toString())
}
}
/**
* 上传主线程日志
*/
fun uploadMainThreadLog(title: String, content: String) {
try {
mainThreadID?.let { uploadLog(it, content, title) }
} catch (t: Throwable) {
}
}
/**
* 上传子线程日志
*/
fun uploadChildThreadLog(title: String, content: String) {
try {
childThreadID?.let { uploadLog(it, content, title) }
} catch (t: Throwable) {
}
}
/**
* 上传生命周期日志
*/
fun uploadLifeLog(title: String, content: String) {
try {
lifeID?.let { uploadLog(it, content, title) }
} catch (t: Throwable) {
}
}
/**
* 上传其他日志
*/
fun uploadOtherLog(title: String, content: String) {
try {
otherID?.let { uploadLog(it, content, title) }
} catch (t: Throwable) {
}
}
/**
* 开启日志拦截
*/
private fun openCrashProtected() {
Thread.setDefaultUncaughtExceptionHandler(object : Thread.UncaughtExceptionHandler {
override fun uncaughtException(t: Thread, e: Throwable) {
CatchService.getInstance().uploadChildThreadLog(
"子线程异常",
getExceptionStr(e)
)
}
})
Handler(Looper.getMainLooper()).post {
while (true) {
try {
Looper.loop()
} catch (e: Throwable) {
CatchService.getInstance().uploadMainThreadLog(
"主线程异常",
getExceptionStr(e)
)
isLifeChange = false
handler?.postDelayed(Runnable {
ServiceManager.getInstance().currentActivity?.let {
if (!it.hasWindowFocus()) {
it.finish()
}
}
}, 3000)
}
}
}
}
}
复制代码
上面的catch文件,只需要在application里init就可以捕获项目中的主线程与子线程异常了,至于生命周期异常,在activity的onCreate、onStart、onResume、onStop、onRestart、onDestroy中try catch,并将catch中的异常上传到服务器。
/**
* 捕捉生命周期异常的
*/
abstract class LifeCatchActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
try {
create(savedInstanceState)
} catch (t: Throwable) {
CatchService.getInstance()
.uploadLifeLog("生命周期异常", CatchService.getExceptionStr(t))
}
}
override fun onResume() {
super.onResume()
try {
resume()
} catch (t: Throwable) {
CatchService.getInstance()
.uploadLifeLog("生命周期异常", CatchService.getExceptionStr(t))
}
}
override fun onDestroy() {
super.onDestroy()
try {
destroy()
} catch (t: Throwable) {
CatchService.getInstance()
.uploadLifeLog("生命周期异常", CatchService.getExceptionStr(t))
}
}
override fun onStart() {
super.onStart()
try {
start()
} catch (t: Throwable) {
CatchService.getInstance()
.uploadLifeLog("生命周期异常", CatchService.getExceptionStr(t))
}
}
override fun onStop() {
super.onStop()
try {
stop()
} catch (t: Throwable) {
CatchService.getInstance()
.uploadLifeLog("生命周期异常", CatchService.getExceptionStr(t))
}
}
override fun onRestart() {
super.onRestart()
try {
restart()
} catch (t: Throwable) {
CatchService.getInstance()
.uploadLifeLog("生命周期异常", CatchService.getExceptionStr(t))
}
}
abstract fun create(savedInstanceState: Bundle??)
open fun resume() {
}
open fun destroy() {
}
open fun start() {
}
open fun stop() {
}
open fun restart() {
}
}
复制代码
fragment是同样的处理方法,这里就不多做赘述了,将BaseActivity继承LifeCatchActivity,就可以实现对生命周期异常的捕获。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END