闲来无事,对以前项目中使用的对Log的封装使用抽取出来,写成一个Demo供博友参考。
Demo是以Gradle构建的项目。其中涉及到打Release包跟Debug包的时候对于日志输出的控制,由gradle脚本进行控制,下文我再细说。
先看下日志的输出效果:
效果比较简洁,主要展示日志的具体来源及日志消息,并提供点击快速定位代码的功能。
下面直接看代码:
package com.csf.simplelog.utils;
import android.text.TextUtils;
import android.util.Log;
/**
* ClassName: LogUtil
* Description:日志工具类
* Created by chensf on 2016-7-27 9:53.
*/
public class LogUtil {
private static final String TAG = LogUtil.class.getSimpleName();
/**
* 日志输出等级
*/
private static int LOG_LEVEL = Log.VERBOSE;
/**
* 是否显示日志
*/
private static boolean isShowLog = true;
private static final String DOUBLE_DIVIDER = "-----------------------------------------------------------------------------------------------------------";
public static void init(boolean isShowLog) {
LogUtil.isShowLog = isShowLog;
}
public static void init(boolean isShowLog, int logLevel) {
LogUtil.isShowLog = isShowLog;
LogUtil.LOG_LEVEL = logLevel;
}
public static int v(Object msg) {
return v(TAG, msg);
}
public static int v(String tag, Object msg) {
return v(tag, msg, null);
}
public static int v(String tag, Object msg, Throwable tr) {
return printLog(Log.VERBOSE, tag, msg, tr);
}
public static int d(Object msg) {
return d(TAG, msg);
}
public static int d(String tag, Object msg) {
return d(tag, msg, null);
}
public static int d(String tag, Object msg, Throwable tr) {
return printLog(Log.DEBUG, tag, msg, tr);
}
public static int i(Object msg) {
return i(TAG, msg);
}
public static int i(String tag, Object msg) {
return i(tag, msg, null);
}
public static int i(String tag, Object msg, Throwable tr) {
return printLog(Log.INFO, tag, msg, tr);
}
public static int w(Object msg) {
return w(TAG, msg);
}
public static int w(String tag, Object msg) {
return w(tag, msg, null);
}
public static int w(String tag, Object msg, Throwable tr) {
return printLog(Log.WARN, tag, msg, tr);
}
public static int e(Object msg) {
return e(TAG, msg);
}
public static int e(String tag, Object msg) {
return e(tag, msg, null);
}
public static int e(String tag, Object msg, Throwable tr) {
return printLog(Log.ERROR, tag, msg, tr);
}
private static int printLog(int type, String tag, Object msgObj, Throwable tr) {
if (!isShowLog) {
return 0;
}
String msg;
StringBuilder builder = new StringBuilder(DOUBLE_DIVIDER).append('\n').append(getFunctionName())
.append(DOUBLE_DIVIDER).append('\n').append(" ");
if (msgObj == null) {
msg = "";
} else {
msg = msgObj.toString();
}
if (!TextUtils.isEmpty(msg)) {
builder.append(msg);
}
if (tr != null) {
builder.append('\n').append(Log.getStackTraceString(tr));
}
builder.append('\n').append(DOUBLE_DIVIDER);
switch (type) {
case Log.VERBOSE:
if (LOG_LEVEL <= Log.VERBOSE) {
return Log.v(tag, builder.toString());
}
break;
case Log.DEBUG:
if (LOG_LEVEL <= Log.DEBUG) {
return Log.d(tag, builder.toString());
}
break;
case Log.INFO:
if (LOG_LEVEL <= Log.INFO) {
return Log.i(tag, builder.toString());
}
break;
case Log.WARN:
if (LOG_LEVEL <= Log.WARN) {
return Log.w(tag, builder.toString());
}
break;
case Log.ERROR:
if (LOG_LEVEL <= Log.ERROR) {
return Log.e(tag, builder.toString());
}
break;
}
return 0;
}
private static String getFunctionName() {
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
if (elements == null) {
return "";
}
for (StackTraceElement ste : elements) {
if (ste.isNativeMethod()) {
continue;
}
if (ste.getClassName().equals(Thread.class.getName())) {
continue;
}
if (ste.getClassName().equals(LogUtil.class.getName())) {
continue;
}
return " " + ste.getFileName().substring(0, ste.getFileName().indexOf(".")) + "." + ste.getMethodName()
+ " (" + ste.getFileName() + ":" + ste.getLineNumber() + ")\n";
}
return "";
}
}
167行的代码,其中提供快速定位功能的代码在getFunctionName这个方法中,通过循环获取Stack Trace Element中的element,分析获取到具体的调用类方法及对应行数。代码如下:
private static String getFunctionName() {
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
if (elements == null) {
return "";
}
for (StackTraceElement ste : elements) {
if (ste.isNativeMethod()) {
continue;
}
if (ste.getClassName().equals(Thread.class.getName())) {
continue;
}
if (ste.getClassName().equals(LogUtil.class.getName())) {
continue;
}
return " " + ste.getFileName().substring(0, ste.getFileName().indexOf(".")) + "." + ste.getMethodName()
+ " (" + ste.getFileName() + ":" + ste.getLineNumber() + ")\n";
}
return "";
下面我们将目光回到对Log的封装类LogUtil上,LogUtil只是对原生Log类进行简单的封装,通过增加属性isShowLog及LOG_LEVEL对我们的log进行控制,方便在发布Release包的时候快速关闭日志输出。
LogUtil提供两个初始函数,
public static void init(boolean isShowLog) {
LogUtil.isShowLog = isShowLog;
}
public static void init(boolean isShowLog, int logLevel) {
LogUtil.isShowLog = isShowLog;
LogUtil.LOG_LEVEL = logLevel;
}
这两个函数二选一,最好是在应用的入口进行初始化,一般我们会自定义一个Application的继承类。在其方法onCreate中就能进行应用的初始工作。
代码如下:
然后在AndroidManifest中引用这个MyApplication类
<application
android:name=".config.MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
在MyApplication的onCreate方法中,我们看到,我这里通过
BuildConfig.LOG_DEBUG这个值来控制日志是否输出。
我们来看这个语句LogUtil.init(true, Log.VERBOSE);其中参一是是否开启日志输出,参二顾名思义,就是日志的级别,这里我将级别控制设置为最低的VERBOSE,意思就是所有的日志信息,VERBOSE,DEBUG,INFO,WARN,ERROR这些级别的信息都会输出。如果传递参数Log.ERROR,那就只会输出错误日志,因为我们的Log.ERROR的级别比VERBOSE,DEBUG,INFO,WARN都要高。具体的大家去看到Demo的效果。
回到上面的BuildConfig.LOG_DEBUG,机智的小伙伴会发现BuildConfig中并没有LOG_DEBUG这个属性,那么我们来讲下开文提到
“涉及到打Release包跟Debug包的时候对于日志输出的控制,由gradle脚本进行控制”
通过Gradle脚本来进行控制日志的输出,这样我们就不用在打包的时候手动去修改代码。
下面贴出部分Gradle脚本:
buildTypes {
debug {
//显示日志
buildConfigField("boolean","LOG_DEBUG","true");
}
release {
//不显示日志
buildConfigField("boolean","LOG_DEBUG","false");
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
在主工程的Gradle脚本的buildTypes下,在debug跟release中增加buildConfigField(“boolean”,”LOG_DEBUG”,”true”);
这个语句的意思是在BuildConfig中增加字段LOG_DEBUG,debug下LOG_DEBUG的值为true,release下则为false.然后再通过该值去初始化LogUtil,达到控制日志输出。
大家如果还有什么疑问,可以留言跟我一起进行交流讨论。
Demo下载地址: http://download.csdn.net/download/q919233914/9587719
转载注明出处: http://blog.csdn.net/q919233914/article/details/52046667