Android – 資料存取機制 – 共用空間存取機制

在專案運作過程中, 除了專案原始碼加上專案資源檔案編譯為.apk之後, 當使用者在執行app的時候會需要將相關資料進行儲存及使用時, 就將是本章節所要探討的資料儲存存取機制.

有以下幾個主要的面向來進行探討:

  • 使用者的偏好設定
  • 專屬於app的檔案資料
  • 可以共用的檔案資料
  • 存取本機資料庫結構
  • 讀取專案特定資源資料

共用空間存取機制


上一節介紹了專案專屬內部儲存空間, 相對的空間就是行動裝置整個的共用空間, 在早期的Android開發觀念上的SD Card, 但是目前的技術, 大多已經將記憶體進行磁區分割的原理, 多半不再需要外接SD Card, 九有足夠的儲存空間可以運用. 在共用儲存空間裡存放的檔案,通常屬於檔案格式是一般性的共用格式,也就這些檔案能夠與其它應用程式共用,也能夠讓使用者在電腦中存取檔案,諸如相片、音樂, PDF文件等。

而共用空間資料的存取, 將有可能涉及存取使用者隱私資料, 因此, 在Android 6.0 (API Level 23+) 之後, 必須在執行階段再度要求使用者給予app的權限, 才得以進行存取. 這部分將在本單元後續詳細說明.

Environment


Environment類別提供了該行動裝置相關的環境資訊以供程式開發運用.

取得外部共用空間的根路徑.

File extRoot = Environment.getExternalStorageDirectory();
Log.i("brad", "external: " + extRoot.getAbsolutePath());

因為Android的廠牌機種眾多, 各家處理的方式各有不同, 千萬不要以絕對路徑字串內容來進行處理, 而應以File物件相對路徑方式處理.

File alarmsPath = new File(extRoot, Environment.DIRECTORY_ALARMS);
File dcimPath = new File(extRoot, Environment.DIRECTORY_DCIM);
File docPath = new File(extRoot, Environment.DIRECTORY_DOCUMENTS);
File downloadPath = new File(extRoot, Environment.DIRECTORY_DOWNLOADS);
File moviePath = new File(extRoot, Environment.DIRECTORY_MOVIES);
File musicPath = new File(extRoot, Environment.DIRECTORY_MUSIC);
File notificationPath = new File(extRoot, Environment.DIRECTORY_NOTIFICATIONS);
File picPath = new File(extRoot, Environment.DIRECTORY_PICTURES);
File podcastPath = new File(extRoot, Environment.DIRECTORY_PODCASTS);
File ringPath = new File(extRoot, Environment.DIRECTORY_RINGTONES);

或是,

File alarmsPath = Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_ALARMS);
File dcimPath = Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_ALARMS);
File docPath = Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_DOCUMENTS);
File downloadPath = Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_DOWNLOADS);
File moviePath = Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_MOVIES);
File musicPath = Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_MUSIC);
File notificationPath = Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_NOTIFICATIONS);
File picPath = Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES);
File podcastPath = Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PODCASTS);
File ringPath = Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_RINGTONES);

 

呼叫使用getExternalStorageState()傳回現在外部儲存空間的狀態,在接收到狀態後來辨別為何種狀態。

  • Environment.MEDIA_MOUNTED:外部儲存空間存在且正常掛載。
  • Environment.MEDIA_REMOVED:外部儲存空間不存在。
  • Environment.MEDIA_UNMOUNTED:外部儲存空間但沒有掛載,在系統中刪除。
  • Environment.MEDIA_MOUNTED_READ_ONLY:外部儲存空間存在且正常掛載但只能讀取。

Environment.MEDIA_UNMOUNTABLE:外部儲存空間存在但無法正常掛載。

public boolean isExternalStorage() {
   String state = Environment.getExternalStorageState();
   if (Environment.MEDIA_MOUNTED.equals(state)) {
       return true;
   }
   return false;
}

 

處理應用程式使用權限

對於使用者使用應用程式而言, 部分的操作行為必須讓使用者知道認知將會使用到的相關權限. 而這樣的宣告使用, 將會放在AndroidManifest.xml中. 本單元將會使用到存取外部空間的危險權限, 請參考: 應用程式使用權限處理 單元

 

進行共用空間檔案資料存取


此時, 就可以依照Java I/O處理機制進行資料存取. 以下針對Documents資料夾進行檔案存取為例.

版面配置處理:

<?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="match_parent"
    android:orientation="vertical"
    >
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="test1"
        android:onClick="test1"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="test2"
        android:onClick="test2"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="read"
        android:onClick="test3"
        />

    <TextView
        android:id="@+id/mesg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />

</LinearLayout>

在MainActivity.java中, 以兩種不同的方式進行寫出, 一種方式永遠以目前的資料作為檔案最新的內容, 另一種方式則是以Append模式添加在檔案末端的方式.

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

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

public class MainActivity extends AppCompatActivity {
    private File docPath;
    private TextView mesg;

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

        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(this,
                        Manifest.permission.READ_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED) {
            // 尚未獲得授權
            // 詢問使用者, 要求授權
            ActivityCompat.requestPermissions(this,
                    new String[]{
                            Manifest.permission.READ_EXTERNAL_STORAGE,
                            Manifest.permission.WRITE_EXTERNAL_STORAGE
                    },
                    0);

        }else{
            // 已經取得授權
            init();
        }

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        // 使用者回應狀況
        if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            init();
        }
    }

    private void init(){
        // 程式正式從此處開始
        mesg = (TextView)findViewById(R.id.mesg);

        docPath = Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_DOCUMENTS
        );

        if (!docPath.exists()){
            docPath.mkdirs();
        }
    }

    public void test1(View view){
        String data = "Hello, World\n";
        File myData = new File(docPath, "MyData.txt");
        try {
            FileOutputStream fout =
                    new FileOutputStream(myData);
            fout.write(data.getBytes());
            fout.flush();
            fout.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    public void test2(View view){
        String data = "Hello, World\n";
        File myData = new File(docPath, "MyData.txt");
        try {
            FileOutputStream fout =
                    new FileOutputStream(myData, true);
            fout.write(data.getBytes());
            fout.flush();
            fout.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void test3(View view){
        File myData = new File(docPath, "MyData.txt");
        try {
            BufferedReader reader =
                    new BufferedReader(
                            new InputStreamReader(
                                    new FileInputStream(myData)));
            String line;
            StringBuilder builder = new StringBuilder();
            while ((line = reader.readLine()) != null){
                builder.append(line + "\n");
            }
            reader.close();
            mesg.setText(builder);
        }catch(Exception e){
            Log.i("brad", e.toString());
        }

    }

}

 

專案專屬檔案放在共用空間


對於行動裝置而言, 共用儲存空間往往較內存儲存空間要大許多, 如果檔案的特性是屬於專案專屬檔案, 沒有app的存在也就沒有存在的意義, 但是又想要存放在空間較大的共用空間時, 則可以放在以下程式中的privatePath之下.

File root = Environment.getExternalStorageDirectory();
File privatePath = new File(root, "Android/" + getPackageName() + "/");
if (!privatePath.exists()){
    privatePath.mkdirs();
}

一但專案被移除, 該資料夾下的所有檔案也將會被移除掉.

 

 

 

本站資源一切隨緣,
不用註冊, 不看廣告
如果對您有所助益,
歡迎功德隨喜, 金額隨意,
請點擊以下...(感謝您)

功德箱/打賞箱

%d bloggers like this: