Android中Logcat長(zhǎng)日志打印不全問(wèn)題正解

前言: Android中Logcat長(zhǎng)日志打印不全的問(wèn)題很多人都知道,網(wǎng)上也有很多解決方案热凹,但問(wèn)題是這些答案確定是正解嗎泵喘?

說(shuō)一下我的經(jīng)過(guò):

  1. 最開始發(fā)現(xiàn)Logcat長(zhǎng)日志打印不全泪电,然后我就去網(wǎng)上找了答案,很多文章說(shuō)的是打印日志最長(zhǎng)是4k長(zhǎng)度的字符串纪铺,我用長(zhǎng)英文字母的字符串測(cè)試后相速,大概代碼是這樣子的(錯(cuò)誤代碼):
/**
     * 超4000字符時(shí)分段打印
     *
     * @param priority
     * @param tag
     * @param content
     */
    private void print(int priority, String tag, String content) {
        if (content == null) {
            content = "";
        }

        // 不超過(guò)4000時(shí)正常打印
        if (content.length() <= 4000) {
            Log.println(priority, tag, content);
            return;
        }

        int count = 1;
        // 超過(guò)4000時(shí)分段打印
        for (int i = 0; i < content.length(); i += 4000) {
            String desc = String.format("分段打印(%s):", count);
            String splitContent = i + 4000 < content.length() ? content.substring(i, i + 4000) : content.substring(i, content.length());
            Log.println(priority, tag, desc + splitContent);
            count++;
        }
    }
  1. 本以為很完美,但使用過(guò)程中還是老出現(xiàn)奇怪的問(wèn)題鲜锚,有的日志打印正常突诬,有的日志打印卻會(huì)丟失,這就讓人很難受了芜繁,我一度以為是我打印日志過(guò)多旺隙,控制臺(tái)選擇性丟棄了部分日志,因?yàn)橐郧叭罩敬蛴∵^(guò)多把控制臺(tái)都搞崩潰過(guò)骏令。

  2. 當(dāng)有一次打印超長(zhǎng)的一個(gè)json字符串蔬捷,然后我分段復(fù)制出來(lái)拼接诈乒,但是一頓操作后卻不能拼接成完整json衰伯,原因就是特么又有日志丟了瞳步,我那個(gè)氣哦~(垃圾AS祠乃,毀我青春)唉,那再接再厲看看啥情況唄款青。

  3. 我就用控制變量法渔彰,反復(fù)測(cè)試打印速度沥匈、文本長(zhǎng)度聪黎、文本內(nèi)容等罕容,在什么情況下會(huì)丟日志,最后發(fā)現(xiàn)就是上面代碼的問(wèn)題稿饰,AS控制臺(tái)打印的最大長(zhǎng)度不是按字符串長(zhǎng)度算的,而字節(jié)長(zhǎng)度B恫础:砹!

  4. 為什么會(huì)偶爾丟日志惭笑?字符串默認(rèn)是UTF-8編碼侣姆,它是一種變長(zhǎng)編碼,一個(gè)字符用1~4個(gè)字節(jié)表示沉噩,數(shù)字捺宗、字母及其他符號(hào)字符用1個(gè)字節(jié)表示,中文字符用3個(gè)字節(jié)表示川蒙,所以偶爾有超長(zhǎng)字符串蚜厉,且包含多個(gè)中文等字符時(shí),其長(zhǎng)度就可能超過(guò)最大限制畜眨,導(dǎo)致打印丟失昼牛。

解決方案

經(jīng)多次測(cè)試术瓮,控制臺(tái)最多打印了4062個(gè)字節(jié)(不同版本等情況會(huì)稍有出入),總的來(lái)說(shuō)打印4000個(gè)字節(jié)在不同情況都是穩(wěn)妥的贰健。做法就是分段打印胞四,這里需要將字符串轉(zhuǎn)字節(jié)數(shù)組,將字節(jié)數(shù)組分段伶椿,再重新轉(zhuǎn)字符串依次打印輸出辜伟。

正解代碼實(shí)現(xiàn)

