16 April 2014

(Android) ファイル読み書き方法・SDK23以降での権限取得方法

※2021年11月追記あり

Androidでのファイル読み書き方法は、任意のディレクトリのファイルを対象と出来るJavaのFileOutputStream/FileInputStream を使う方法と、アプリケーションのdataディレクトリ内のファイルしかアクセスできないopenFileOutput/openFileInputを使う方法がある。

20140416-android-file.jpg
作成したサンプルプログラムの画面

プログラムの画面設計と共通部分

MainActivity.java
package com.example.android_file_test_1;
 
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;
 
import android.os.Environment;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Button;
import android.widget.Toast;
import android.view.View.OnClickListener;
 
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.IOException;
 
public class MainActivity extends ActionBarActivity implements OnClickListener {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        // システムで規定されているディレクトリ一覧を文字列strに格納
        String str = "";
        str = "SD Card = " + Environment.getExternalStorageDirectory() + "\n";
        str = str + "SD Card Status = " + Environment.getExternalStorageState() + "\n";
        str = str + "Cache = " + getCacheDir() + "\n";
        str = str + "File = " + getFilesDir() + "\n";
        str = str + "Data = " + Environment.getDataDirectory() + "\n";
        str = str + "Download = " + Environment.getDownloadCacheDirectory() + "\n";
 
        LinearLayout layout = new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);
        setContentView(layout);
 
        // 書き込み、読み込みのボタン定義
        Button button = new Button(this);
        button.setText("ファイル書き込み(java)");
        button.setId(0x1001);
        layout.addView(button);
        button.setOnClickListener(this);
 
        button = new Button(this);
        button.setText("ファイル読み込み(java)");
        button.setId(0x1002);
        layout.addView(button);
        button.setOnClickListener(this);
 
        button = new Button(this);
        button.setText("ファイル書き込み(android)");
        button.setId(0x1003);
        layout.addView(button);
        button.setOnClickListener(this);
 
        button = new Button(this);
        button.setText("ファイル読み込み(android)");
        button.setId(0x1004);
        layout.addView(button);
        button.setOnClickListener(this);
 
        // テキスト文字列表示領域(最初は、システムdir一覧を表示する)
        TextView text = new TextView(this);
        text.setId(0x1005);
        text.setText(str);
        layout.addView(text);
    }
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
 
    @Override
    public void onClick(View v) {
        // ボタンのID番号で処理振り分け
        switch(v.getId()){
        case 0x1001:
            FileWrite();
            Toast.makeText(MainActivity.this, "書き込みました", Toast.LENGTH_LONG).show();
            break;
        case 0x1002:
            FileRead();
            Toast.makeText(MainActivity.this, "読み込みました", Toast.LENGTH_LONG).show();
            break;
        case 0x1003:
            FileWriteAndroid();
            Toast.makeText(MainActivity.this, "書き込みました", Toast.LENGTH_LONG).show();
            break;
        case 0x1004:
            FileReadAndroid();
            Toast.makeText(MainActivity.this, "読み込みました", Toast.LENGTH_LONG).show();
            break;
        }
    }
 
// 〜 ここに、下記に示すファイル読み書きのクラス関数が来る 〜
 
}

アクセス権限を得る

SDカードにアクセスするために、次の設定も行う。

Android 5以前の場合(Build.VERSION.SDK_INT < 23)

MainActivity.java
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android_file_test_1"
    android:versionCode="1"
    android:versionName="1.0" >
 
    <uses-sdk
        android:minSdkVersion="10"
        android:targetSdkVersion="10" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.android_file_test_1.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

または、画面上で編集することも出来る(※ Android StudioではGUIによる権限編集はできない)

20140416-filewrite-permission.jpg

Android 6以降の場合(23 <= Build.VERSION.SDK_INT)

20211122-permission-dialog.jpg
Androidの権限要求ダイアログ(SDK 23以降)

MainActivityでの権限要求処理部分のみ抜粋
public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_PERMISSION = 0x100;

    /*
     権限リクエストのダイアログでユーザが許可・拒否を選択後の処理
    */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    	// パーミッション取得が拒否された場合、デバッグログに表示する
        for(int i=0; i<permissions.length; i++){
            if(grantResults[i] != PackageManager.PERMISSION_GRANTED){
                Log.i("PERMISSION", "Permission Denied : " + permissions[i].toString());
            }
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    /*
     MainActivity作成時に1回処理される
    */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 外部記憶(SDカード)への書き込み権限の状況を得る
        int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        // 書き込み権限が無い場合、権限を得るダイアログを表示する
        if(permission != PackageManager.PERMISSION_GRANTED) {
            // 書き込み・読み込みの2つの権限を要求する
            ActivityCompat.requestPermissions(this,
                    new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE },
                    this.REQUEST_PERMISSION);
        }

    // ~ ここに各種処理(ボタンを押したときの処理)などが記述されている

    }
}

