Android網(wǎng)絡(luò)與數(shù)據(jù)存儲(chǔ)——File存儲(chǔ)(實(shí)現(xiàn)SD卡文件瀏覽器)

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卡上文件的步驟:

  1. 調(diào)用Environment的getExternalStorageState()方法判斷手機(jī)上是否插入了SD卡吭净,并且應(yīng)用程序具有讀寫SD卡的權(quán)限。使用如下代碼:
//如果返回true肴甸,說(shuō)明已插入SD卡寂殉,且應(yīng)用程序具有讀寫SD卡的能力
Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) 
  1. 調(diào)用Environment的getExternalStorageDirectory()方法來(lái)獲取外部存儲(chǔ)器,也就是SD卡的目錄原在。
  2. 使用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)行上面程序更胖,可以看到:

SDExplorer.png

六.擴(kuò)展學(xué)習(xí)


  • android-sdk中的samples
  • github/google samples
  • developers
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市隔显,隨后出現(xiàn)的幾起案子却妨,更是在濱河造成了極大的恐慌,老刑警劉巖括眠,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彪标,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡哺窄,警方通過(guò)查閱死者的電腦和手機(jī)捐下,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)萌业,“玉大人,你說(shuō)我怎么就攤上這事奸柬∩辏” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵廓奕,是天一觀的道長(zhǎng)抱婉。 經(jīng)常有香客問(wèn)我档叔,道長(zhǎng),這世上最難降的妖魔是什么蒸绩? 我笑而不...
    開(kāi)封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任衙四,我火速辦了婚禮,結(jié)果婚禮上患亿,老公的妹妹穿的比我還像新娘传蹈。我一直安慰自己,他們只是感情好步藕,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布惦界。 她就那樣靜靜地躺著,像睡著了一般咙冗。 火紅的嫁衣襯著肌膚如雪沾歪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天雾消,我揣著相機(jī)與錄音灾搏,去河邊找鬼。 笑死立润,一個(gè)胖子當(dāng)著我的面吹牛狂窑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播范删,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蕾域,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了到旦?” 一聲冷哼從身側(cè)響起旨巷,我...
    開(kāi)封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎添忘,沒(méi)想到半個(gè)月后采呐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搁骑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年斧吐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仲器。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡煤率,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出乏冀,到底是詐尸還是另有隱情蝶糯,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布辆沦,位于F島的核電站昼捍,受9級(jí)特大地震影響识虚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜妒茬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一担锤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乍钻,春花似錦肛循、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至欢摄,卻和暖如春熬丧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怀挠。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工析蝴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绿淋。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓闷畸,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親吞滞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子佑菩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容