Hướng dẫn lập trình Android với bộ lưu trữ ngoài (External Storage)
Công ty Vĩnh Cửu tuyển dụng lập trình viên Java

1- Android External Storage

Android External Storage: là nơi lưu trữ dữ liệu ngoài của Android, các file dữ liệu lưu trữ mà bạn lưu trữ tại đây không được hệ thống áp dụng bảo mật.

Thông thường có 2 loại lưu trữ ngoài (external storage).
  1. Lưu trữ ngoài cố định: Thường được hiểu là ổ cứng của điện thoại.
  2. Lưu trữ ngoài lưu động (Removeable Storage): Chẳng hạn SD Card.
Sử dụng các phương tĩnh của class Environment bạn có thể lấy được các thông tin về thư mục của các bộ lưu trữ ngoài.
Bảng kết quả dưới đây chạy trên thiết bị mô phỏng.
Phương thức Trả về
getDataDirectory() /data
getDownloadCacheDirectory() /cache
getExternalStorageState() mounted
getExternalStoragePublicDirectory(Environment.Music): /storage/emulated/0/Music
getDownloadCacheDirectory() /cache
getRootDirectory() /system
Để đọc ghi dữ liệu trên bộ lưu trữ ngoài yêu cầu bạn phải cấu hình AndroidManifest.xml, thêm vào:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Với Android Level >= 23 để đọc ghi dữ liệu trên thiết bị lưu trữ ngoài, bạn cần phải hỏi người dùng bằng cách sử dụng code. (Xem thêm trong ví dụ).

2- Ví dụ đọc ghi file trên bộ lưu trữ ngoài

Tạo một project có tên ExternalStorageDemo.
Cấu hình AndroidManifest.xml cho phép đọc ghi dữ liệu trên bộ nhớ lưu trữ ngoài.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Nội dung đầy đủ của AndroidManifest.xml:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.o7planning.externalstoragedemo">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
 
Giao diện ứng dụng:
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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true">

        <requestFocus />
    </EditText>

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="120dp"
        android:id="@+id/textView"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/editText"
        android:layout_above="@+id/button_save"
        android:layout_marginTop="10dp" />

    <Button
        android:id="@+id/button_save"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="save"
        android:layout_alignParentBottom="true"
        android:layout_alignLeft="@+id/textView"
        android:layout_alignStart="@+id/textView" />

    <Button
        android:id="@+id/button_read"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/button_save"
        android:layout_alignBottom="@+id/button_save"
        android:layout_toRightOf="@+id/button_save"
        android:text="read" />

    <Button
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="List Dirs"
        android:id="@+id/button_list"
        android:layout_alignBottom="@+id/button_read"
        android:layout_toRightOf="@+id/button_read"
        android:layout_toEndOf="@+id/button_read" />

