Tips

Android データ入出力と永続化 外部保存域 【Android Tips】

外部保存域

8-2
データ入出力と永続化
外部保存域
この章では外部保存領域について解説していきます。
外部保存域について
今回はデータをAndroid端末の外部保存域に保存する方法について解説していきます。
外部保存域とはSDカードなどの外部メディアの事です。外部保存域に保存したデータは、どのユーザー、どのアプリケーションでも読み取りも書き込みも可能です。

プリファレンスや内部保存域とは異なり、以下の3点に注意する必要があります。

  1. マニフェストファイルへのパーミッション追加
  2. 外部メディアへの書き込みを行う場合、マニフェストファイルへのパーミッションの追加が必要となります。パーミッションを追加しないと、アプリケーション実行時に例外が発生します。

    追加するパーミッションは以下のものです。

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
  3. 外部メディアの使用可否の確認
  4. 外部メディアを使用する前に、外部メディアの状態を確認する必要があります。
    例えばSDカードは抜き差しができるので、常に書き込みが可能であるとは限りません。

    そこでEnvironmentクラスのgetExternalStorageState()を用います。
    getExternalStorageState()からの戻り値により、外部メディアの状態を識別することができます。
    Environmentクラスには外部メディアの状態を表す定数が用意されています。
    代表的なものは次の4つです。

    定数名 状態
    MEDIA_MOUNTED マウント済み
    MEDIA_MOUNTED_READ_ONLY 読み込み専用としてマウント済み
    MEDIA_REMOVED SDカードが挿入されていない
    MEDIA_UNMOUNTED SDカードは存在するがマウントされていない


  5. ファイルの作成場所
  6. Android端末では外部メディアのマウントディレクトリ(外部メディアの場所)が端末により異なります。したがって、マウントディレクトリを明示的に指定(ハードコーティング)してしまうと、端末によっては動かないということになります。そこで、各処理を実行する前に外部メディアの場所を確認する必要があります。

    外部メディアの場所はEnvironmentクラスのgetExternalStorageDirectory()を使用して取得します。
    getExternalStorageDirectory()の戻り値が、外部メディアのマウントディレクトリです。

    String sdPath = Environment.getExternalStorageDirectory();
    
内部保存領域のサンプルコード
それではサンプルコードを実際に動かしながら確認していきましょう。
activity_main.xml(レイアウトファイル)

<LinearLayout 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:orientation="vertical"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="名前を入力してください。" />

    <EditText
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="登録" />

    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="参照" />

</LinearLayout>

MainActivity.java(Activityファイル)

package com.example.outstoragesample;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {

	private String sdPath = Environment.getExternalStorageDirectory() + "/sample.txt";

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

		Button button1 = (Button) findViewById(R.id.button1);
		button1.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {

				String sdCardState = Environment.getExternalStorageState();

				if (sdCardState.equals(Environment.MEDIA_MOUNTED)) {
					EditText editText = (EditText) findViewById(R.id.editText1);
					String str = editText.getText().toString();

					FileOutputStream fos = null;
					try {
						fos = new FileOutputStream(sdPath);
						fos.write(str.getBytes());
						Toast.makeText(MainActivity.this, str + "さんを登録しました。", Toast.LENGTH_SHORT).show();

					} catch (IOException e) {
						e.printStackTrace();
						Toast.makeText(MainActivity.this, "登録できませんでした。SDカードを確認してください。", Toast.LENGTH_LONG).show();

					} finally {

						try {
							if (fos != null) {
								fos.close();
							}

						} catch (IOException e) {
							e.printStackTrace();
						}

					}
				} else if (sdCardState.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
					Toast.makeText(MainActivity.this, "このSDカードは読取専用です。", Toast.LENGTH_LONG).show();
				} else if (sdCardState.equals(Environment.MEDIA_REMOVED)) {
					Toast.makeText(MainActivity.this, "SDカードが挿入されていません。", Toast.LENGTH_LONG).show();
				} else if (sdCardState.equals(Environment.MEDIA_UNMOUNTED)) {
					Toast.makeText(MainActivity.this, "SDカードがマウントされていません。", Toast.LENGTH_LONG).show();
				} else {
					Toast.makeText(MainActivity.this, "SDカードを確認してください。", Toast.LENGTH_LONG).show();
				}
			}

		});

		Button button2 = (Button) findViewById(R.id.button2);
		button2.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {

				String sdCardState = Environment.getExternalStorageState();

				if (sdCardState.equals(Environment.MEDIA_MOUNTED) || sdCardState.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
					FileInputStream fis = null;

					try {
						fis = new FileInputStream(sdPath);
						byte[] buffer = new byte[100];
						fis.read(buffer);
						String str = new String(buffer).trim();
						Toast.makeText(MainActivity.this, str + "さんが登録されています。", Toast.LENGTH_SHORT).show();

					} catch (IOException e) {
						e.printStackTrace();
						Toast.makeText(MainActivity.this, "登録できませんでした。", Toast.LENGTH_LONG).show();

					} finally {
						try {
							if (fis != null) {
								fis.close();
							}

						} catch (IOException e) {
							e.printStackTrace();
						}

					}

				} else if (sdCardState.equals(Environment.MEDIA_REMOVED)) {
					Toast.makeText(MainActivity.this, "SDカードが挿入されていません。", Toast.LENGTH_LONG).show();
				} else if (sdCardState.equals(Environment.MEDIA_UNMOUNTED)) {
					Toast.makeText(MainActivity.this, "SDカードがマウントされていません。", Toast.LENGTH_LONG).show();
				} else {
					Toast.makeText(MainActivity.this, "SDカードを確認してください。", Toast.LENGTH_LONG).show();
				}
			}
		});

	}
}

Android Manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.outstoragesample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="7"
        android:targetSdkVersion="17" />

    <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.outstoragesample.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>

内部保存域のサンプルコードとまったく同じ部分の説明は割愛します。ここでは内部保存域と異なる部分のみ説明します。内部保存域と外部保存域との違いを確認してみてください。

MainActivityの18行目でgetExternalStorageDirectory()を用いてSDカードのディレクトリ(位置)を取得し、その後に「sample.txt」というファイルを指定しています。(この段階ではまだファイルはできていません。)
31行目と78行目ではgetExternalStorageState()を用いて、SDカードの状態を取得しています。

  1. データの書き込み処理
  2. 33行目以降でgetExternalStorageDirectory()の戻り値により場合分けを行っています。
    データ書き込み処理を行うのは、SDカードが書き込み可能な状態のときのみにする必要があります。
    つまり以下の状態が書き込み可能な状態ということになります。

    MEDIA_MOUNTED
    

    39行目ではFileOutputStreamをインスタンス化しています。
    FileOutputStream()の引数には、取得した外部メディア(今回はSDカード)のパスを渡しています。

  3. データの読み込み処理
  4. 80行目以降でgetExternalStorageDirectory()の戻り値により場合分けを行っています。
    データ読み込み処理を行うのは、SDカードが読み込み可能な状態のときのみにする必要があります。
    したがって以下の2つの状態が読み取り可能な状態となります。

    MEDIA_MOUNTED
    MEDIA_MOUNTED_READ_ONLY
    

    84行目ではFileInputStreamをインスタンス化しています。
    FileInputStream()の引数には、取得した外部メディア(今回はSDカード)のパスを渡しています。

サンプルアプリを実行し、名前を入力すると写真のようになります。

 

エミュレータの下記フォルダにsampleという名前のファイルが作成されます。
「mnt/adcard/」

 

出力したファイルを展開すると、登録した「Suzuki」が保存されているのが確認できます。

Suzuki
 

以上が外部保存域への保存方法です。

Androidアプリ開発の必須知識!JAVAプログラミングを学べる連載リンク

はじめてのJAVA 連載

Recent News

Recent Tips

Tag Search