Java提供了一套完整的IO流體系,包括FileInputStream惕医、FileOutPutStream等耕漱,通過(guò)這些IO流可以非常方便的訪問(wèn)磁盤上的文件內(nèi)容。Android同樣支持以這種方式來(lái)訪問(wèn)手機(jī)存儲(chǔ)器上的文件抬伺。
一.存儲(chǔ)在內(nèi)部還是外部螟够?
AndroidManifest.xml中manifest標(biāo)簽下有一個(gè)屬性android:installLocation
,用于指定應(yīng)用程序安裝在什么地方沛简,該屬性有三個(gè)可選值:
- auto:程序可能被安裝在外部存儲(chǔ)器上齐鲤,例如SD卡;但是默認(rèn)會(huì)被安裝到手機(jī)內(nèi)存中椒楣。當(dāng)手機(jī)內(nèi)存為空時(shí),程序?qū)⒈话惭b到外部存儲(chǔ)器上牡肉;當(dāng)程序安裝到手機(jī)上后捧灰,用戶可以決定把程序放在外部存儲(chǔ)器還是內(nèi)存中。
- internalOnly:默認(rèn)值统锤,程序只能被安裝在內(nèi)存中毛俏,如果內(nèi)存為空,程序則不能成功被安裝饲窿。
- preferExternal:將程序安裝在外部存儲(chǔ)器煌寇,但是系統(tǒng)不保證程序一定會(huì)被安裝到外部存儲(chǔ)器上。當(dāng)外部存儲(chǔ)器不可以安裝或?yàn)榭諘r(shí)逾雄,程序?qū)⒈话惭b到內(nèi)存中阀溶。當(dāng)程序使用了forward-locking機(jī)制時(shí)也將被安裝到內(nèi)存中腻脏,因?yàn)橥獠看鎯?chǔ)不支持此機(jī)制。程序安裝后银锻,用戶可以自由切換程序應(yīng)該在外部還是內(nèi)部存儲(chǔ)器上永品。
獲取External存儲(chǔ)的權(quán)限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
二.openFileInput和openFileOutput
Context提供了兩個(gè)方法打開(kāi)應(yīng)用程序的數(shù)據(jù)文件夾里的文件IO流:
- FileInputStream openFileInput(String name):打開(kāi)應(yīng)用程序的數(shù)據(jù)文件夾下的name文件對(duì)應(yīng)的輸入流。
- FileOutputStream openFileOutput(String name, int mode):打開(kāi)應(yīng)用程序的數(shù)據(jù)文件夾下的name文件對(duì)應(yīng)的輸出流击纬。第二個(gè)參數(shù)指定打開(kāi)文件的模式鼎姐,該模式支持如下值:
- MODE_PRIVATE:該文件只能被當(dāng)前程序讀寫。
- MODE_APPEND:以追加方式打開(kāi)該文件更振,應(yīng)用程序可以向該文件中追加內(nèi)容炕桨。
- MODE_WORLD_READABLE:該文件的內(nèi)容可以被其他程序讀取。
- MODE_WORLD_WRITEABLE:該文件的內(nèi)容可以由其他程序讀寫肯腕。
Context還提供了訪問(wèn)應(yīng)用程序的數(shù)據(jù)文件夾的方法:
- getDir(String name, int mode):在應(yīng)用程序的數(shù)據(jù)文件夾下獲取或創(chuàng)建name對(duì)應(yīng)的子目錄谋作。
- File getFileDir():獲取應(yīng)用程序的數(shù)據(jù)文件夾的絕對(duì)路徑。
- String[] fileList():返回應(yīng)用程序的數(shù)據(jù)文件夾下的全部文件乎芳。
- deleteFile(String):刪除應(yīng)用程序的數(shù)據(jù)文件夾下的指定文件遵蚜。
三.讀寫SD卡上的文件
為了更好的存取應(yīng)用程序的大文件數(shù)據(jù),應(yīng)用程序需要讀寫SD卡上的文件奈惑。
讀寫SD卡上文件的步驟:
- 調(diào)用Environment的getExternalStorageState()方法判斷手機(jī)上是否插入了SD卡吭净,并且應(yīng)用程序具有讀寫SD卡的權(quán)限。使用如下代碼:
//如果返回true肴甸,說(shuō)明已插入SD卡寂殉,且應(yīng)用程序具有讀寫SD卡的能力
Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
- 調(diào)用Environment的getExternalStorageDirectory()方法來(lái)獲取外部存儲(chǔ)器,也就是SD卡的目錄原在。
- 使用FileInputStream友扰、FileOutputStream、FileReader或FileWriter讀寫SD卡里的文件庶柿。
為了讀寫SD卡上的數(shù)據(jù)村怪,必須在AndroidManifest.xml中添加讀寫SD卡的權(quán)限:
<!-- 在SD卡中創(chuàng)建于刪除文件權(quán)限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 向SD卡中寫入數(shù)據(jù)權(quán)限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
四.操作assets、raw浮庐、res目錄下文件
1.assets
資源文件夾甚负,在main下與res同級(jí),與res不同的是审残,該目錄下的資源文件在打包apk時(shí)梭域,會(huì)按原格式一并被打包。
有三種使用方法:
- 在assets下放一個(gè)test.html文件搅轿,加載該文件:
webView.loadUrl("file:///android_asset/test.html");//假設(shè)已經(jīng)創(chuàng)建了一個(gè)WebView實(shí)例```
- 同樣是讀取test.html文件:
//這里的open只能打開(kāi)文件病涨,不能打開(kāi)文件夾
InputStream inputStream = getResource().getAssets().open("test.html");
- 讀取列表、讀取圖片璧坟、讀音樂(lè)既穆,assets目錄下包含一個(gè)images目錄和一個(gè)mp3文件xuwei.mp3赎懦,images目錄中包含一張圖片dog.jpg:
String[] fileNames = getAssets().list("images/");//讀列表
InputStream inputStream = getAssets().open("images/dog.jpg");//讀圖片
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
imageView.setImageBitmap(bitmap);
AssetFileDescriptor assetFileDescriptor = getAssets().openFd("xuwei.mp3");//得到asset文件描述符
player.reset();//假設(shè)已創(chuàng)建一個(gè)MediaPlayer實(shí)例
player.setDataResource(assetFileDescriptor.getFileDescriptor(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength());
player.prepare();
player.start();
### 2.raw
資源文件夾,在res目錄下循衰,系統(tǒng)會(huì)為res目錄下的所有資源生成相應(yīng)的資源ID铲敛,raw中的文件也不例外,所以可以通過(guò)ID去訪問(wèn)res/raw目錄中的任何文件会钝,而assets目錄中的文件就需要借助AssetManager去訪問(wèn)了伐蒋。
assets目錄允許下面有多級(jí)子目錄,而res/raw下不允許存在目錄結(jié)構(gòu)迁酸。
讀raw下的xuwei.mp3文件:
InputStream is = getResources().openRawResource(R.raw.xuwei);
### 3.res
res目錄下的文件都可用getResources()方法讀取先鱼。
# 五.SD卡文件瀏覽器
***
利用Java的File類開(kāi)發(fā)一個(gè)SD卡文件瀏覽器,通過(guò)Environment.getExternalStorageDirectory()訪問(wèn)系統(tǒng)的SD卡目錄奸鬓,然后通過(guò)File的listFiles()方法獲取指定目錄下的全部文件和文件夾焙畔。
布局文件如下:
activity_main.xml
```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"
tools:context="com.trampcr.sdfileexplorer.MainActivity">
<TextView
android:id="@+id/path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:gravity="center_horizontal" />
<ListView
android:id="@+id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/path" />
<Button
android:id="@+id/parent"
android:layout_width="38dp"
android:layout_height="34dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="@drawable/home" />
</RelativeLayout>
布局文件包含一個(gè)TextView用于顯示當(dāng)前路徑,ListView顯示當(dāng)前目錄下文件和文件夾串远,Button用于返回上一級(jí)目錄宏多。
ListView中的子布局,包含一個(gè)ImageView和一個(gè)TextView:
line.xml:
<?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="50dp"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/icon"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/folder"/>
<TextView
android:id="@+id/file_name"
android:layout_marginLeft="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="abc"/>
</LinearLayout>
主程序代碼如下:
public class MainActivity extends AppCompatActivity {
private ListView mListView;
private TextView mTextView;
//記錄當(dāng)前的父文件夾
private File mCurrentParent;
//記錄當(dāng)前路徑下的所有文件的文件數(shù)組
File[] mCurrentFiles;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.list);
mTextView = (TextView) findViewById(R.id.path);
//獲取系統(tǒng)的SD卡的目錄
File root = new File(String.valueOf(Environment.getExternalStorageDirectory()));
//如果SD卡存在
if (root.exists()){
mCurrentParent = root;
mCurrentFiles = root.listFiles();
//使用當(dāng)前目錄下的全部文件澡罚、文件夾來(lái)填充ListView
inflateListView(mCurrentFiles);
}
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (mCurrentFiles[position].isFile()){
return;
}
File[] tmp = mCurrentFiles[position].listFiles();
if (tmp == null || tmp.length == 0){
Toast.makeText(MainActivity.this, "當(dāng)前路徑不可訪問(wèn)或該路徑下沒(méi)有文件", Toast.LENGTH_SHORT).show();
}else {
mCurrentParent = mCurrentFiles[position];
mCurrentFiles = tmp;
inflateListView(mCurrentFiles);
}
}
});
//獲取上一級(jí)目錄的按鈕
Button parent = (Button) findViewById(R.id.parent);
parent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
if (!mCurrentParent.getCanonicalFile().equals("/mnt/shell/emulated/0")){
mCurrentParent = mCurrentParent.getParentFile();
mCurrentFiles = mCurrentParent.listFiles();
inflateListView(mCurrentFiles);
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
private void inflateListView(File[] files) {
//創(chuàng)建一個(gè)List集合伸但,List集合的元素是Map
List<Map<String, Object>> listItems = new ArrayList<>();
for (int i = 0; i < files.length; i++) {
Map<String, Object> listItem = new HashMap<>();
if (files[i].isDirectory()){
listItem.put("icon", R.drawable.folder);
}else {
listItem.put("icon", R.drawable.file);
}
listItem.put("fileName", files[i].getName());
listItems.add(listItem);
}
//創(chuàng)建一個(gè)SimpleAdapter
SimpleAdapter simpleAdapter = new SimpleAdapter(this, listItems, R.layout.line, new String[]{"icon", "fileName"}, new int[]{R.id.icon, R.id.file_name});
mListView.setAdapter(simpleAdapter);
try {
mTextView.setText("當(dāng)前路徑為:" + mCurrentParent.getCanonicalPath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用File[]數(shù)組填充ListView,填充是程序會(huì)根據(jù)File[]數(shù)組里的數(shù)據(jù)元素代表的是文件還是文件夾來(lái)選擇使用文件圖標(biāo)或文件夾圖標(biāo)留搔。
運(yùn)行上面程序更胖,可以看到:
六.擴(kuò)展學(xué)習(xí)
- android-sdk中的samples
- github/google samples
- developers