ファイル
資料
http://developer.android.com/guide/topics/resources/index.html
1.1 概説
FileIOはJavaの一般の機能です。JavaIOのおさらいを付録Aに記述しています。
Androidに特有なことは、InputStream/OutputStreamオブジェクトの取得のしかたです。通常のファイルの場合は、ファイル名から取得します。InputStream/OutputStreamオブジェクトをいったん取得すると、後は通常のJavaIOと同じです。
Android はリソースファイルを複数種類提供しています。Androidで扱うファイルの種類を表1-1に示します。
表1-1 ファイルの種類
# |
区分 |
場所 |
特徴 |
1 |
リソース |
res/rawディレクトリ下のファイル |
リソースIDでInputStreamを取得する。 |
res/xml下のファイル |
リソースIDでInputStreamを取得する xmlパーサを使う。 |
||
assetsディレクトリ下のファイル*1 |
ファイル名でInputStreamを取得する。 |
||
2 |
リソース外 |
filesディレクトリ下のファイル |
ファイル名でInputStream/OutputStreamを取得する。 |
SDカード |
ファイル名でInputStream/OutputStreamを取得する。 |
注*1 assetsディレクトリはresディレクトリの下ではなく、resディレクトリと同じレベルに置かれます。
ファイルの種類によってInputStream/OutputStreamを取得する方法が異なっています。リソースファイルの種類ごとにInputStreamオブジェクトの取得方法を説明します。
1.2 res/raw下のファイル
Resources(android.content.res)オブジェクトのopenRawResource()メソッドを使います。また、Resourcesオブジェクトは通常、Context.getResources()メソッドで取得します。
コード例を次に示します。
// contextにContextオブジェクトが設定されている場合 Resources resources = context.getResources(); InputStream inputStream = resources.openRawResource(R.raw.hello);
// Activity内では、次にように記述できます。 InputStream inputStream = getResources().openRawResource(R.raw.hello); |
注 ファイルはraw直下になければなりません。
1.3xml下のファイル
ResourcesオブジェクトのgetXml()メソッドで、XmlResourceParserオブジェクトを取得します。XmlResourceParserは、標準のXmlPullParserを実装しています。その他に、AttributeSetインタフェースとclose()メソッドを実装しています。いったん、XmlResourceParserオブジェクトを取得すれば、通常の(Androidとは限らない)xmlをXmlPullParserで解析するのと同じ方法が使えます。
XmlPullParser の使い方については本資料では説明しません。XmlPullParserの使用例は、次の資料に記述されています。
資料
http://www.ibm.com/developerworks/opensource/library/x-android/index.html(英語)
http://www.ibm.com/developerworks/jp/xml/library/x-android/(日本語)
Android で XML を扱う,モバイル機器用の Java アプリケーションを作成する
この資料が扱っている資料は、xml下のxmlではありませんが、XmlPullParserの使い方は同じです。
1.4assets下のファイル
AssetManager(android.content.res)オブジェクトのopen()メソッドをを使います。AssetManagerオブジェクトはContext.getAssets()メソッドで取得できます。
コード例を次に示します。
// contextにContextオブジェクトが設定されている場合 AssetManager assetManager = context.getAssets(); InputStream inputStream = assetManager.open("file name");
// Activity内では、次にように記述できます。 InputStream inputStream = getAssets ().open("file name"); |
注file nameは、拡張子があれば拡張子も含みます。assetsの直下である必要はありません。
open()メソッドは、第2引数でアクセスモードを設定する版もあります。
AssetManagerはファイルを扱うためのメソッドをいくつか持っています。このうち、list()メソッドを紹介します。
list()メソッド
public final String[] list (String path)
引数
# |
引数 |
説明 |
1 |
path |
assets下の相対的パス。例docs/home.html |
戻り値String型の配列。与えられたパスにある全ファイル(asset)のリスト。ファイル名は、与えられたパスに相対的です。
コード例を次に示します。
String[] list = assetManager.list ("diamond"); |
1.5files下のファイル
アプリの中で作成し、読むファイルです。"private file"と呼んでいます。他のアプリに公開するフラグはありますが、他ののアプリから参照する方法は記述されていません。
InputStreamオブジェクトとOutputStreamオブジェクトはそれぞれContextのopenFileInput()メソッドとopenFileOutput()メソッドを使って取得します。ファイルを削除するメソッドdeleteFile()メソッドもあります。
各メソッドの第1引数はファイル名です。
openFileOutput()メソッドは第2引数でモードを指定できます。アクセスをアプリ内に限るか否かを指定できます。また同じ引数で新規に作るか、既存のファイルの最後から続けて書くか指定できます。
openFileInput()メソッドとopenFileOutput()の戻り値の型はそれぞれ、FileInputStreamとFileOutputStreamです。これはそれぞれ、InputStreamとOutputStreamを実装した型ですので、InputStream又はOutputStream型が必要な場合は、そのまま使えます。ファイルを扱う場合は、FileInputStreamとFileOutputStream型で受けると便利です。
deleteFile()メソッドの戻り値はbooleanです。削除できたときはtrue、削除できなかったときはfalseを戻します。
ファイルの実体は次の場所に作成されます。これはDDMSのファイルのエクスプローラで確認できます。
data/data/<アプリ パッケージ>/files/<ファイル名>
コード例
FileOutputStream outputStream = openFileOutput("some.txt", Context.MODE_PRIVATE); FileInputStream inputStream = openFileInput("some.txt"); boolean rtn = deleteFile("some.txt"); |
1.6 SDカード
AVDでSDカードを模擬する方法は付録 Bを参照して下さい。
SDカードは、通常のPCのハードディスクの役割をします。単にファイルIOだけでなく、ディレクトリの作成・削除等もできなければなりません。そのためにAndroidは、ファイルの実体に対応するFileクラス(java.io)を取得するメソッドを提供しています。FileクラスはAndroid特有のクラスではありません。Javaの標準ライブラリに含まれるクラスです。Fileクラスは、ファイルやディレクトリの作成・削減・名前の変更等、入出力ではなく、ファイルの実体を操作するメソッドを持っています。Fileオブジェクトを引数にして、ファイルIOのオブジェクトを生成できます。
SDカードに対応するFileオブジェクトは、Environment(android.os)オブジェクトのgetExternalStorageDirectory()メソッドを使って取得します。
ユーザ(ケイタイの利用者)が外部記憶装置を挿入していたとしても、そのディレクトリにアクセスできないこともあります。また、外部記憶装置を挿入していなかったとか、他の何らかの問題が起きて、そのディレクトリにアクセスできないこともあります。アプリはEnvironment.getExternalStorageState()メソッドを使って、そのときの状態を知ることができます。
アプリは最上位のディレクトリは使うのべきではありません。というのは、ユーザの名前空間のルートを他の名前と混じらないようにすべきだからです。アプリ内だけで利用するファイルはすべてContext.getExternalFilesDir()メソッドで取得できるディレクトリに置くべきです。そこに置いておくと、該当するアプリがアンインストールされると、Androidが自動的にそのファイルを削除します。アプリ間で共用するファイルはEnvironment.getExternalStoragePublicDirectory(String)メソッドで取得できるディレクトリに置くべきです。
外部記憶装置の状態をモニターするコードの例が次の資料(マニュアル)に記載されていますので参照して下さい。
http://developer.android.com/reference/android/os/Environment.html#getExternalStorageDirectory()
次は、
File topDir = Environment.getExternalStorageDirectory(); String strDir1Path = topDir.getAbsolutePath(); File dir1 = new File(strDir1Path); dir1.mkdir(); String strFile1 = dir1.getAbsolutePath() + "/file1.txt"; File file1 = new File(strFile1); file1.createNewFile(); // IOException FileOutputStream fos = new FileOutputStream(file1);// FileNotFoundException fos.write("Hello, World!".getBytes());//IOException |
注 例外の処理は省略しています。実用のIOのコードとして推奨できる内容ではありません。SDカードの扱い方を示す目的のコードです。
SDカードに書き込む場合は、そのためのパーミッションが必要です。次のパーミッションをAndroidManifest.xmlに追加しなければなりません。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> |
2.例
例題のソースコードは付録に掲載します。また、web上の参考になるサイトも付録で紹介しています。
2.1 ヘルパーメソッド
ファイルの種類に関係なく使うヘルパーメソッドをまず作成します。
2.1.1 readTextFile
引数でInputStreamを与えて、そこからテキストファイルを読み、ログにプリントするメソッドです。テキストはBufferedReaderオブジェクトを使って読みます。
仕様
private void readTextFile(InputStream is,String title)
titleはデバッグ用の情報を書くときに、タイトルとして使う文字列です。
これはJavaのjdkの環境でテストできます。
2.1.2 writeTextFile
引数でOutputStreamを与えて、そこへテキストファイルを書きます。内容は固定します。
仕様
private void writeTextFile(OutputStream os,String title, String prefix)
prefixは各行の先頭に追加する文字列です。
これはJavaのjdkの環境でテストできます。
2.1.2 parseXml()
これは、一度しか使いません。理解しやすくする目的で一つのメソッドにまとめます。
2.2 ファイルの入出力
この例で使うファイルはテキストファイルです。コードはUTF-8にします。
注DDMSのログは"漢字"を表示できないようです(設定の問題かもしれません)。漢字を含む場合は、TextView等で表示して確認して下さい。掲載したコードは、ファイルの内容をログに出力していますので漢字が化けます。
2.2.1res/rawのファイル
res/rawの下にraw_test.txtのファイルを作ります。このファイルを読むためのコードは次の通りです。
// raw InputStream rawIs = getResources().openRawResource(R.raw.raw_test); readTextFile(rawIs,"raw resource"); |
2.2.2xmlファイル
res/xmlの下にstations.xmlファイルを作成します。xmlの解析は、xmlの形式に依存します。この例で使ったxmlファイルの一部を次に示します。
<?xml version="1.0" encoding="utf-8"?> <stations> <station>Tokyo</station> <station>Shinagawa</station> ... </stations> |
このファイルを解析するためのXmlPullParserオブジェクトを取得するコードは次の通りです。
// xml XmlPullParser xpp = getResources().getXml(R.xml.stations); |
2.2.3assets下のファイル
assetsディレクトリの下にディレクトリsubdir_assetsを作ります。subdir_assetsの下にasset_test.txtファイルを作ります。このファイルを読むためのコードは次の通りです。
//assets try{ InputStream assetIs = getAssets().open("subdir_assets/asset_test.txt"); readTextFile(assetIs,"asset file"); }catch(IOException e){ Log.e(tag,"assetsIs IOException msg=" + e.getMessage()); } |
リソースIdを使う場合と違って、assets下のファイルをオープンするときは、例外が生じる恐れがあります。try・catch構文等でこれを捕える処理が必要です。
2.2.4files下のファイル
ファイルの生成もプログラム内で行わなければなりません。ファイル名はfiles_test.txtにします。次のコードは、前半のtry-catchがこのファイルへの出力、後半のtry-catchがこのファイルからの入力の処理をしています。
//files String filesFileName = "files_test.txt"; try{ FileOutputStream filesOs = openFileOutput(filesFileName, Context.MODE_PRIVATE); writeTextFile(filesOs,"files output file","files"); }catch(FileNotFoundException e){ Log.e(tag,"FileNotFoundException msg=" + e.getMessage()); } try{ InputStream filesIs = openFileInput(filesFileName); readTextFile(filesIs,"files input file"); }catch(FileNotFoundException e){ Log.e(tag,"FileNotFoundException msg=" + e.getMessage()); } |
実行後、data/data/<アプリ パッケージ>/filesの下にfiles_test.txtができていることを確認して下さい。
2.2.5 SDカード
SDカードをマウントする方法は付録を参照して下さい。
SDカードの最上位のディレクトリの下にディレクトリdir1を作り、その下にfile1.txtを作ります。これにデータを書き込み、いったんクローズした後、このファイルを読みます。このコードを次に示します。
Fileクラスを使っている点がこれまでと異なります。FileIOのオブジェクトは、Fileオブジェクトを引数に取るコンストラクタを使って生成しています。もちろん、Fileオブジェクトの代わりに、ファイル名を使ってもできます。
// SDカード // 最上位のディレクトリを取得 File topDir = Environment.getExternalStorageDirectory(); // 一つしたのディレクトリを作る。 String strDir1Path = topDir.getAbsolutePath() + "/dir1"; File dir1 = new File(strDir1Path); dir1.mkdir(); // ファイルの作成 String strFile1 = dir1.getAbsolutePath() + "/file1.txt"; File file1 = new File(strFile1); FileOutputStream sdOs = null; try{ file1.createNewFile(); // IOException }catch(IOException e){ Log.e(tag,"IOException msg=" + e.getMessage()); } // ファイルへの出力 try{ sdOs = new FileOutputStream(file1);// FileNotFoundException }catch(FileNotFoundException e){ Log.e(tag,"FileNotFoundException msg=" + e.getMessage()); } writeTextFile(sdOs,"SD output file","SD"); // ファイルからの入力 try{ InputStream sdIs = new FileInputStream(file1); readTextFile(sdIs,"SD input file"); }catch(FileNotFoundException e){ Log.e(tag,"FileNotFoundException msg=" + e.getMessage()); } |
実行するためには次のパーミッションをAndroidManifest.xmlに追加しなければなりません。
付録
付録 A Java IOのおさらい
Javaの入出力の基本のクラスはInputStreamクラスとOutputStreamクラスです。
入力を担当するInputStreamに限定して説明します。
InputStreamクラスがどれくらい基本のクラスかといえば、もしInputStreamクラスのインスタンスがなんらかの方法で取得できたとしたら、JavaのIOの機能がすべて利用できるといえます。
InputStreamクラスは抽象クラスです。ですから、正確に言えば、InputStreamクラスのインスタンスを取得するということは、InputStreamを継承した具体クラスのインスタンスを得るという意味です。
ファイルの場合を例あげましょう。
1:InputStream is = new FileInputStream("test");
2:BufferedInputStream bis = new BufferedInputStream(is);
3:DataInputStream dis = new DataInputStream(is);
4:InputStreamReader isr = new InputStreamReader(is)
5:BufferedReader br = new BufferedReader(isr) *1
ここに挙げたクラスは、どれもJavaIOの重要なクラスです。
ここで大切なことは、第1行目でいったんInputStreamオブジェクトを取得すると、後のインスタンシェートにはファイル名が不要だということです。
仮に、第1行目の代わりに、何らかの方法でInputStreamオブジェクトを取得できると、2行目以降は、そのまま使えるということです。
逆に言えば、ファイルに限らず、InputStreamオブジェクトを提供できるモノ(オブジェクト)があれば、JavaIOのクラスが活用できるということです。
開発者の立場から言えば、InputStreamオブジェクトを提供するオブジェクトがある場合、それをどのように取得するか分かれば、その後はJavaIOの知識がそのまま生かせるということです。
注*1 通常、ファイル名が分かっている場合は次の方法を使います。
BufferedReader br = new BufferedReader(new FileReader("test")
br = new BufferedReader(isr)の方法はファイル名が分かっていなくても使える方法です。使用頻度が高いファイル名が分かっている場合については、簡略にできるようにFileReaderクラスが低級されていると考えられます。
付録 B SDカードの模擬
AVDを作る際、SDカード作成の指定をした場合、そのAVDを使うと自動でSDカードがマウント(挿入)されるはずです。
次の説明はAVDを作る際、SDカード作成の指定をしなかった場合です。SDカードを使うテストが多くはない場合、この方法が適しています。
B.1SDカードイメージの作成
資料 http://developer.android.com/guide/developing/tools/othertools.html
mksdcard [-l label] <size>[K|M] <file>
例 >mksdcard -l sdcard1 10M C:\android\sdcard.iso
sizeで指定した値だけ割り当てられます。割り当ての時間がかかり、かつファイルもそれだけ消費します。そのため、sizeは必要最小限に設定すべきです。ただし、9M以上でなければなりません。実際には10M以上ないとエラーが出るようです。ただし、環境に依存するかもしれません。
B.2SDカードイメージ付きでemulatorを起動
B.2.1 コマンドプロセッサーから起動する場合
emulator -avd AVD名 -sdcard full_file_name
例>emulator -avd the2nd_AVD -sdcard C:\android\sdcard.iso
B.2.2eclipse場合
実行の構成で、実行時のパラメータを設定します。
Run >> Run Configurations >> targetタブ
>> 下の部分 Additional Emulator Command Line Optionsの入力領域に
-sdcard full_file_nameを設定します。
B.3 パーミッションの追加
SDカードに書き込む場合は次のパーミッションが必要です。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> |
付録 C 参考webサイト
raw フォルダ
http://android-er.blogspot.com/2010/07/display-text-file-in-resraw_01.html
Beginner to beginner
Thursday, July 1, 2010
Display text file in /res/raw
xml
http://thedevelopersinfo.com/2009/12/14/using-xml-file-resources-in-android/
The Developer's Info
Using XML file resources in Android
By Oleg Mazurashu, on December 14th, 2009
assets
http://thedevelopersinfo.com/2009/11/17/using-assets-in-android/
The Developer's Info
Using assets in Android
By Oleg Mazurashu, on November 17th, 2009
files
http://thedevelopersinfo.com/2009/11/26/using-filesystem-in-android/
The Developer's Info
Using filesystem in Android
By Oleg Mazurashu, on November 26th, 2009
SDカード
http://thedevelopersinfo.com/2010/01/13/working-with-sdcards-filesystem-in-android/
The Developer's Info
Working with SDCard’s filesystem in Android
By Oleg Mazurashu, on January 13th, 2010
http://thedevelopersinfo.com/2009/10/17/putting-files-on-sdcard-in-android/
Putting files on SDCard in Android
コマンドでSDカードにファイルをコピーする方法が記述されています。
http://thedevelopersinfo.com/2009/10/13/creating-sdcard-for-android-emulator/
Creating SDCard for Android emulator
本資料でも紹介したemulator用のSDカードの作り方を説明しています。
http://thedevelopersinfo.com/2009/10/13/emulating-sdcard-in-android-emulator/
Emulating SDCard in Android emulator
SDをマウントしたAVDをコマンドから起動する方法を説明しています。
付録D 例題のソースコード
Activity
public class HelloRes_0_1 extends Activity { private String tag = this.getClass().getSimpleName(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.d(tag,"onCreate starts.");
// raw InputStream rawIs = getResources().openRawResource(R.raw.raw_test); Log.d(tag,"onCreate calling readTextFile."); readTextFile(rawIs,"raw resource");
// xml parseXml();
//assets try{ InputStream assetIs = getAssets().open("subdir_assets/asset_test.txt"); readTextFile(assetIs,"asset file"); }catch(IOException e){ Log.e(tag,"assetsIs IOException msg=" + e.getMessage()); }
//files String filesFileName = "files_test.txt"; try{ FileOutputStream filesOs = openFileOutput(filesFileName, Context.MODE_PRIVATE); writeTextFile(filesOs,"files output file","files"); }catch(FileNotFoundException e){ Log.e(tag,"FileNotFoundException msg=" + e.getMessage()); } try{ InputStream filesIs = openFileInput(filesFileName); readTextFile(filesIs,"files input file"); }catch(FileNotFoundException e){ Log.e(tag,"FileNotFoundException msg=" + e.getMessage()); }
// SDカード // 最上位のディレクトリを取得 File topDir = Environment.getExternalStorageDirectory(); // 一つしたのディレクトリを作る。 String strDir1Path = topDir.getAbsolutePath() + "/dir1"; File dir1 = new File(strDir1Path); dir1.mkdir(); // ファイルの作成 String strFile1 = dir1.getAbsolutePath() + "/file1.txt"; File file1 = new File(strFile1); FileOutputStream sdOs = null; try{ file1.createNewFile(); // IOException }catch(IOException e){ Log.e(tag,"IOException msg=" + e.getMessage()); } // ファイルへの出力 try{ sdOs = new FileOutputStream(file1);// FileNotFoundException }catch(FileNotFoundException e){ Log.e(tag,"FileNotFoundException msg=" + e.getMessage()); } writeTextFile(sdOs,"SD output file","SD"); // ファイルからの入力 try{ InputStream sdIs = new FileInputStream(file1); readTextFile(sdIs,"SD input file"); }catch(FileNotFoundException e){ Log.e(tag,"FileNotFoundException msg=" + e.getMessage()); } }
private void readTextFile(InputStream is,String title){ Log.d(tag,"*** readTextFile: " + title + " ***"); try{ Reader reader = new InputStreamReader(is,"UTF-8"); // UnsupportedEncodingException BufferedReader br = new BufferedReader(reader); String line; int k=0; while((line=br.readLine())!=null){ // IOException k++; Log.d(tag,k + ":" + line); } }catch(UnsupportedEncodingException e){ Log.e(tag,"UnsupportedEncodingException msg=" + e.getMessage()); }catch(IOException e){ Log.e(tag,"IOException msg=" + e.getMessage()); }finally{ if(is!=null){ try{ is.close(); }catch(Exception e){ } } } }
private void writeTextFile(OutputStream os,String title, String prefix){ Log.d(tag,"*** writeTextFile: " + title + " ***"); String[] lines ={ "file test 1" ,"file test 2 漢字 かな カナ" ,"file test 3 end" }; BufferedWriter bw=null; try{ Writer writer = new OutputStreamWriter(os); // UnsupportedEncodingException bw = new BufferedWriter(writer); Log.d(tag,"writeTextFile number of written lines=" + lines.length); int i; for(i=0;i<lines.length;i++){ bw.write(prefix + " " + lines[i]); // IOException bw.newLine(); } }catch(UnsupportedEncodingException e){ Log.e(tag,"UnsupportedEncodingException msg=" + e.getMessage()); }catch(IOException e){ Log.e(tag,"IOException msg=" + e.getMessage()); }finally{ if(bw!=null){ try{ bw.close(); }catch(Exception e){ } } } }
private void parseXml(){ XmlPullParser xpp = getResources().getXml(R.xml.stations); int t; boolean dataF=false; String station; try{ while((t=xpp.getEventType())!=XmlPullParser.END_DOCUMENT){ if(t==XmlPullParser.START_TAG){ if(xpp.getName().equals("station")){ dataF=true; }else{ dataF=false; } }else{ if(dataF && t==XmlPullParser.TEXT){ station = xpp.getText(); Log.d(tag,"station=" + station); } dataF=false; } xpp.next(); } }catch(XmlPullParserException e){ Log.e(tag,"ERROR during parsing, msg=" + e.getMessage()); }catch(IOException e){ Log.e(tag,"ERROR during parsing, msg=" + e.getMessage()); } } } |
raw_test.txt
raw file test 1 raw file test 2 漢字 かな カナ raw file test 3 end |
stations.xml
<?xml version="1.0" encoding="utf-8"?> <stations> <station>Tokyo</station> <station>Shinagawa</station> <station>ShinYokohama</station> <station>Odawara</station> <station>Atami</station> <station>Mishima</station> <station>ShinFuji</station> <station>Shizuoka</station> <station>Kakegawa</station> <station>Hamamatsu</station> <station>Toyohashi</station> <station>MikawaAnjyo</station> <station>Nagoya</station> <station>GifuHashiba</station> <station>Maebara</station> <station>Kyouto</station> <station>ShinOsaka</station> </stations> |
asset_test.txt
asset file test 1 asset file test 2 漢字 かな カナ asset file test 3 end |
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest ... > <application ... > ... </application> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> </manifest> |
以上