日志模块封装:单例模式+策略模式+构建者模式+bugly
一.单例模式+策略模式+构建者模式
二.日志模块封装
默认情况下我们的日志只能打印输出,没有永久保存,这里我们的日志不仅能log输出还要能上传到网络,邮件或者本地存储,下面我们进行简单的封装,这里涉及到kotlin基础以及单例模式/策略模式/构建者模式
1.日志等级:LoggerLevel枚举类
/**
* @Author : ytx
* @Time : On 2023/5/21 15:23
* @Description : LoggerLevel 日志等级
*/
enum class LoggerLevel(var value: Int) {
Verbose(1),
Debug(2),
Info(3),
Warn(4),
Error(5)
}
2.日志输出策略:LoggerStrategy枚举类
/**
* @Author : ytx
* @Time : On 2023/5/21 15:36
* @Description : LoggerType 日志输出策略
*/
enum class LoggerStrategy {
LOGCAT,FILE,EMAIL,NET
}
3.ILogger接口
/**
* @Author : ytx
* @Time : On 2023/5/21 15:18
* @Description : ILogger:所有logger顶层接口
*/
interface ILogger {
fun d(tag:String,log:String)
fun v(tag:String,log:String)
fun i(tag:String,log:String)
fun w(tag:String,log:String)
fun e(tag:String,log:String)
/**
* 是否Debug
*/
fun setDebug(isDeug:Boolean)
/**
* 默认TAG
*/
fun setLogTAG(TAG:String)
/**
* 默认日志等级
*/
fun setLoggerLevel(level:LoggerLevel)
/**
* Log存储位置,可以是本地路径 可以是网络路径 可以是Email地址
*/
fun setSaveUrl(url:String)
}
4.LogCatLogger/FileLogger/NetWorkLogger/EmailLogger
创建logger包,下面分别对应4种不同方式,以LogCatLogger为案例
/**
* @Author : ytx
* @Time : On 2023/5/21 15:26
* @Description : LogcatLogger:打印输出log
*/
class LogcatLogger: ILogger {
private var isDebug = true
private var TAG = ""
private var level = LoggerLevel.Debug
private var loggerFormat = "(:=>$TAG %s -->%s"
private var url = ""
override fun d(tag: String, log: String) {
if(isDebug && level.ordinal >= LoggerLevel.Debug.ordinal){
Log.d(TAG,String.format(loggerFormat,tag,log))
}
}
override fun v(tag: String, log: String) {
if(isDebug && level.ordinal >= LoggerLevel.Verbose.ordinal){
Log.v(TAG,String.format(loggerFormat,tag,log))
}
}
override fun i(tag: String, log: String) {
if(isDebug && level.ordinal >= LoggerLevel.Info.ordinal){
Log.i(TAG,String.format(loggerFormat,tag,log))
}
}
override fun w(tag: String, log: String) {
if(isDebug && level.ordinal >= LoggerLevel.Warn.ordinal){
Log.w(TAG,String.format(loggerFormat,tag,log))
}
}
override fun e(tag: String, log: String) {
if(isDebug && level.ordinal >= LoggerLevel.Error.ordinal){
Log.e(TAG,String.format(loggerFormat,tag,log))
}
}
override fun setDebug(isDebug: Boolean) {
this.isDebug = isDebug
}
override fun setLogTAG(TAG: String) {
this.TAG = TAG
}
override fun setLoggerLevel(level: LoggerLevel) {
this.level = level
}
override fun setSaveUrl(url: String) {
this.url = url
}
}
5.使用构建者模式和策略模式创建对应的logger对象
/**
* @Author : ytx
* @Time : On 2023/5/21 15:37
* @Description : Logger创建对应的logger对象 采用策略模式和构建者
*/
class Logger private constructor (private val TAG:String, private val level:LoggerLevel, private val loggerType: LoggerStrategy, private val isDebug:Boolean, private val saveUrl:String) {
private var logger:ILogger
init {
logger = getLogger()
}
constructor(builder: Builder):this(
builder.TAG,
builder.level,
builder.loggerType,
builder.isDebug,
builder.saveUrl
)
/**
* 创建Logger
*/
fun getLogger():ILogger{
when(loggerType){
LoggerStrategy.EMAIL -> throw IllegalArgumentException("该方案未实现")
LoggerStrategy.FILE -> throw IllegalArgumentException("该方案未实现")
LoggerStrategy.NET -> throw IllegalArgumentException("该方案未实现")
LoggerStrategy.LOGCAT -> logger = LogcatLogger()
}
logger.setLoggerLevel(level)
logger.setLogTAG(TAG)
logger.setDebug(isDebug)
logger.setSaveUrl(saveUrl)
return logger
}
/**
* 输出Debug log
*/
fun d(Tag:String,log:String){
logger.d(Tag,log)
}
/**
* 输出Warn log
*/
fun w(Tag:String,log:String){
logger.w(Tag,log)
}
/**
* 输出Verbose log
*/
fun v(Tag:String,log:String){
logger.v(Tag,log)
}
/**
* 输出Error log
*/
fun e(Tag:String,log:String){
logger.e(Tag,log)
}
/**
* 输出Info log
*/
fun i(Tag:String,log:String){
logger.i(Tag,log)
}
class Builder{
var TAG:String = "默认TAG"
var level:LoggerLevel = LoggerLevel.Debug
var loggerType: LoggerStrategy = LoggerStrategy.LOGCAT
var isDebug:Boolean = true
var saveUrl:String = "默认路径"
fun setTAG(TAG:String):Builder{
this.TAG = TAG
return this
}
fun setLevel(level:LoggerLevel):Builder{
this.level = level
return this
}
fun setLoggerType(loggerType: LoggerStrategy):Builder{//设置策略
this.loggerType = loggerType
return this
}
fun isDebug(isDebug: Boolean):Builder{
this.isDebug = isDebug
return this
}
fun setSaveUrl(saveUrl: String):Builder{
this.saveUrl = saveUrl
return this
}
fun build():Logger{
return Logger(this)
}
}
}
6.最后业务模块中使用:
class LoggerUtils {
companion object{
private val logger = Logger.Builder()
.setLevel(LoggerLevel.Error)
.setLoggerType(LoggerStrategy.LOGCAT)
.isDebug(true)
.setTAG("main业务模块")
.build()
fun d(tag:String,log:String){
logger.d(tag,log)
}
}
}
LoggerUtils.d("123","234")
三.bugly的使用
1.什么是Bugly
腾讯Bugly,为移动开发者提供专业的异常上报和运营统计,帮助开发者快速发现并
解决异常,同时掌握产品运营动态,及时跟进用户反馈。
2.集成Bugly
https://bugly.qq.com/docs/user-guide/instruction-manual-android/?v=1.0.0
3.如何创建Bugly产品
https://bugly.qq.com/v2/workbench/apps
4.面试问题难点:如果项目混淆后如何准确定位到错误的信息
0.现象
class Utils {
companion object{
fun chu2( num1:Int, num2:Int):Int{
return num1/num2
}
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//制造异常
var result = Utils.chu2(4,0)
}
}
设置代码混淆
打包成apk,并将apk安装到手机上,报错,不显示Utils的第几行报错,因为Utils默认被混淆,而activity安卓的四大组件默认不被混淆,那么怎么解决这个问题呢
解决问题方案:
(1)集成文档最后
(2)点击符号表配置最后
(3)下载
a。将jar扔到根目录下,注意根目录不是libs
b。大家可以看文档的有关参数说明,我这边使用的命令是这样,以后可以方便复制使用
java -jar buglyqq-upload-symbol.jar -appid xxxx -appkey xxxxx -bundleid com.zhongjh -version 5.5.0 -platform Android -inputSymbol D:\XXX\build\outputs\mapping\debug\mapping.txt -inputMapping D:\XXX\build\outputs\mapping\debug\mapping.txt
然后回车,执行,看到200则表示上传成功
c。崩溃信息中可以看到报错的具体位置,即使代码混淆后