</RelativeLayout>
MainActivity.java
package org.o7planning.externalstoragedemo;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class MainActivity extends Activity {
   private EditText editText;
   private TextView textView;
   private Button saveButton;
   private Button readButton;
   private Button listButton;


   private static final int REQUEST_ID_READ_PERMISSION = 100;
   private static final int REQUEST_ID_WRITE_PERMISSION = 200;

   private final String fileName = "note.txt";

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

       editText = (EditText) findViewById(R.id.editText);
       textView = (TextView) findViewById(R.id.textView);

       saveButton = (Button) findViewById(R.id.button_save);
       readButton = (Button) findViewById(R.id.button_read);
       listButton = (Button) findViewById(R.id.button_list);

       saveButton.setOnClickListener(new OnClickListener() {

           @Override
           public void onClick(View arg0) {
               askPermissionAndWriteFile();
           }

       });


       readButton.setOnClickListener(new OnClickListener() {

           @Override
           public void onClick(View arg0) {
               askPermissionAndReadFile();
           }

       });

       listButton.setOnClickListener(new OnClickListener() {

           @Override
           public void onClick(View arg0) {
               listExternalStorages();
           }

       });
   }

   private void askPermissionAndWriteFile() {
       boolean canWrite = this.askPermission(REQUEST_ID_WRITE_PERMISSION,
               Manifest.permission.WRITE_EXTERNAL_STORAGE);
       //
       if (canWrite) {
           this.writeFile();
       }
   }

   private void askPermissionAndReadFile() {
       boolean canRead = this.askPermission(REQUEST_ID_READ_PERMISSION,
               Manifest.permission.READ_EXTERNAL_STORAGE);
       //
       if (canRead) {
           this.readFile();
       }
   }


   // Với Android Level >= 23 bạn phải hỏi người dùng cho phép các quyền với thiết bị
   // (Chẳng hạn đọc/ghi dữ liệu vào thiết bị).
   private boolean askPermission(int requestId, String permissionName) {
       if (android.os.Build.VERSION.SDK_INT >= 23) {
 
           // Kiểm tra quyền
           int permission = ActivityCompat.checkSelfPermission(this, permissionName);


           if (permission != PackageManager.PERMISSION_GRANTED) {
       
               // Nếu không có quyền, cần nhắc người dùng cho phép.
               this.requestPermissions(
                       new String[]{permissionName},
                       requestId
               );
               return false;
           }
       }
       return true;
   }

   // Khi yêu cầu hỏi người dùng được trả về (Chấp nhận hoặc không chấp nhận).
   @Override
   public void onRequestPermissionsResult(int requestCode,
                                          String permissions[], int[] grantResults) {

       super.onRequestPermissionsResult(requestCode, permissions, grantResults);
       //

       // Chú ý: Nếu yêu cầu bị hủy, mảng kết quả trả về là rỗng.
       if (grantResults.length > 0) {
           switch (requestCode) {
               case REQUEST_ID_READ_PERMISSION: {
                   if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                       readFile();
                   }
               }
               case REQUEST_ID_WRITE_PERMISSION: {
                   if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                       writeFile();
                   }
               }
           }
       } else {
           Toast.makeText(getApplicationContext(), "Permission Cancelled!", Toast.LENGTH_SHORT).show();
       }
   }


   private void writeFile() {
       // Thư mục gốc của SD Card.
       File extStore = Environment.getExternalStorageDirectory();
       // ==> /storage/emulated/0/note.txt
       String path = extStore.getAbsolutePath() + "/" + fileName;
       Log.i("ExternalStorageDemo", "Save to: " + path);

       String data = editText.getText().toString();

       try {
           File myFile = new File(path);
           myFile.createNewFile();
           FileOutputStream fOut = new FileOutputStream(myFile);
           OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut);
           myOutWriter.append(data);
           myOutWriter.close();
           fOut.close();

           Toast.makeText(getApplicationContext(), fileName + " saved", Toast.LENGTH_LONG).show();
       } catch (Exception e) {
           e.printStackTrace();
       }
   }

   private void readFile() {
       // Thư mục gốc của SD Card.
       File extStore = Environment.getExternalStorageDirectory();
       // ==> /storage/emulated/0/note.txt
       String path = extStore.getAbsolutePath() + "/" + fileName;
       Log.i("ExternalStorageDemo", "Read file: " + path);

       String s = "";
       String fileContent = "";
       try {
           File myFile = new File(path);
           FileInputStream fIn = new FileInputStream(myFile);
           BufferedReader myReader = new BufferedReader(
                   new InputStreamReader(fIn));

           while ((s = myReader.readLine()) != null) {
               fileContent += s + "\n";
           }
           myReader.close();

           this.textView.setText(fileContent);
       } catch (IOException e) {
           e.printStackTrace();
       }
       Toast.makeText(getApplicationContext(), fileContent, Toast.LENGTH_LONG).show();
   }

   private void listExternalStorages() {
       StringBuilder sb = new StringBuilder();

       sb.append("Data Directory: ").append("\n - ")
               .append(Environment.getDataDirectory().toString()).append("\n");

       sb.append("Download Cache Directory: ").append("\n - ")
               .append(Environment.getDownloadCacheDirectory().toString()).append("\n");

       sb.append("External Storage State: ").append("\n - ")
               .append(Environment.getExternalStorageState().toString()).append("\n");

       sb.append("External Storage Directory: ").append("\n - ")
               .append(Environment.getExternalStorageDirectory().toString()).append("\n");

       sb.append("Is External Storage Emulated?: ").append("\n - ")
               .append(Environment.isExternalStorageEmulated()).append("\n");

       sb.append("Is External Storage Removable?: ").append("\n - ")
               .append(Environment.isExternalStorageRemovable()).append("\n");

       sb.append("External Storage Public Directory (Music): ").append("\n - ")
               .append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).toString()).append("\n");

       sb.append("Download Cache Directory: ").append("\n - ")
               .append(Environment.getDownloadCacheDirectory().toString()).append("\n");

       sb.append("Root Directory: ").append("\n - ")
               .append(Environment.getRootDirectory().toString()).append("\n");

       Log.i("ExternalStorageDemo", sb.toString());
       this.textView.setText(sb.toString());
   }

}
Chạy ứng dụng:
Sử dụng "Android Device Manager" bạn có thể xem file được tạo ra.
Xem thêm về "Công cụ quản lý thiết bị Android - Android Device Manager"