1. 程式人生 > >Android問題之res/raw和assets的區別及檔案大小限制

Android問題之res/raw和assets的區別及檔案大小限制

*res/raw和assets的相同點:

1.兩者目錄下的檔案在打包後會原封不動的儲存在apk包中,不會被編譯成二進位制。


*res/raw和assets的不同點:
1.res/raw中的檔案會被對映到R.java檔案中,訪問的時候直接使用資源ID即R.id.filename;assets資料夾下的檔案不會被對映到R.java中,訪問的時候需要AssetManager類。
2.res/raw不可以有目錄結構,而assets則可以有目錄結構,也就是assets目錄下可以再建立資料夾

*讀取檔案資源:

1.讀取res/raw下的檔案資源,通過以下方式獲取輸入流來進行寫操作

  1. InputStream is = getResources().openRawResource(R.id.filename);  

2.讀取assets下的檔案資源,通過以下方式獲取輸入流來進行寫操作

  1. AssetManager am = null;  
  2. am = getAssets();  
  3. InputStream is = am.open("filename");  

補充一下:在未知目錄下有哪些檔案,該去和獲取這些檔案的名稱並把檔案拷貝到目標目錄中呢?(用於內建檔案但不知道檔名稱,需要篩選出想要的檔案然後拷貝到目標目錄中,推薦內建在assets資料夾中)
1.res/raw目錄:
通過反射的方式得到R.java裡面raw內部類裡面所有的資源ID的名稱,然後通過名稱獲取資源ID的值來讀取我們想要的檔案。(這個方法我沒試過,有用過的同學麻煩發一段程式碼看看)。
2.assets目錄:
getAssets().list("");來獲取assets目錄下所有資料夾和檔案的名稱,再通過這些名稱再讀取我們想要的檔案。

Android系統對資原始檔(res/raw和assets資料夾下)的大小有限制,預設最大僅支援1M的檔案。否則apk程式將報錯。如果AssetManager或Resources classes方法來獲取InputStream,將丟擲java.io.IOException的異常如下DEBUG/asset(1123): Data exceeds UNCOMPRESS_DATA_MAX。

1、大檔案解決辦法
1.將你的資原始檔字尾改成後面aapt忽略壓縮的檔案字尾。
2.在命令列上使用-0引數來指定不需要壓縮的檔案字尾,具體配置檢視aapt幫助文件。
3.把資原始檔分割成多個小於UNCOMPRESS_DATA_MAX(1M)的檔案,然後在程式中進行組合。

2、aapt壓縮忽略檔案說明
由於aapt工具在打包apk檔案時,會對資原始檔進行壓縮以減少apk檔案大小。檢視aapt工具中的Package.cpp原始碼,發現有些檔案不會被壓縮處理:

/* these formats are already compressed, or don't compress well */ 
  static const char* kNoCompressExt[] = { 
  ".jpg"".jpeg"".png"".gif"
  ".wav"".mp2"".mp3"".ogg"".aac"
  ".mpg"".mpeg"".mid"".midi"".smf"".jet"
  ".rtttl"".imy"".xmf"".mp4"".m4a"
  ".m4v"".3gp"".3gpp"".3g2"".3gpp2"
  ".amr"".awb"".wma"".wmv" 
  };

實現步驟:

1.先把需要拷貝的大檔案分割成若干個大小小於1M的小檔案(事先寫個程式來分隔或者使用一些工具,我這裡直接寫了個程式),把這些小檔案放在assets資料夾中;

2.在程式啟動時我們獲取這些小檔案的檔名,當然我們得事先規定小檔案的命名方式方便我們來獲取這些檔名;
3.通過獲得的小檔名分別建立輸入流來合併成一個大檔案,並拷貝到sdcard中。

下面是解決方法中需要用到的一些程式碼,僅供參考,不妨自己寫。分割大檔案的方法:

  1. public static void main(String[] args) throws Exception {  
        // 大檔案放置的路徑  
        String path = "D:/";  
        // 大檔案的檔名稱  
        String base = "demo";  
        String ext = ".db";  
        // 以每個小檔案1024*1024位元組即1M的標準來分割  
        int split = 1024 * 1024;  
        byte[] buf = new byte[1024];  
        int num = 1;  
        // 建立輸入流  
        File inFile = new File(path + base + ext);  
        FileInputStream fis = new FileInputStream(inFile);  
        while (true) {  
            // 以"demo"+num+".db"方式來命名小檔案即分割後為demo1.db,demo2.db,。。。。。。  
            FileOutputStream fos = new FileOutputStream(new File(path + base  
                    + num + ext));  
            for (int i = 0; i < split / buf.length; i++) {  
                int read = fis.read(buf);  
                fos.write(buf, 0, read);  
                // 判斷大檔案讀取是否結束  
                if (read < buf.length) {  
                    fis.close();  
                    fos.close();  
                    return;  
                }  
            }  
            fos.close();  
            num++;  
        }  
    }


獲取輸入流來合併檔案,我們這裡以assets資料夾下的檔案為例,raw資料夾下如何獲取輸入流之前的已經講過。
  1.   
    private void mergeApkFile(Context c, ArrayList<String> partFileList, String dst) throws IOException {  
        if (!new File(dst).exists()) {  
            OutputStream out = new FileOutputStream(dst);  
            byte[] buffer = new byte[1024];  
            InputStream in;  
            int readLen = 0;  
            for(int i=0;i<partFileList.size();i++){  
                // 獲得輸入流  
                in = c.getAssets().open(partFileList.get(i));  
                while((readLen = in.read(buffer)) != -1){  
                    out.write(buffer, 0, readLen);  
                }  
                out.flush();  
                in.close();  
            }  
            // 把所有小檔案都進行寫操作後才關閉輸出流,這樣就會合併為一個檔案了  
            out.close();  
        }  
    }  


這樣就OK了,大檔案已經拷貝到你需要的路徑中了。