SDカード上のファイルに書き込む

バッファ無しでの書き込み例
    // SDカード等、任意のディレクトリに書きこむ
    private void FileWrite() {
         try{
            // 外部ディスクに書き込み権限があるかチエック(SDK 23以降対応)
            int permission = ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
            if(permission == PackageManager.PERMISSION_GRANTED) {
                // SDカード上のファイルの「絶対パス」フルパス名を作成
                String filename = Environment.getExternalStorageDirectory().getPath() + File.separator + "test.txt";
                // ファイルを開く(FileOutputStreamはパス名に「/」を含むことができる)
                OutputStreamWriter writer = new OutputStreamWriter(
                        new FileOutputStream(filename, true), "utf-8"
                );
                // ファイルに書き込み
                writer.write("サンプル文字列".toString());
                writer.flush();
                writer.close();

            }
            else{
                Log.i("PERMISSION", "Permission Denied : WRITE_EXTERNAL_STORAGE");
            }
        } catch(Exception e){
            Log.i("FILE ACCESS", e.toString());
        }
    }
バッファ有りでの書き込み例
    // SDカード等、任意のディレクトリに書きこむ
    private void FileWrite() {
         try{
            // 外部ディスクに書き込み権限があるかチエック(SDK 23以降対応)
            int permission = ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
            if(permission == PackageManager.PERMISSION_GRANTED) {
                // SDカード上のファイルの「絶対パス」フルパス名を作成
                String filename = Environment.getExternalStorageDirectory().getPath() + File.separator + "test.txt";
                // ファイルを開く(FileOutputStreamはパス名に「/」を含むことができる)
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
                        new FileOutputStream(filename, true), "utf-8"
                ));
                // ファイルに書き込み
                writer.write("サンプル文字列".toString());
                writer.flush();
                writer.close();

            }
            else{
                Log.i("PERMISSION", "Permission Denied : WRITE_EXTERNAL_STORAGE");
            }
        } catch(Exception e){
            Log.i("FILE ACCESS", e.toString());
        }
    }

SDカード上のファイルから読み込む

    private void FileRead() {
        String filename = Environment.getExternalStorageDirectory() + "/test.txt";
        try {
            FileInputStream fileInputStream = new FileInputStream(filename);
            BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream,"UTF-8"));
            String str = "";
            String strTemp;
            while ((strTemp = reader.readLine() ) != null){
                str = str + strTemp + "\n";
            }
            TextView text = (TextView)findViewById(0x1005);
            text.setText(str);
            reader.close();
            fileInputStream.close();
        } catch (FileNotFoundException e) {
        } catch (IOException e) {
        }
    }

ユーザディレクトリを指定するには

Download, Pictures などのユーザディレクトリはどのように指定すればよいのか...
DOWNLOADディレクトリを得る場合
String filename = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + File.separator + "test.txt";

Androidのアプリケーションディレクトリ下のファイルに書き込む

バッファ無しでの書き込み例
    // SDカード等、任意のディレクトリに書きこむ
    private void FileWrite() {
         try{
                // ファイル名(/data/data/com.example/file 以下のファイル名)
                String filename = "test.txt";
                // ファイルを開く(openFileOutputはパス名に「/」を含むことができない)
                OutputStreamWriter writer = new OutputStreamWriter(
                        openFileOutput(filename, Context.MODE_PRIVATE|Context.MODE_APPEND), "utf-8"
                );
                // ファイルに書き込み
                writer.write("サンプル文字列".toString());
                writer.flush();
                writer.close();

        } catch(Exception e){
            Log.i("FILE ACCESS", e.toString());
        }
    }
バッファ有りでの書き込み例
    // SDカード等、任意のディレクトリに書きこむ
    private void FileWrite() {
         try{
                // ファイル名(/data/data/com.example/file 以下のファイル名)
                String filename = "test.txt";
                // ファイルを開く(openFileOutputはパス名に「/」を含むことができない)
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
                        openFileOutput("test.txt", Context.MODE_PRIVATE|Context.MODE_APPEND)
                ));
                // ファイルに書き込み
                writer.write("サンプル文字列".toString());
                writer.flush();
                writer.close();

        } catch(Exception e){
            Log.i("FILE ACCESS", e.toString());
        }
    }

Androidのアプリケーションディレクトリ下のファイルから読み込む

    private void FileReadAndroid() {
        String filename = "test.txt";
        try {
            FileInputStream fileInputStream = openFileInput(filename);
            BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream,"UTF-8"));
            String str = "";
            String strTemp;
            while ((strTemp = reader.readLine() ) != null){
                str = str + strTemp + "\n";
            }
            TextView text = (TextView)findViewById(0x1005);
            text.setText(str);
            reader.close();
            fileInputStream.close();
        } catch (FileNotFoundException e) {
        } catch (IOException e) {
        }
    }