Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android系統教程 >> Android開發教程 >> Android數據存儲之File文件儲存數據

Android數據存儲之File文件儲存數據

編輯:Android開發教程

Android數據儲存之File文件儲存數據

 

一.存儲在內部還是外部?


AndroidManifest.xml中manifest標簽下有一個屬性android:installLocation,用於指定應用程序安裝在什麼地方,該屬性有三個可選值:

  • auto:程序可能被安裝在外部存儲器上,例如SD卡;但是默認會被安裝到手機內存中。當手機內存為空時,程序將被安裝到外部存儲器上;當程序安裝到手機上後,用戶可以決定把程序放在外部存儲器還是內存中。
  • internalOnly:默認值,程序只能被安裝在內存中,如果內存為空,程序則不能成功被安裝。
  • preferExternal:將程序安裝在外部存儲器,但是系統不保證程序一定會被安裝到外部存儲器上。當外部存儲器不可以安裝或為空時,程序將被安裝到內存中。當程序使用了forward-locking機制時也將被安裝到內存中,因為外部存儲不支持此機制。程序安裝後,用戶可以自由切換程序應該在外部還是內部存儲器上。

獲取External存儲的權限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

二.openFileInput和openFileOutput


Context提供了兩個方法打開應用程序的數據文件夾裡的文件IO流:

  • FileInputStream openFileInput(String name):打開應用程序的數據文件夾下的name文件對應的輸入流。
  • FileOutputStream openFileOutput(String name, int mode):打開應用程序的數據文件夾下的name文件對應的輸出流。第二個參數指定打開文件的模式,該模式支持如下值:
    • MODE_PRIVATE:該文件只能被當前程序讀寫。
    • MODE_APPEND:以追加方式打開該文件,應用程序可以向該文件中追加內容。
    • MODE_WORLD_READABLE:該文件的內容可以被其他程序讀取。
    • MODE_WORLD_WRITEABLE:該文件的內容可以由其他程序讀寫。

Context還提供了訪問應用程序的數據文件夾的方法:

 

  • getDir(String name, int mode):在應用程序的數據文件夾下獲取或創建name對應的子目錄。
  • File getFileDir():獲取應用程序的數據文件夾的絕對路徑。
  • String[] fileList():返回應用程序的數據文件夾下的全部文件。
  • deleteFile(String):刪除應用程序的數據文件夾下的指定文件。

 核心代碼如下:

public String read() {
        try {
            FileInputStream inStream = this.openFileInput("message.txt");
            byte[] buffer = new byte[1024];
            int hasRead = 0;
            StringBuilder sb = new StringBuilder();
            while ((hasRead = inStream.read(buffer)) != -1) {
                sb.append(new String(buffer, 0, hasRead));
            }

            inStream.close();
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } 
        return null;
    }
    
    public void write(String msg){
        // 步驟1:獲取輸入值
        if(msg == null) return;
        try {
            // 步驟2:創建一個FileOutputStream對象,MODE_APPEND追加模式
            FileOutputStream fos = openFileOutput("message.txt",
                    MODE_APPEND);
            // 步驟3:將獲取過來的值放入文件
            fos.write(msg.getBytes());
            // 步驟4:關閉數據流
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 openFileOutput()方法的第一參數用於指定文件名稱,不能包含路徑分隔符“/” ,如果文件不存在,Android 會自動創建它。創建的文件保存在/data/data/<package name>/files目錄,如: /data/data/cn.tony.app/files/message.txt,

三.讀寫SD卡上的文件

讀寫SD卡上文件的步驟:

  1. 調用Environment的getExternalStorageState()方法判斷手機上是否插入了SD卡,並且應用程序具有讀寫SD卡的權限。使用如下代碼:
    //如果返回true,說明已插入SD卡,且應用程序具有讀寫SD卡的能力
    Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
  2. 調用Environment的getExternalStorageDirectory()方法來獲取外部存儲器,也就是SD卡的目錄。
  3. 使用FileInputStream、FileOutputStream、FileReader或FileWriter讀寫SD卡裡的文件。

為了讀寫SD卡上的數據,必須在AndroidManifest.xml中添加讀寫SD卡的權限:

<!-- 在SD卡中創建於刪除文件權限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 向SD卡中寫入數據權限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

接著在使用SDcard進行讀寫的時候 會用到Environment類下面的幾個靜態方法  :  

 1: getDataDirectory() 獲取到Android中的data數據目錄(sd卡中的data文件夾)

 2:getDownloadCacheDirectory() 獲取到下載的緩存目錄(sd卡中的download文件夾)

 3:getExternalStorageDirectory() 獲取到外部存儲的目錄 一般指SDcard(/storage/sdcard0)

 4:getExternalStorageState() 獲取外部設置的當前狀態 一般指SDcard,比較常用的應該是      MEDIA_MOUNTED(SDcard存在並且可以進行讀寫)還有其他的一些狀態,可以在文檔中進行查找。

 5:getRootDirectory()  獲取到Android Root路徑

以下是具體操作:

1,判斷SD卡是否存在

 

	/**
	 * 判斷SDCard是否存在 [當沒有外掛SD卡時,內置ROM也被識別為存在sd卡]
	 * 
	 * @return
	 */
	public static boolean isSdCardExist() {
		return Environment.getExternalStorageState().equals(
				Environment.MEDIA_MOUNTED);
	}

 2,獲取SD卡根目錄

 

	/**
	 * 獲取SD卡根目錄路徑
	 * 
	 * @return
	 */
	public static String getSdCardPath() {
		boolean exist = isSdCardExist();
		String sdpath = "";
		if (exist) {
			sdpath = Environment.getExternalStorageDirectory()
					.getAbsolutePath();
		} else {
			sdpath = "不適用";
		}
		return sdpath;

	}

 3,獲取默認的文件存放路徑

 

	/**
	 * 獲取默認的文件路徑
	 * 
	 * @return
	 */
	public static String getDefaultFilePath() {
		String filepath = "";
		File file = new File(Environment.getExternalStorageDirectory(),
				"abc.txt");
		if (file.exists()) {
			filepath = file.getAbsolutePath();
		} else {
			filepath = "不適用";
		}
		return filepath;
	}

 4-1,使用FileInputStream讀取文件

 

        try {
    		File file = new File(Environment.getExternalStorageDirectory(),
    				"test.txt");
            FileInputStream is = new FileInputStream(file);
            byte[] b = new byte[inputStream.available()];
            is.read(b);
            String result = new String(b);
            System.out.println("讀取成功:"+result);
        } catch (Exception e) {
        	e.printStackTrace();
        }

 4-2,使用BufferReader讀取文件

 

	try {
			File file = new File(Environment.getExternalStorageDirectory(),
					DEFAULT_FILENAME);
			BufferedReader br = new BufferedReader(new FileReader(file));
			String readline = "";
			StringBuffer sb = new StringBuffer();
			while ((readline = br.readLine()) != null) {
				System.out.println("readline:" + readline);
				sb.append(readline);
			}
			br.close();
			System.out.println("讀取成功:" + sb.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}

 

案例代碼:

// 文件寫操作函數
    private void write(String content) {
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) { // 如果sdcard存在
            File file = new File(Environment.getExternalStorageDirectory()
                    .toString()
                    + File.separator
                    + DIR
                    + File.separator
                    + FILENAME); // 定義File類對象
            if (!file.getParentFile().exists()) { // 父文件夾不存在
                file.getParentFile().mkdirs(); // 創建文件夾
            }
            PrintStream out = null; // 打印流對象用於輸出
            try {
                out = new PrintStream(new FileOutputStream(file, true)); // 追加文件
                out.println(content);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (out != null) {
                    out.close(); // 關閉打印流
                }
            }
        } else { // SDCard不存在,使用Toast提示用戶
            Toast.makeText(this, "保存失敗,SD卡不存在!", Toast.LENGTH_LONG).show();
        }
    }

    // 文件讀操作函數
    private String read() {

        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) { // 如果sdcard存在
            File file = new File(Environment.getExternalStorageDirectory()
                    .toString()
                    + File.separator
                    + DIR
                    + File.separator
                    + FILENAME); // 定義File類對象
            if (!file.getParentFile().exists()) { // 父文件夾不存在
                file.getParentFile().mkdirs(); // 創建文件夾
            }
            Scanner scan = null; // 掃描輸入
            StringBuilder sb = new StringBuilder();
            try {
                scan = new Scanner(new FileInputStream(file)); // 實例化Scanner
                while (scan.hasNext()) { // 循環讀取
                    sb.append(scan.next() + "\n"); // 設置文本
                }
                return sb.toString();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (scan != null) {
                    scan.close(); // 關閉打印流
                }
            }
        } else { // SDCard不存在,使用Toast提示用戶
            Toast.makeText(this, "讀取失敗,SD卡不存在!", Toast.LENGTH_LONG).show();
        }
        return null;
    }

 

四.操作assets、raw、res目錄下文件

1.assets

資源文件夾,在main下與res同級,與res不同的是,該目錄下的資源文件在打包apk時,會按原格式一並被打包。

有三種使用方法:

  • 在assets下放一個test.html文件,加載該文件:
    webView.loadUrl("file:///android_asset/test.html");//假設已經創建了一個WebView實例
  • 同樣是讀取test.html文件:
    //這裡的open只能打開文件,不能打開文件夾
    InputStream inputStream = getResource().getAssets().open("test.html");
  • 讀取列表、讀取圖片、讀音樂,assets目錄下包含一個images目錄和一個mp3文件xuwei.mp3,images目錄中包含一張圖片dog.jpg:

    String[] fileNames = getAssets().list("images/");//讀列表
    
    InputStream inputStream = getAssets().open("images/dog.jpg");//讀圖片
    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
    imageView.setImageBitmap(bitmap);
    
    AssetFileDescriptor assetFileDescriptor = getAssets().openFd("xuwei.mp3");//得到asset文件描述符
    player.reset();//假設已創建一個MediaPlayer實例
    player.setDataResource(assetFileDescriptor.getFileDescriptor(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength());
    player.prepare();
    player.start();

2.raw

資源文件夾,在res目錄下,系統會為res目錄下的所有資源生成相應的資源ID,raw中的文件也不例外,所以可以通過ID去訪問res/raw目錄中的任何文件,而assets目錄中的文件就需要借助AssetManager去訪問了。

assets目錄允許下面有多級子目錄,而res/raw下不允許存在目錄結構。

讀raw下的xuwei.mp3文件:

InputStream is = getResources().openRawResource(R.raw.xuwei);

3.res

res目錄下的文件都可用getResources()方法讀取。

五.SD卡文件浏覽器


利用Java的File類開發一個SD卡文件浏覽器,通過Environment.getExternalStorageDirectory()訪問系統的SD卡目錄,然後通過File的listFiles()方法獲取指定目錄下的全部文件和文件夾。

布局文件如下:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.trampcr.sdfileexplorer.MainActivity">

    <TextView
        android:id="@+id/path"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:gravity="center_horizontal" />

    <ListView
        android:id="@+id/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/path" />

    <Button
        android:id="@+id/parent"
        android:layout_width="38dp"
        android:layout_height="34dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/home" />

</RelativeLayout>

布局文件包含一個TextView用於顯示當前路徑,ListView顯示當前目錄下文件和文件夾,Button用於返回上一級目錄。

ListView中的子布局,包含一個ImageView和一個TextView:

line.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:orientation="horizontal"
    android:gravity="center_vertical">

    <ImageView
        android:id="@+id/icon"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:background="@drawable/folder"/>

    <TextView
        android:id="@+id/file_name"
        android:layout_marginLeft="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="abc"/>

</LinearLayout>

主程序代碼如下:

public class MainActivity extends AppCompatActivity {

    private ListView mListView;
    private TextView mTextView;
    //記錄當前的父文件夾
    private File mCurrentParent;
    //記錄當前路徑下的所有文件的文件數組
    File[] mCurrentFiles;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mListView = (ListView) findViewById(R.id.list);
        mTextView = (TextView) findViewById(R.id.path);
        //獲取系統的SD卡的目錄
        File root = new File(String.valueOf(Environment.getExternalStorageDirectory()));
        //如果SD卡存在
        if (root.exists()){
            mCurrentParent = root;
            mCurrentFiles = root.listFiles();
            //使用當前目錄下的全部文件、文件夾來填充ListView
            inflateListView(mCurrentFiles);
        }

        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (mCurrentFiles[position].isFile()){
                    return;
                }
                File[] tmp = mCurrentFiles[position].listFiles();
                if (tmp == null || tmp.length == 0){
                    Toast.makeText(MainActivity.this, "當前路徑不可訪問或該路徑下沒有文件", Toast.LENGTH_SHORT).show();
                }else {
                    mCurrentParent = mCurrentFiles[position];
                    mCurrentFiles = tmp;
                    inflateListView(mCurrentFiles);
                }
            }
        });
        //獲取上一級目錄的按鈕
        Button parent = (Button) findViewById(R.id.parent);
        parent.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    if (!mCurrentParent.getCanonicalFile().equals("/mnt/shell/emulated/0")){
                        mCurrentParent = mCurrentParent.getParentFile();
                        mCurrentFiles = mCurrentParent.listFiles();
                        inflateListView(mCurrentFiles);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }


    private void inflateListView(File[] files) {
        //創建一個List集合,List集合的元素是Map
        List<Map<String, Object>> listItems = new ArrayList<>();
        for (int i = 0; i < files.length; i++) {
            Map<String, Object> listItem = new HashMap<>();
            if (files[i].isDirectory()){
                listItem.put("icon", R.drawable.folder);
            }else {
                listItem.put("icon", R.drawable.file);
            }
            listItem.put("fileName", files[i].getName());
            listItems.add(listItem);
        }
        //創建一個SimpleAdapter
        SimpleAdapter simpleAdapter = new SimpleAdapter(this, listItems, R.layout.line, new String[]{"icon", "fileName"}, new int[]{R.id.icon, R.id.file_name});
        mListView.setAdapter(simpleAdapter);
        try {
            mTextView.setText("當前路徑為:" + mCurrentParent.getCanonicalPath());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

使用File[]數組填充ListView,填充是程序會根據File[]數組裡的數據元素代表的是文件還是文件夾來選擇使用文件圖標或文件夾圖標。

運行上面程序,可以看到:

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved