【Android的從零單排開發日記】——Android資料存放區(上)

來源:互聯網
上載者:User

標籤:android   blog   http   java   os   使用   io   strong   檔案   

在講解Android的資料來源組件——ContentProvider之前我覺得很有必要先弄清楚Android的資料結構。資料和程式是應用構成的兩個核心要素,資料存放區永遠是應用開發中最重要的主題之一,也是開發平台必須提供的基礎功能。不光是在Android平台上,在其他的平台上,資料的儲存永遠是不可缺少的一塊。Android的資料存放區是構建在Linux的檔案系統上,它充分利用Linux的帳號系統來限定應用對資料的訪問,部署了一套安全和靈活並重的資料存放區解決方案。Android的檔案架構,以及各種資料存放區手段,具體包括:Android的 檔案系統操作,設定檔案的使用,資料庫的使用,資料來源組件的使用以及雲端資料的儲存。一、Android的檔案系統
  1. Android系統檔案目錄
    目錄 內容
    system 系統目錄,放置在Android運行所需的核心庫
    data 應用目錄,放置著運行在Android上的應用及其資料
    sdcard 擴充儲存卡目錄,用來存放共用的資料
    mnt 記錄Android掛載的外部儲存資訊








  2. Android的應用資料存放區機制
    在Android中,第三方應用及其資料,都存放在data目錄下。其中,應用安裝包會被存放到/data/app/目錄下,每個安裝包的檔案名稱都形如:應用程式套件名.apk,以避免重複。
    比如包名為com.test.sample的應用,其應用資料的目錄為/data/data/com.test.sample/。對應的資料庫檔案儲存在/data/data/com.test.sample/database/目錄下,設定檔案儲存體在/data/data/com.test.sample/shared_prefs/,自訂的應用資料檔案儲存在目錄/data/data/com.test.sample/files/下,等等。
    不僅如此,Android還會為每個應用建立一個帳號,只有通過本應用的帳號才有許可權去運行該應用的安裝包檔案,讀寫應用資料目錄下的檔案(當然root許可權除外啊~),從而保證了該應用資料不會再被其他應用擷取或破壞。
  3. Android的檔案操作
    從應用資料目錄下可以看出,資料檔案可以分成兩類,一類是放置在擴充儲存空間中的檔案,即/sdcard/目錄下的檔案,它們可以被各個應用共用;而另一類則是放在該應用資料目錄下檔案,它們僅能被各個應用獨享,不能被其他應用讀寫。
    (1)擴充儲存空間中的檔案讀寫方式跟標準的java檔案處理無異。
           我們可以建立一個FileUtil的工具類來協助我們處理檔案的I/O操作,首先我們先判斷SD卡的狀態,看看SD卡是否可用,還有多少可用容量等。建立一個FileUtil的Class,加入方法
     1 // =================get SDCard information=================== 2     public static boolean isSdcardAvailable() { 3         String status = Environment.getExternalStorageState(); 4         //Environment.MEDIA_MOUNTED表示SD卡正常掛載 5         if (status.equals(Environment.MEDIA_MOUNTED)) { 6             return true; 7         } 8         return false; 9     }10 11     public static long getSDAllSizeKB() {12         //sd卡的位置13         File path = Environment.getExternalStorageDirectory();14         //StatFs擷取的都是以block為單位的15         StatFs sf = new StatFs(path.getPath());16         // 得到單個block的大小17         long blockSize = sf.getBlockSize();18         // 擷取所有資料區塊數19         long allBlocks = sf.getBlockCount();20         // 返回SD卡大小21         return (allBlocks * blockSize) / 1024; // KB22     }23 24     /**25      * free size for normal application26      * @return27      */28     public static long getSDAvalibleSizeKB() {29         File path = Environment.getExternalStorageDirectory();30         StatFs sf = new StatFs(path.getPath());31         long blockSize = sf.getBlockSize();32         long avaliableSize = sf.getAvailableBlocks();33         return (avaliableSize * blockSize) / 1024;// KB34     }

    Environment.getExternalStorageDirectory()表示擷取擴充儲存空間的目錄。(建議使用此方法動態擷取,因為sdcard這個目錄路徑是可配置的)
    StatFs.getBlockSize在API18後變為StatFs.getBlockSizeLong,其他類似的getBlock方法也一樣,關於StatFs,詳情可以看這篇博文
    然後在activity中的button1加入事件

    case R.id.button1: {            Log.d("TEST", "sdcard?"+FileUtil.isSdcardAvailable());            Log.d("TEST", "全部容量"+(float)FileUtil.getSDAllSizeKB()/1024/1024);            Log.d("TEST", "可用容量"+(float)FileUtil.getSDAvalibleSizeKB()/1024/1024);            Toast.makeText(this, "status", Toast.LENGTH_SHORT).show();            break;        }

    運行結果如下

    接下來我們來判斷某個檔案夾是否存在在SD卡中以及建立一個檔案夾

    /**     * @param director 檔案夾名稱     * @return     */    public static boolean isFileExist(String director) {        File file = new File(Environment.getExternalStorageDirectory()                + File.separator + director);        return file.exists();    }    /**     * create multiple director     * @param path     * @return     */    public static boolean createFile(String director) {        if (isFileExist(director)) {            return true;        } else {            File file = new File(Environment.getExternalStorageDirectory()                    + File.separator + director);            if (!file.mkdirs()) {                return false;            }            return true;        }    }

    其中File.separator是表示分隔字元,在不同作業系統下是不同的,如windows就是代表"/",而在Linux下卻是代表"\"。所以介意使用File.separator來代替分隔字元。File.mkdirs()表示建立一個檔案夾,且可附帶建立父目錄,而mkdir()不行,詳情的File大家可以查看官方文檔,或者看看這篇博文
    然後在activity中的button2加入響應事件

    case R.id.button2: {            Log.d("TEST", "example檔案夾存在?"+FileUtil.isFileExist("example"));            Log.d("TEST", "建立forexample檔案夾"+FileUtil.createFile("forexample"));            Toast.makeText(this, "IsFile", Toast.LENGTH_SHORT).show();            break;        }  

    運行後可以看到

    我們會發現在手機的sdcard目錄下建立了一個forexample的檔案夾。
    最後我們來實現檔案的讀和寫
    寫:

    /**     *      * @param director     *            (you don‘t need to begin with     *            Environment.getExternalStorageDirectory()+File.separator)     * @param fileName     * @param content     * @param encoding     *            (UTF-8...)     * @param isAppend     *            : Context.MODE_APPEND     * @return     */    public static File writeToSDCardFile(String directory, String fileName,            String content, String encoding, boolean isAppend) {        // mobile SD card path +path        File file = null;        OutputStream os = null;        try {            if (!createFile(directory)) {                return file;            }            file = new File(Environment.getExternalStorageDirectory()                    + File.separator + directory + File.separator + fileName);            os = new FileOutputStream(file, isAppend);            if (encoding.equals("")) {                os.write(content.getBytes());            } else {                os.write(content.getBytes(encoding));            }            os.flush();        } catch (IOException e) {            Log.e("FileUtil", "writeToSDCardFile:" + e.getMessage());        } finally {            try {                if (os != null) {                    os.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }        return file;    }    /**     * write data from inputstream to SDCard     */    public File writeToSDCardFromInput(String directory, String fileName,            InputStream input) {        File file = null;        OutputStream os = null;        try {            if (createFile(directory)) {                return file;            }            file = new File(Environment.getExternalStorageDirectory()                    + File.separator + directory + File.separator + fileName);            os = new FileOutputStream(file);            byte[] data = new byte[bufferd];            int length = -1;            while ((length = input.read(data)) != -1) {                os.write(data, 0, length);            }            // clear cache            os.flush();        } catch (Exception e) {            Log.e("FileUtil", "" + e.getMessage());            e.printStackTrace();        } finally {            try {                os.close();            } catch (Exception e) {                e.printStackTrace();            }        }        return file;    }

    從上面可以看到有兩種寫入的方法,一種是將字串直接寫入,另一種是將資料流寫到檔案中。還有一點要提的是file的預設目錄就是sdcard的目錄,所以開頭不必每次都要加sdcard的目錄路徑。
    FileOutputStream(file, isAppend) 兩個參數,左邊是File檔案,而右邊是一個boolean值,為true時,資料將會接在原來檔案的後面寫入,而false是則會覆蓋。
    讀:

    public static String ReadFromSDCardFile(String directory,String fileName){        String res="";         File file = null;        file = new File(Environment.getExternalStorageDirectory()                + File.separator + directory + File.separator + fileName);        try {            FileInputStream fis = new FileInputStream(file);            int length = fis.available();            byte [] buffer = new byte[length];             fis.read(buffer);
    //將位元組按照編碼格式轉成字串 res = EncodingUtils.getString(buffer, "UTF-8"); fis.close(); return res; }catch (FileNotFoundException e) { // TODO Auto-generated catch block Log.d("TEST", "FileNotFound"); e.printStackTrace(); }catch (Exception e) { Log.d("TEST", "Can Not Open File"); e.printStackTrace(); } return null; }

    編碼預設是UTF-8,若是想要改變的話,將其作為參數傳入就行。
    Activity中在按鈕中加入響應

    case R.id.button3: {            FileUtil.writeToSDCardFile("forexample", "test.txt",                       editText.getText().toString(), "UTF-8", true);            Toast.makeText(this, "WriteFile", Toast.LENGTH_SHORT).show();            break;        }         case R.id.button4: {            textView.setText(FileUtil.ReadFromSDCardFile("forexample", "test.txt"));            Toast.makeText(this, "ReadFile", Toast.LENGTH_SHORT).show();            break;        }

    在文字編輯框上寫入“我是cpacm”,先點擊writefile按鈕,再點擊ReadFile,得到運行結果

    同時在根目錄下的forexample檔案夾裡會找到test.txt,裡面有著“我是cpacm”的一行字。到此,檔案的讀寫成功。
    (2)放在該應用資料目錄下的檔案讀寫
         儲存在應用目錄下的私人資料目錄,通常不會通過File類的方式直接讀寫,而是利用一些封裝過的類或函數來操作。一般可以通過Context.openFileOutput來執行。
        在Activity加入兩個方法,分別為檔案的讀和寫

        public void writeFile(String fileName,String writestr){         try{                 FileOutputStream fout =openFileOutput(fileName,MODE_PRIVATE);                 byte [] bytes = writestr.getBytes();                 fout.write(bytes);                 fout.close();               }                 catch(Exception e){                 e.printStackTrace();                }         }         //讀資料    public String readFile(String fileName){       String res="";       try{              FileInputStream fin = openFileInput(fileName);              int length = fin.available();              byte [] buffer = new byte[length];              fin.read(buffer);                  res = EncodingUtils.getString(buffer, "UTF-8");              fin.close();              }          catch(Exception e){              e.printStackTrace();          }          return res;     }

    同時在按鈕的響應中加入

    case R.id.button5: {            writeFile("test2.txt",editText.getText().toString());            Toast.makeText(this, "WritePrivateFile", Toast.LENGTH_SHORT).show();            break;        }         case R.id.button6: {            textView.setText(readFile("test2.txt"));            Toast.makeText(this, "ReadPrivateFile", Toast.LENGTH_SHORT).show();            break;        }

    跟上張一樣。

    最後不要忘記在設定檔中聲明許可權

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
  二、Android設定檔案的使用

使用者在使用應用時,常常會有一些個人偏好。為了滿足不同使用者的需求,應用通常會提供對應的設定項(Preference),讓使用者根據自己的喜好選擇。這些設定資訊會儲存在本地並進行結構化地展示,使使用者可以編輯。

  1. 設定檔案的儲存和使用

    Android應用的設定資料,可以通過android.content.SharedPreferences類來表示。它提供了一組資料讀取的介面,可以從設定檔案中讀取給定索引值的整形數,布爾型數等資料。
    首先是擷取SharedPreferences
    private SharedPreferences userInfo;
            //在介面組件或服務元件中調用,構造應用預設的設定檔案,預設檔案名稱字為_preferences.xml        //userInfo = PreferenceManager.getDefaultSharedPreferences(this);          //或擷取指定名字的SharedPreferences對象  參數分別為儲存的檔案名稱和儲存模式。        userInfo = getSharedPreferences("preferences", Activity.MODE_PRIVATE);                 //讀取資料,如果無法找到則會使用預設值        String username = userInfo.getString("name", "未定義姓名");          String msg = userInfo.getString("msg", "未定義資訊");        //顯示文本        textView.setText(username+","+msg);

    兩種擷取方式,預設或者指定一個檔案
    接下來加入響應按鈕

            case R.id.button7: {            //獲得SharedPreferences的編輯器            SharedPreferences.Editor editor = userInfo.edit();            //將資訊存入相應的索引值中            editor.putString("name", editText.getText().toString()).commit();            Toast.makeText(this, "SetName", Toast.LENGTH_SHORT).show();            break;        }         case R.id.button8: {            //獲得SharedPreferences的編輯器            SharedPreferences.Editor editor = userInfo.edit();            //將資訊存入相應的索引值中ss            editor.putString("msg", editText.getText().toString()).commit();            Toast.makeText(this, "SetMessage", Toast.LENGTH_SHORT).show();            break;        }         case R.id.button9: {
    //獲得SharedPreferences檔案 userInfo = getSharedPreferences("preferences", Activity.MODE_PRIVATE); String username = userInfo.getString("name", "未定義姓名"); String msg = userInfo.getString("msg", "未定義資訊"); textView.setText(username+","+msg); Toast.makeText(this, "ShowMsg", Toast.LENGTH_SHORT).show(); break; } case R.id.button10: {
    //輸出XML檔案 textView.setText(print()); Toast.makeText(this, "ShowXML", Toast.LENGTH_SHORT).show(); break; }

    按鈕7,8可以設定資訊,按鈕9則從SharedPreferences檔案中讀取資訊並顯示在文字框中。按鈕10會顯示這個XML檔案中的所有資訊。


    訪問其他應用中的Preference(在SecondApp中訪問FirstApp的資料),前提條件是:FirstApp的preference建立時指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE許可權。

    如:在<package name>為com.first.app的應用使用下面語句建立了preference("first_app_perferences")。

    Java代碼getSharedPreferences("first_app_perferences", Context.MODE_WORLD_READABLE);  

    在SecondApp中要訪問FirstApp應用中的preference,首先需要建立FirstApp應用的Context,然後通過Context 訪問preference ,訪問preference時會在應用所在包下的shared_prefs目錄找到preference

    Context firstAppContext = createPackageContext("com.first.app", Context.CONTEXT_IGNORE_SECURITY);   SharedPreferences sharedPreferences = firstAppContext.getSharedPreferences("first_app_perferences",  Context.MODE_WORLD_READABLE);   String name = sharedPreferences.getString("name", "");  int age = sharedPreferences.getInt("age", 0); 

    如果不通過建立Context訪問FirstApp應用的preference,可以以讀取xml檔案方式直接存取FirstApp應用的preference對應的xml檔案,

    如: 
    File xmlFile = new File(“/data/data/<package name>/shared_prefs/first_app_perferences.xml”);//<package name>應替換成應用的包名: com.first.app

     

  2. 設定介面組件
    有一類特殊的Preference對象:android.preference.PreferenceGroup。它是容器型的Preference對象,負責管理一組相關聯的Preference對象。設定項編輯的介面組件,通常派生自android.preference.PreferenceActivity類。它可以將一個定製好的設定樹轉換成對應的控制項呈現出來。
    public class PreferencesDemo extends PreferenceActivity{     @Override     public void onCreate(Bundle savadInstanceState){         super.onCreate(savadInstanceState);         this.addPreferencesFromResource(R.xml.preference);     }}

【Android的從零單排開發日記】——Android資料存放區(上)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.