Android 踏雷日記(2)更優雅地解決 String Out Of Memory by weitsai | CodeData
top

Android 踏雷日記(2)更優雅地解決 String Out Of Memory

分享:

Android 踏雷日記(1)String Out Of Memory << 前情

還記得〈Android 踏雷日記(1)String Out Of Memory〉這篇文章嗎?雖然當時有解決 String Out Of Memory 的問題, 但是其實這樣的解決方法並不好, 因為我們是更改系統本身對每個 App 的限制, 因此 Android 高雄開發者社群 有人提出了更好的解法.

Android 高雄開發者社群

前提摘要

使用的是下面這樣的方式將 Asset 中的 dict.json 資料讀出, 在 new String 發生 String:

AssetManager assetManager = getAssets();
InputStream ims = assetManager.open("dict.json");
int size = ims.available();
byte[] buffer = new byte[size];
ims.read(buffer);
ims.close();
// 41175344
System.out.println(buffer.length);
// java.lang.OutOfMemoryError
String s = new String(buffer);

dict.json 架構

dict.json 格式大約如下, 檔案大小 41M.

{
    "八":{
        "ㄅㄚ":[
            "介於七與九之間的自然數。如:「六、七、八、九……」。大寫作「捌」,阿拉伯數字作「8」。",
            "姓。如漢代有西域人八滑。",
            "二一四部首之一。","表示數量是八的。如:「八字」、「八方」。",
            "形容多數或多方面。如:「四通八達」。(「八」字口語連用在去聲字前讀成陽平,如:「八號」、「八拜」。)"
        ]
    },
    "八伯":{
        "ㄅㄚ ㄅㄛˊ":[
            "古代京畿外八> 州的最高長官,分別掌管四方諸侯,相傳堯、舜時皆有設置。堯時八伯有驩兜、共工、放齊、鯀,其餘則不可考。舜時八伯相傳伯夷為陽伯,羲仲、羲叔之後為二羲伯,棄為夏伯,咎繇為秋伯>    ,和仲、和叔之後為和伯,垂為冬伯,一人不詳。見宋˙王應麟˙小學紺珠˙卷五˙名臣類上。",
            "兗州八伯的簡稱。見「兗州八伯」條。"
        ]
    }
}

讓我們來不用透過設定 largeheap 就解決 OOM 吧!

  1. 環境介紹
  2. 我們先建立 Dict.java, 用來儲存每個單字的注音和解釋.
    public class Dict {
        private String phonetic;
        private String meaning;
    
        public Dict() {
        }
    
        public Dict(String phonetic, String meaning) {
            this.phonetic = phonetic;
            this.meaning = meaning;
        }
    
        public void setPhonetic(String phonetic) {
            this.phonetic = phonetic;
        }
    
        public void setMeaning(String meaning) {
            this.meaning = meaning;
        }
    
        public String getPhonetic() {
            return phonetic;
        }
    
        public String getMeaning() {
            return meaning;
        }
    }
    
  3. 使用原生 JsonReader 的解法3.1 方法介紹

    注意事項:

    1. 如果下一層是 Array 結果使用 beginObject, 會產生下列訊息:
      java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY
    2. 想要 beginObject 之前, 一定要先把前面的 kny 讀出來, 否則也會發生錯誤!!
      java.lang.IllegalStateException: Expected BEGIN_OBJECT but was NAME

    3.2 現在讓我們從內往外讀出來吧

    • 建立 readStringsArray(JsonReader reader) 方法, 來得到單字解釋的 Array
    private List readStringsArray(JsonReader reader) throws IOException {
        List strings = new ArrayList();
        reader.beginArray();
        // 取得解釋
        while (reader.hasNext()) {  
            strings.add(reader.nextString());
        }
        reader.endArray();
        return strings;
    }
    
    • 建立 getDict(JsonReader reader) 方法, 得到 Dict Object, 裡面存放單字的解釋及注音

    取得注音並透過 readStringsArray() 來取得解釋,

    private Dict getDict(JsonReader reader) throws IOException {
        HashMap<String,String> map = new HashMap<String, String>();
        // 解開第二層 object
        reader.beginObject();
    
        Dict mDict = null;
        while (reader.hasNext()) {
            // 取得注音
            String phonetic = reader.nextName();
            String meaning = TextUtils.join(",", readStringsArray(reader).toArray()).replace(",", "\n");
            mDict = new Dict(phonetic, meaning);
        }
        reader.endObject();
    
        return mDict;
    }
    
    
    • 建立 readDictJsonStream(InputStream in), 取得所有單字的資料
    public HashMap<String, Dict> readDictJsonStream(InputStream in) throws IOException {
        JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
            // 解開第一層 Object
        reader.beginObject();
    
        HashMap<String, Dict> dictMap = new HashMap<String, Dict>();
    
        while (reader.hasNext()) {
                // 取得標題
            String name = reader.nextName();
            Dict mDict = getDict(reader);
            if (mDict == null) {
                continue;
            }
            dictMap.put(name, mDict);
        }
    
        reader.endObject();
        reader.close();
        return dictMap;
    }
    
    • 接著我們就可以使用剛剛建立的 readDictJsonStream(InputStream in), 使用方式如下:
    AssetManager assetManager = getAssets();
    InputStream ims = assetManager.open("dict.json");
    HashMap<String, Dict> mDictsMap = readDictJsonStream(ims);
    // ㄅㄚ
    Log.d("Dict",   mDictsMap.get("八").phonetic());
    
  4. 接著我們用 JsonReader + Gson 來試試看會不會更簡單呢?
public HashMap<String, Dict> readDictJsonStream(InputStream in) throws IOException {

    HashMap<String, String> dictMap = new HashMap<String, String>();
    JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));

    // 解開第一層 Object
    reader.beginObject();

    // 輪詢
    while (reader.hashNext()) {
        // 取得標題
        String name = reader.nextName();
        // 解開第二層 object
        reader.beginObject();
        // 取得注音
        String phonetic = reader.nextNmae();
        // 取得解釋
        String[] meanings = gson.fromJson(reader, String[].class);

        Dict mDict = new Dict(phonetic, TextUtil.join(",", meanings).replace(",", "\n");
        dictMap.put(name, mDict);

    }
}
分享:
按讚!加入 CodeData Facebook 粉絲群

相關文章

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

關於作者

我出生於一個無聲家庭,大二時便將我所學 Android App 發揮在自己家庭身上,聾啞人士長久以來沒有辦法像我們常人一樣可以打 110、119 報案,因此 iHelp 就此誕生。因為自己非常喜歡 Android,於是便在高雄成立 Android 高雄開發者社群 ,歡迎對 Android 有興趣的朋友來參與。

熱門論壇文章

熱門技術文章