zoukankan      html  css  js  c++  java
  • Android KLog源代码分析

    Android KLog源代码分析

    一直使用这个库。但没有细致研究。今天就来研究一下。该库的地址:

    KLog,在这里先感谢下作者。棒棒哒!

    代码结构

    整个代码的结构非常easy。例如以下:

    library
        klog
            BaseLog.java
            FileLog.java
            JsonLog.java
            XmlLog.java
        KLog.java
        KLogUtil.java

    共六个文件:

    • BaseLog.java,支持主要的log打印
    • FileLog.java。支持以文件的形式保存log
    • JsonLog.java,支持打印json对象和json数组
    • XmlLog.java,支持打印Xml形式的log

    详细分析

    BaseLog

    两个方法:

    public class BaseLog {
    
        private static final int MAX_LENGTH = 4000;
    
        public static void printDefault(int type, String tag, String msg) {
    
            int index = 0;
            int length = msg.length();
            int countOfSub = length / MAX_LENGTH;
    
            if (countOfSub > 0) {// 超过指定长度,粉刺打印。这样就避免了系统默认的log长度限制了
                for (int i = 0; i < countOfSub; i++) {
                    String sub = msg.substring(index, index + MAX_LENGTH);
                    printSub(type, tag, sub);
                    index += MAX_LENGTH;
                }
                printSub(type, tag, msg.substring(index, length));// 打印余数部分
            } else {
                printSub(type, tag, msg);
            }
        }
    
        private static void printSub(int type, String tag, String sub) {
            switch (type) {
                case KLog.V:
                    Log.v(tag, sub);
                    break;
                case KLog.D:
                    Log.d(tag, sub);
                    break;
                case KLog.I:
                    Log.i(tag, sub);
                    break;
                case KLog.W:
                    Log.w(tag, sub);
                    break;
                case KLog.E:
                    Log.e(tag, sub);
                    break;
                case KLog.A:
                    Log.wtf(tag, sub);
                    break;
            }
        }
    
    }

    这里突破了android系统的log字数限制,事实上就是超过字数限制后。採用分次打印的方法来打印,其它代码不做分析,比較简单。

    FileLog

    三个方法

    public class FileLog {
    
        private static final String FILE_PREFIX = "KLog_";
        private static final String FILE_FORMAT = ".log";
    
        /**
         * @param tag             log tag
         * @param targetDirectory log file save dir
         * @param fileName        log file name
         * @param headString      log file 文件头
         * @param msg             log file log内容主体
         */
        public static void printFile(String tag, File targetDirectory, @Nullable String fileName, String headString, String msg) {
    
            fileName = (fileName == null) ? getFileName() : fileName;
            if (save(targetDirectory, fileName, msg)) {
                Log.d(tag, headString + " save log success ! location is >>>" + targetDirectory.getAbsolutePath() + "/" + fileName);
            } else {
                Log.e(tag, headString + "save log fails !");
            }
        }
    
        /**
         * @param dic      log file save dir
         * @param fileName og file name
         * @param msg      log file log内容主体
         * @return true if save success
         */
        private static boolean save(File dic, @NonNull String fileName, String msg) {
    
            File file = new File(dic, fileName);
    
            try {
                OutputStream outputStream = new FileOutputStream(file);
                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
                outputStreamWriter.write(msg);
                outputStreamWriter.flush();
                outputStream.close();
                return true;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return false;
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                return false;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
    
        }
    
        /**
         * 当未设置文件名称时,随机生成一个文件名称
         *
         * @return default file name
         */
        private static String getFileName() {
            Random random = new Random();
            return FILE_PREFIX + Long.toString(System.currentTimeMillis() + random.nextInt(10000)).substring(4) + FILE_FORMAT;
        }
    
    }
    JsonLog

    JsonLog就更简单了,仅仅有一个方法(本宝宝曾经还以为Json打印会非常麻烦呢)

    public class JsonLog {
    
        public static void printJson(String tag, String msg, String headString) {
    
            String message;
    
            try {
                if (msg.startsWith("{")) {// 处理json对象
                    JSONObject jsonObject = new JSONObject(msg);
                    message = jsonObject.toString(KLog.JSON_INDENT);
                } else if (msg.startsWith("[")) {// 处理json数组
                    JSONArray jsonArray = new JSONArray(msg);
                    message = jsonArray.toString(KLog.JSON_INDENT);
                } else {
                    message = msg;
                }
            } catch (JSONException e) {
                message = msg;
            }
    
            KLogUtil.printLine(tag, true);// 调用格式化方法
            message = headString + KLog.LINE_SEPARATOR + message;
            String[] lines = message.split(KLog.LINE_SEPARATOR);
            for (String line : lines) {
                Log.d(tag, "║ " + line);
            }
            KLogUtil.printLine(tag, false);// 调用格式化方法
        }
    }
    XmlLog

    两个方法:

    public class XmlLog {
        /**
         * 打印xml
         *
         * @param tag        log tag
         * @param xml        xml content
         * @param headString 文件头
         */
        public static void printXml(String tag, String xml, String headString) {
    
            if (xml != null) {
                xml = XmlLog.formatXML(xml);
                xml = headString + "
    " + xml;
            } else {
                xml = headString + KLog.NULL_TIPS;
            }
    
            KLogUtil.printLine(tag, true);
            String[] lines = xml.split(KLog.LINE_SEPARATOR);
            for (String line : lines) {
                if (!KLogUtil.isEmpty(line)) {
                    Log.d(tag, "║ " + line);
                }
            }
            KLogUtil.printLine(tag, false);
        }
    
        /**
         * @param inputXML xml content
         * @return 格式化后的xml String
         */
        private static String formatXML(String inputXML) {
            try {
                Source xmlInput = new StreamSource(new StringReader(inputXML));
                StreamResult xmlOutput = new StreamResult(new StringWriter());
                Transformer transformer = TransformerFactory.newInstance().newTransformer();
                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
                transformer.transform(xmlInput, xmlOutput);
                return xmlOutput.getWriter().toString().replaceFirst(">", ">
    ");
            } catch (Exception e) {
                e.printStackTrace();
                return inputXML;
            }
        }
    
    }

    细心的你会发现。不管是Json形式还是XML形式。调用的都是系统原生的方法来处理数据,因此又时候对熟悉熟悉java(或android)提供的方法还是非常方便的。哈哈。

    上面用到的KLogUtil,例如以下:

    public class KLogUtil {
    
        public static boolean isEmpty(String line) {
            return TextUtils.isEmpty(line) || line.equals("
    ") || line.equals("	") || TextUtils.isEmpty(line.trim());
        }
    
        public static void printLine(String tag, boolean isTop) {
            if (isTop) {
                Log.d(tag, "╔═══════════════════════════════════════════════════════════════════════════════════════");
            } else {
                Log.d(tag, "╚═══════════════════════════════════════════════════════════════════════════════════════");
            }
        }
    
    }

    核心文件KLog.java分析

    给方法主要提供了相似于系统log方法的不同重载,比較简单,我这里要讲的是那个获取log详细有关的类名、方法名、行号等。与之相关的就是wrapperContent这种方法了。

    private static String[] wrapperContent(int stackTraceIndex, String tagStr, Object... objects) {
    
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
    
            StackTraceElement targetElement = stackTrace[stackTraceIndex];
            String className = targetElement.getClassName();// 得到类名
            String[] classNameInfo = className.split("\.");// 第一种形式
            if (classNameInfo.length > 0) {
                className = classNameInfo[classNameInfo.length - 1] + SUFFIX;
            }
    
            if (className.contains("$")) {//另外一种形式
                className = className.split("\$")[0] + SUFFIX;
            }
    
            String methodName = targetElement.getMethodName();//得到方法名
            int lineNumber = targetElement.getLineNumber();//得到所在的行号
    
            if (lineNumber < 0) {
                lineNumber = 0;
            }
    
            String tag = (tagStr == null ? className : tagStr);
    
            if (mIsGlobalTagEmpty && TextUtils.isEmpty(tag)) {
                tag = TAG_DEFAULT;
            } else if (!mIsGlobalTagEmpty) {
                tag = mGlobalTag;
            }
            // 得到消息主体
            String msg = (objects == null) ? NULL_TIPS : getObjectsString(objects);
            String headString = "[ (" + className + ":" + lineNumber + ")#" + methodName + " ] ";
    
            return new String[]{tag, msg, headString};
        }
    
    /**
         * 得到消息主体
         *
         * @param objects 消息对象数组
         * @return 消息主体字符串
         */
        private static String getObjectsString(Object... objects) {
    
            if (objects.length > 1) {
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.append("
    ");
                for (int i = 0; i < objects.length; i++) {
                    Object object = objects[i];
                    if (object == null) {
                        stringBuilder.append(PARAM).append("[").append(i).append("]").append(" = ").append(NULL).append("
    ");
                    } else {
                        stringBuilder.append(PARAM).append("[").append(i).append("]").append(" = ").append(object.toString()).append("
    ");
                    }
                }
                return stringBuilder.toString();
            } else {
                Object object = objects[0];
                return object == null ?

    NULL : object.toString(); } }

    分析完成,是不是非常easy,假设你看了这个库的代码。你会认为我的分析都是多余的。

    遇到的问题

    在使用KLog打印json形式信息时。假设网络请求时异步的,会导致KLog.json打印的格式出现错乱。即一个结果还没有全然打印出来。里外一个就開始打印了,这个应该是并发导致的问题,之后我会在KLog的基础上对这个问题进行优化的。

  • 相关阅读:
    Python动态生成方法
    aid learning安装python
    Pic Go使用阿里云OSS搭建图床
    QSqlQuery、QSqlQueryModel、QSqlTableModel的区别
    python文件上传错误“Required request part 'xxx' is not present”
    【已解决】执行yum命令失败:error: rpmdb: BDB0113 Thread/process 16978/139878363277376 failed: BDB1507 Thread died in Berkeley DB library
    C# DataTable Select用法
    Error in event handler: SyntaxError: Unexpected token '<'
    Lodash 两个数组合并-排重
    forEach,map,filter,find,some,every区别
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/7380903.html
Copyright © 2011-2022 走看看