/**
     * 打印日志到控制臺(tái)(解決Android控制臺(tái)丟失長(zhǎng)日志記錄)
     *
     * @param priority 
     * @param tag
     * @param content
     */
    public void print(int priority, String tag, String content) {
        // 1. 測(cè)試控制臺(tái)最多打印4062個(gè)字節(jié),不同情況稍有出入(注意:這里是字節(jié)脊另,不是字符S沃纭!)
        // 2. 字符串默認(rèn)字符集編碼是utf-8尝蠕,它是變長(zhǎng)編碼一個(gè)字符用1~4個(gè)字節(jié)表示
        // 3. 這里字符長(zhǎng)度小于1000烘豌,即字節(jié)長(zhǎng)度小于4000,則直接打印看彼,避免執(zhí)行后續(xù)流程廊佩,提高性能哈
        if (content.length() < 1000) {
            Log.println(priority, tag, content);
            return;
        }

        // 一次打印的最大字節(jié)數(shù)
        int maxByteNum = 4000;

        // 字符串轉(zhuǎn)字節(jié)數(shù)組
        byte[] bytes = content.getBytes();

        // 超出范圍直接打印
        if (maxByteNum >= bytes.length) {
            Log.println(priority, tag, content);
            return;
        }

        // 分段打印計(jì)數(shù)
        int count = 1;

        // 在數(shù)組范圍內(nèi),則循環(huán)分段
        while (maxByteNum < bytes.length) {
            // 按字節(jié)長(zhǎng)度截取字符串
            String subStr = cutStr(bytes, maxByteNum);

            // 打印日志
            String desc = String.format("分段打印(%s):%s", count++, subStr);
            Log.println(priority, tag, desc);

            // 截取出尚未打印字節(jié)數(shù)組
            bytes = Arrays.copyOfRange(bytes, subStr.getBytes().length, bytes.length);

            // 可根據(jù)需求添加一個(gè)次數(shù)限制靖榕,避免有超長(zhǎng)日志一直打印
            /*if (count == 10) {
                break;
            }*/
        }

        // 打印剩余部分
        Log.println(priority, tag, String.format("分段打印(%s):%s", count, new String(bytes)));
    }


    /**
     * 按字節(jié)長(zhǎng)度截取字節(jié)數(shù)組為字符串
     *
     * @param bytes
     * @param subLength
     * @return
     */
    public String cutStr(byte[] bytes, int subLength) {
        // 邊界判斷
        if (bytes == null || subLength < 1) {
            return null;
        }

        // 超出范圍直接返回
        if (subLength >= bytes.length) {
            return new String(bytes);
        }

        // 復(fù)制出定長(zhǎng)字節(jié)數(shù)組标锄,轉(zhuǎn)為字符串
        String subStr = new String(Arrays.copyOf(bytes, subLength));

        // 避免末尾字符是被拆分的,這里減1使字符串保持完整
        return subStr.substring(0, subStr.length() - 1);
    }

以下為測(cè)試代碼

public void test() {
        JSONArray array = new JSONArray();
        StringBuilder builder1 = new StringBuilder();
        StringBuilder builder2 = new StringBuilder();

        for (int i = 0; i < 1000; i++) {
            array.put("好h哦~");
            builder1.append("哈哈哦哦");
            builder2.append("hello1024");
        }

        String json = array.toString();
        String str1 = builder1.toString();
        String str2 = builder2.toString();

        // json打印了2581個(gè)字符茁计,字節(jié)長(zhǎng)度為4057料皇,其余丟失
        Log.i("defaultTag", json);

        // str1打印了1352個(gè)字符,字節(jié)長(zhǎng)度為4056星压,其余丟失
        Log.i("defaultTag", str1);

        // str2打印了4055個(gè)字符践剂,字節(jié)長(zhǎng)度為4055,其余丟失
        Log.i("defaultTag", str2);

        // 以下為分段打印娜膘,看是否有丟失日志
        print(Log.INFO, "defaultTag", json);
        print(Log.INFO, "defaultTag", str1);
        print(Log.INFO, "defaultTag", str2);
    }

注意事項(xiàng)

  1. 日志打印為了保證性能逊脯,要減少調(diào)用字符串的getBytes()方法,這個(gè)方法會(huì)遍歷字符串竣贪,將每個(gè)字符編碼為不同的字節(jié)军洼。
  2. 可根據(jù)需求添加一個(gè)次數(shù)限制,避免有非常長(zhǎng)的日志一直打印演怎,如果本身需要全部打印就忽略這個(gè)問(wèn)題匕争。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市爷耀,隨后出現(xiàn)的幾起案子甘桑,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扇住,死亡現(xiàn)場(chǎng)離奇詭異春缕,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)艘蹋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門锄贼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人女阀,你說(shuō)我怎么就攤上這事宅荤。” “怎么了浸策?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵冯键,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我庸汗,道長(zhǎng)惫确,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任蚯舱,我火速辦了婚禮改化,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘枉昏。我一直安慰自己陈肛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布兄裂。 她就那樣靜靜地躺著句旱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晰奖。 梳的紋絲不亂的頭發(fā)上谈撒,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音畅涂,去河邊找鬼港华。 笑死,一個(gè)胖子當(dāng)著我的面吹牛午衰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播冒萄,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼臊岸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了尊流?” 一聲冷哼從身側(cè)響起帅戒,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后逻住,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钟哥,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年瞎访,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了腻贰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扒秸,死狀恐怖播演,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伴奥,我是刑警寧澤写烤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站拾徙,受9級(jí)特大地震影響洲炊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜尼啡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一暂衡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧玄叠,春花似錦古徒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至寺惫,卻和暖如春疹吃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背西雀。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工萨驶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人艇肴。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓腔呜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親再悼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子核畴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354