Android中數(shù)據(jù)存儲與文件存儲

前言

Android提供了多種選項來保存永久性應(yīng)用數(shù)據(jù)欣硼。用戶可以選擇的數(shù)據(jù)存儲選項有:共享首選項骑冗、內(nèi)部存儲秉宿、外部存儲疙剑、SQLite數(shù)據(jù)庫氯迂、網(wǎng)絡(luò)存儲。

文章開始前用一張圖表示共享首選項,內(nèi)部存儲,外部存儲

微信截圖_20190305114614.png

一言缤、使用共享首選項

1嚼蚀、關(guān)鍵類:SharedPreferences類

可以使用ShatedPreferences來保存任何原始數(shù)據(jù):布爾值、浮點(diǎn)值管挟、整型值轿曙、長整型、字符串。
這些數(shù)據(jù)將會跨多個用戶會話永久保留导帝。

2守谓、介紹如何獲得SharedPreferences對象

2.1 、(1)getSharedPreferences(String name,int mode);

通過Context調(diào)用舟扎,參數(shù)一:首選項文件名分飞,如果此名稱的首選項文件不存在,那么會在再檢索編輯器SharedPreferences.edit()并提交更改(Editor.commit())時創(chuàng)建這個文件睹限。
參數(shù)二默認(rèn)填寫0或者是MODE_PRIVATE(創(chuàng)建的文件只能由調(diào)用應(yīng)用程序訪問)譬猫,別的模式MODE_WORLD_READABLE(創(chuàng)建全都可讀文件,容易造成安全漏洞羡疗,已在17棄用)與MODE_WORLD_WRITEABLE(創(chuàng)建全部課讀寫文件染服,容易造成安全漏洞,已在17棄用)與MODE_MULTI_PROCESS(在某些版本的Android中無法可靠運(yùn)行叨恨,不提供任何機(jī)制來協(xié)調(diào)跨進(jìn)程的并發(fā)修改柳刮,已在23棄用)

2.2 、getPreferences(int mode);

在Activity中調(diào)用痒钝,參數(shù)參照上面秉颗,一般用0就可以。
調(diào)用SharedPreferences.edit()方法并最后提交更改(Editor.commit())時候創(chuàng)建文件送矩,文件名就是以當(dāng)前Activity的類名蚕甥,比較簡單的基于當(dāng)前Activity創(chuàng)建其相應(yīng)的SharedPreferences文件。

3栋荸、詳細(xì)介紹

其應(yīng)該是一個interface,放在android.content.SharedPreferences包下菇怀。
其嵌套了接口Editor與接口 OnShatedPreferenceChangeListener。
通過SharedPreferences對象可以調(diào)用如下方法:
3.1 ->>>

contains(String key)
返回值:boolean
檢驗當(dāng)前SharedPreferences文件中是否包含某個key 

3.2->>>

getAll()
返回值:Map<String,?>
返回當(dāng)前SharedPreferences文件中所有的key以及相應(yīng)的value值

示例:

        Map<String,?> map = mSharedPreferences.getAll();
        這樣此SharedPreferences中所有的值都在Map集合中

3.3->>>

getBoolean(String key,boolean defValue)
返回值:boolean
得到此SharedPreferences中boolean類型的Key對應(yīng)的value晌块,其中參數(shù)二表示默認(rèn)值爱沟。
需要說明的是,如果此key 對應(yīng)的值不是布爾值匆背,那么就會拋出ClassCastException異常呼伸,如果不處理,程序會崩潰靠汁,所以在使用查詢的時候:

示例

 try {
            int i = mSharedPreferences.getInt("age", 1);
            mTextViewa.setText("" + i);
        } catch (ClassCastException e) {
             mTextViewa.setText("出現(xiàn)異常了");
        }

3.4->>>

getFloat(String key,float defValue)
返回值:float
與getBoolean方法一樣蜂大,不同的是得到的是float值,針對的是float類型的數(shù)據(jù)查詢蝶怔,如果傳入的Key對應(yīng)的值不是vlaue,那么也會拋出ClassCastException異常兄墅。

3.5->>>

getInt(String key,int defValue)
返回值:int
與getBoolean方法一樣踢星,不同的是得到的是int值,如果傳入的key查詢出來不是int類型的隙咸,那么也會拋出ClassCastException異常沐悦。

3.6->>>

getLong(String key ,long defValue)
返回值:long
與getBoolean方法一樣成洗,不同的是得到的是long值,如果傳入的key查詢出來不是int類型的藏否,那么也會拋出ClassCastException異常瓶殃。

3.7->>>

getString(String key,String defValue)
與getBoolean方法一樣,不同的是得到的是String值副签,如果傳入的key查詢出來不是int類型的遥椿,那么也會拋出ClassCastException異常,defVlue也可以是null

3.8->>>

getStringSet(String key,Set<String> defValues)
返回值:Set<String>
defValues可以是null,也會拋出ClassCastException,另外,存入的是Set<String>,也就是一組String數(shù)據(jù)淆储。

3.9->>>

registerOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener)
警告:首選項管理器當(dāng)前不存儲對偵聽器的強(qiáng)引用冠场。 您必須存儲對偵聽器的強(qiáng)引用,否則它將容易被垃圾收集本砰。 只要您需要偵聽器
碴裙,我們建議您在對象的實(shí)例數(shù)據(jù)中保留對偵聽器的引用。
此監(jiān)聽發(fā)生在此SharedPreferences文件內(nèi)容發(fā)生更改的時候点额。

3.10->>>

unregisterOnSharedPreferencesChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener)
取消監(jiān)聽

3.11->>>

edit()
返回值:SharedPreferences.Editor
具體作用參照下面Editor中方法具體介紹舔株。

4介紹SharedPreferences中的Editor中方法

操作當(dāng)前SharedPreferences中文件中的值,只有通過commit或者apply之后还棱,才會建立相應(yīng)的SharedPreferences文件與完成相應(yīng)的修改载慈。
4.1 ->>>

putBoolean(String key,boolean value)
返回值:SharedPreferences.Editor
寫入到當(dāng)前SharedPreferences文件中一對key-value或者是修改已經(jīng)存在key對應(yīng)的value的值。

4.2 ->>>

putFloat(String key,float value)
返回值:SharedPreferences.Editor
寫入到當(dāng)前SharedPreferences文件中一對key-value或者是修改已經(jīng)存在key對應(yīng)的value的值诱贿。

4.3 ->>>

putInt(String key,int value)
返回值:SharedPreferences.Editor
寫入到當(dāng)前SharedPreferences文件中一對key-value或者是修改已經(jīng)存在key對應(yīng)的value的值娃肿。

4.4 ->>>

putLong(String key,long value)
返回值:SharedPreferences.Editor
寫入到當(dāng)前SharedPreferences文件中一對key-value或者是修改已經(jīng)存在key對應(yīng)的value的值。

4.5 ->>>

putString(String key,String value)
返回值:SharedPreferences.Editor
寫入到當(dāng)前SharedPreferences文件中一對key-value或者是修改已經(jīng)存在key對應(yīng)的value的值珠十。

4.6 ->>>

putStringSet(String key,Set<String> values)
返回值:SharedPreferences.Editor
寫入到當(dāng)前SharedPreferences文件中一對key-value或者是修改已經(jīng)存在key對應(yīng)的value的值料扰。
對此參數(shù)傳遞null等價于使用此鍵調(diào)用remove(String)

4.7 ->>>

remove(String key)
返回值:SharedPreferences.Editor
移除當(dāng)前SharedPreferences中某個Key以及其所代表的值

4.8 ->>>

clear()
返回值:SharedPreferences.Editor
移除所有的key以及value從當(dāng)前SharedPreferences文件中。

4.9 ->>>

commit()
返回值:boolean
如果新值成功寫入當(dāng)前SharedPreferences焙蹭,則返回true;
會自動執(zhí)行請求的參數(shù)晒杈,替換SharedPreferences中的修改,如果沒有的話孔厉,就會創(chuàng)建此文件
當(dāng)兩個編輯器同時修改時拯钻,最后一個調(diào)用commit的成功
如果你不關(guān)心返回值,并且你在應(yīng)用程序的主線程中使用它撰豺,請使用apply();

4.10 ->>>

apply()
返回值:void
1粪般、如果先后apply()了幾次,那么會以最后一次apply()的為準(zhǔn)污桦。 
2亩歹、commit()是把內(nèi)容同步提交到硬盤的。而apply()先立即把修改提交到內(nèi)存,然后開啟一個異步的線程提交到硬盤小作,
并且如果提交失敗亭姥,你不會收到任何通知。 
也就是說顾稀,commit有返回值达罗,apply沒有返回值,commit寫入文件操作是在主線程中静秆,會消耗資源粮揉,apply寫入文件的操作是異步的,
會把Runnable放到線程池中執(zhí)行
3诡宗、如果當(dāng)一個apply()的異步提交還在進(jìn)行的時候滔蝉,執(zhí)行commit()操作,那么commit()是會阻塞的塔沃。
而如果commit()的時候蝠引,前面的commit()還未結(jié)束,這個commit()還是會阻塞的蛀柴。(所以引起commit阻塞會有這兩種原因) 
4螃概、由于SharePreferences在一個程序中的實(shí)例一般都是單例的,所以如果你不是很在意返回值的話鸽疾,你使用apply()代替commit()是無所謂的吊洼。

5、SP例子

(1)初始一個應(yīng)用在data/data/應(yīng)用包名下:


初始未進(jìn)行操作時候應(yīng)用的樣子.png

當(dāng)你使用了SharedPreferences時候

public static final String FILE_NAME = "MySharedPreferences";
 mSharedPreferences = getSharedPreferences(FILE_NAME, 0);
 //執(zhí)行完上述話制肮,會在應(yīng)用程序中建立shared_prefs文件夾冒窍,里面將要存放sharedPreferences文件,但是目前沒有內(nèi)容

(2)當(dāng)你完成一些查詢操作豺鼻,但是沒有完成使用Editor

mSharedPreferences = getSharedPreferences(FILE_NAME, 0);  
String name = mSharedPreferences.getString("name","xlj");
 //也沒有在shared_prefs文件夾下建立相應(yīng)的FILE_NAME文件综液,另外這樣用也不會報錯,返回name = xlj

(3)通過操作Editor

mSharedPreferences = getSharedPreferences(FILE_NAME, 0);  
SharedPreferences.Editor editor = mSharedPreferences.edit();
        editor.putString("name", "xljs");
        editor.commit();
建立相應(yīng)的SharedPreferences.png

(1)演示第二種得到SharedPerferences方法:

       mSharedPreferences = getPreferences(0);
        SharedPreferences.Editor editor = mSharedPreferences.edit();
        editor.putString("age", "10");
        editor.putBoolean("is", true);
        editor.commit();

        Map<String,?> map = mSharedPreferences.getAll();

        mTextViewa.setText(map.toString());
        try {
            int i = mSharedPreferences.getInt("age", 1);
            mTextViewa.setText("" + i);
        } catch (ClassCastException e) {
             mTextViewa.setText("出現(xiàn)異常了");
        }

(2)當(dāng)前Activity名字:SharedPActivity

使用第二種方法.png

在手機(jī)上使用應(yīng)用的內(nèi)部存儲

直接在設(shè)備的內(nèi)部存儲中保存文件儒飒。默認(rèn)情況下谬莹,保存到內(nèi)部存儲的文件是應(yīng)用的私有文件,其他應(yīng)用(和用戶)都不能訪問這些文件桩了。當(dāng)用戶卸載應(yīng)用的時候附帽,這些文件也會被移除。并且這些文件理論上是不可見的

簡單來講,就是給我們自己的應(yīng)用,在內(nèi)部存儲分配的空間,默認(rèn)的有files文件夾井誉、cache文件夾蕉扮、
(其實(shí)共享首選項也是提供的默認(rèn)的shared_prefs文件夾)
當(dāng)然了,用戶也可以不用這些已經(jīng)提供的files 與 cache文件夾,自己建立自己的的文件夾,并放文件.

一、讀寫files文件夾(根文件夾)下文件

1颗圣、openFileOutput()方法

屬于android.content.Context;
返回值:FileOutputStram一個文件輸出流
參數(shù)一:name,你要打開的文件的名字慢显,這里的name只能是名字爪模,不能使用"/name"此類的欠啤。
參數(shù)二:MODE_PRIVATE荚藻,別的模式處于安全都摒棄了
不同于常見getxxx方法,字面翻譯就是:打開files文件夾下某個文件的輸出流
簡單的方式得到一個文件輸出流

1.1例子

初始默認(rèn)的應(yīng)用程序內(nèi)部存儲如下圖:


初始.png

shared_prefs文件夾是因為操作過SharedPreferences才有的,默認(rèn)的初始就有一個空的cache文件夾洁段。

                  private String FILE_NAME = "hello_file";
                  private String FILE_NAME_NEW = "test.txt";

                  String string = "怎么都是寫不會的啊";
                try {
                    FileOutputStream fileOutputStream = openFileOutput(FILE_NAME, Context.MODE_PRIVATE);

                    fileOutputStream.write(string.getBytes());

                    fileOutputStream.close();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                    mTextView2.setText("出現(xiàn)文件找不到異常");
                } catch (IOException e) {
                    e.printStackTrace();
                    mTextView2.setText("出現(xiàn)了IO異常");
                }

結(jié)果如圖:


結(jié)果

出現(xiàn)了files文件夾以及相應(yīng)的文件应狱。

           try {
                    FileOutputStream fileOutputStream = openFileOutput(FILE_NAME_NEW, Context.MODE_PRIVATE);

                    fileOutputStream.write(string.getBytes());

                    fileOutputStream.close();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                    mTextView2.setText("出現(xiàn)文件找不到異常");
                } catch (IOException e) {
                    e.printStackTrace();
                    mTextView2.setText("出現(xiàn)了IO異常");
                }

結(jié)果:


結(jié)果

如果給文件名字帶上具體的文件格式,也沒有什么影響祠丝。

說明:
自Api17以來疾呻,常量MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE已經(jīng)被棄用。
從Android N開始写半,使用這些常量會引發(fā)securityException岸蜗。
這意味著,面向Android N 和更高版本的應(yīng)用無法按名稱共享私有文件叠蝇,嘗試共享file://URL將會導(dǎo)致引發(fā)FileUriExposedException璃岳。
如果應(yīng)用需要與其他應(yīng)用共享私有文件,則可以將FileProvider與FLAG_GRANT_READ_URI_PERMISSION配合使用悔捶。
2铃慷、openFileInput

屬于android.content.Context;
參數(shù)一:傳入你想讀取的文件的名字,這里的name只能是名字蜕该,不能使用"/name"此類的犁柜。
返回值:返回一個FileInputStream。
不用于常見的getxxx方法,字面翻譯:打開文件輸入流
方便直接得到一個流

2.1例子
             try {
                    FileInputStream fileInputStream = openFileInput(FILE_NAME);

                    byte[] buf = new byte[1024];

                    int hasRead = 0;

                    StringBuffer sb  = new StringBuffer("");

                    //讀取文件部分
                    while((hasRead = fileInputStream.read(buf))>0){
                        sb.append(new String(buf,0,hasRead));
                    }

                    fileInputStream.close();

                    mTextView.setText(sb.toString());

                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                    mTextView.setText("出現(xiàn)文件找不到異常了");
                } catch (IOException e) {
                    e.printStackTrace();
                    mTextView.setText("出現(xiàn)了IO異常了");
                }
            }
GIF.gif

通過動圖可以看出來堂淡,是可以的讀取出來數(shù)據(jù)馋缅。
另外需要注意的是,如果你開始讀的文件不存在就會報FileNotFoundException異常绢淀,當(dāng)時會建立files文件夾萤悴,如果此文件夾不存在的話。

3更啄、getFilesDir()

也是在android.content.context中提供的屬性
執(zhí)行此方法的到的是:
如果不想用openFileInput與openFileOutput兩個方法,想使用get系列的方法也是可以的.
直接得到的是文件的路徑,而不是得到一個流,這樣可以自己用.

/data/user/0/example.xlj.savecundemo/files
應(yīng)用程序內(nèi)部存儲空間中files的目錄
獲取在其中存儲內(nèi)部文件的文件系統(tǒng)目錄的絕對路徑稚疹。
例子

這只是一種簡單的例子,延時了某一個用法

               File file = new File(getFilesDir(),"infos.txt");//返回值是File對象

               try {
                   FileOutputStream outputStream =  new FileOutputStream(file);

                   String s = "這是一個測試的例子祭务,往info里面寫入數(shù)據(jù)";
                   outputStream.write(s.getBytes());

                   outputStream.close();

                   mTextView4.setText("文件寫入成功");
               } catch (FileNotFoundException e) {
                   e.printStackTrace();
                   mTextView4.setText("文件未找到異常");
               } catch (IOException e) {
                   e.printStackTrace();
                   mTextView4.setText("出現(xiàn)了IO異常");
               }

結(jié)果如圖:


結(jié)果

也是在files文件夾下創(chuàng)建了infos.txt文件内狗。完成了存儲

3其他的常用方法,針對于自己應(yīng)用內(nèi)部存儲中 files (強(qiáng)調(diào)的是files)文件夾下的操作
3.1fileList()

返回保存在內(nèi)部存儲空間一系列文件。

      String [] list = fileList();
        for (int i = 0; i < list.length; i++) {
            Log.d("xljxlj",list[i]);
        }

得到的結(jié)果如下:
         05-05 03:43:48.829 7396-7396/example.xlj.savecundemo D/xljxlj: hello_file
         05-05 03:43:48.829 7396-7396/example.xlj.savecundemo D/xljxlj: test.txt
         05-05 03:43:48.829 7396-7396/example.xlj.savecundemo D/xljxlj: infos.txt
3.2deleteFile(String name)

刪除保存在內(nèi)部存儲的文件义锥,其返回值是一個Boolean,表示是否刪除成功柳沙。
這里的name只能是名字,不能使用"/name"此類的拌倍。

說明

如果在一讀一寫的操作中赂鲤,出現(xiàn)了亂碼噪径,那么上面的代碼需要做如下調(diào)整
寫入的時候

 try {
                    FileOutputStream fileOutputStream = GudleActivity.this.openFileOutput(ApiTools.MAIN_INFO_FILES, Context.MODE_PRIVATE);
                    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream,"UTF-8");
                    outputStreamWriter.write(response.toString());
                    outputStreamWriter.flush();
                    outputStreamWriter.close();
                } catch (IOException e) {
                    //出現(xiàn)異常緩存失敗
                    e.printStackTrace();
                }

讀取的時候

try {
            FileInputStream fileInputStream = openFileInput(ApiTools.MAIN_INFO_FILES);
            InputStreamReader reader = new InputStreamReader(fileInputStream,"UTF-8"); //最后的"GBK"根據(jù)文件屬性而定,如果不行数初,改成"UTF-8"試試
            BufferedReader br = new BufferedReader(reader);
            String line;
            while ((line = br.readLine()) != null) {
                LogUtils.debugInfo("maininfo","得到數(shù)據(jù):"+line);
            }
            br.close();
            reader.close();

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

二找爱、讀寫cache文件夾中文件

如果您想要緩存一些數(shù)據(jù),而不是永久的存儲這些數(shù)據(jù)泡孩,可以考慮Cache文件夾下存儲车摄。
當(dāng)設(shè)備的內(nèi)部存儲空間不足的時候,Android可能會刪除這些緩存文件以回收空間仑鸥。但您不應(yīng)該依賴系統(tǒng)來為你清理這些文件吮播,而應(yīng)該是中自行維護(hù)緩存文件,使其占用的控件保持在合理的限制范圍內(nèi)例如1MB眼俊,當(dāng)用戶卸載您的應(yīng)用的時候意狠,這些文件也會被移除。

1疮胖、通過getCacheDir()
例子
                File  file = new File(getCacheDir(),"infos.txt"); //返回值是File對象
                try {
                    FileInputStream fileInputStream = new FileInputStream(file);

                    byte[] bytes = new byte[1024];

                    int hascode = 0;

                    StringBuffer stringBuffer = new StringBuffer("");

                    while ((hascode = fileInputStream.read(bytes))>0){
                        stringBuffer.append(new String(bytes,0,hascode));
                    }

                    mTextView5.setText(stringBuffer.toString());

                    fileInputStream.close();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                    mTextView5.setText("出現(xiàn)了文件未找到異常");
                } catch (IOException e) {
                    e.printStackTrace();

                    mTextView5.setText("出現(xiàn)了IO異常了");
                }
演示cache.png

忽略info环戈,這是之前做的測試,目前只貼了一個infos.txt的代碼

2获列、讀寫內(nèi)存存儲空間中自定義文件夾中文件

如果你不想放在files與cache文件夾下谷市,可以通過getDir()方法,其返回值是:File
如果執(zhí)行g(shù)etDir("test",MODE_PRIVATE)
這樣在執(zhí)行代碼,會先建立這個文件夾.,不過都會自動添加app_前綴,這個不需要自己去考慮

/data/user/0/example.xlj.savecundemo/app_test
如果存在則返回先關(guān)文件夾的路徑击孩,如果沒有的話就創(chuàng)建并且返回相關(guān)的FIle迫悠。

那么相關(guān)的操作就類似了,

       File file = new File(getDir("test",MODE_PRIVATE),"who.txt");
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            String ss = "自己新建立了一個文件夾";
            fileOutputStream.write(ss.getBytes());
            fileOutputStream.close();
            Toast.makeText(FileActivity.this,"寫入成功",Toast.LENGTH_SHORT).show();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
結(jié)果.png

如圖中app_test就是通過執(zhí)行上述代碼完成的巩梢。這里只演示了文件寫入创泄,對于讀取文件通上述,不在過多代碼演示括蝠。

讀取資源文件(不屬于內(nèi)部存儲內(nèi)容)這部分是引申

如果在編譯時想要保存應(yīng)用中的靜態(tài)文件鞠抑,請在項目的res/raw/目錄中保存該文件〖删可以通過openRawResource()打開該資源文件并傳遞R.raw.xxx資源ID,此方法返回一個InputStream(注意不是FileInputStream)搁拙,您可以使用該流傳輸讀取文件(但是不能寫入到原始文件,只能讀确唷)
也是得到一個資源文件流,注意不是應(yīng)用存儲內(nèi)部存儲中
下面通過例子進(jìn)行演示:
準(zhǔn)備文件:


WTISQN`{LBKB234VF2$812T.png

演示讀取activity.file.xml文件

            try {
                    InputStream fileInputStream =  FileActivity.this.getResources().openRawResource(R.raw.activity_file);

                    byte[] buf = new byte[1024];

                    int hasRead = 0;

                    StringBuffer sb  = new StringBuffer("");

                    //讀取文件部分
                    while((hasRead = fileInputStream.read(buf))>0){
                        sb.append(new String(buf,0,hasRead));
                    }

                    fileInputStream.close();

                    mTextView3.setText(sb.toString());

                } catch (Resources.NotFoundException e) {
                    e.printStackTrace();
                    mTextView3.setText("出現(xiàn)文件找不到異常了");
                } catch (IOException e) {
                    e.printStackTrace();
                    mTextView3.setText("出現(xiàn)了IO異常了");
                }
結(jié)果

演示如何讀取一個圖片

              //演示怎么讀取一張圖片
              InputStream inputStream = getResources().openRawResource(R.raw.my_my_name);


              //方式一
              Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
             mImageView.setImageBitmap(bitmap);

                //方式二
                BitmapDrawable bitmapDrawable = new BitmapDrawable(inputStream);

                Bitmap bitmap = bitmapDrawable.getBitmap();

                mImageView.setImageBitmap(bitmap);

注意:如果出現(xiàn)了亂碼箕速,建議首先檢查源文件,也就是raw目錄下的文件是否是亂碼朋譬。

使用手機(jī)的外部存儲

每個兼容Android的設(shè)備都支持可用于保存文件的共享“外部存儲”盐茎。該存儲可能是可移除的存儲介質(zhì)(如SD卡,次要卷),也可能是不可移除的存儲(主要卷)♂阌現(xiàn)在大部分手機(jī),都自帶不可移除的存儲.
保存到外部存儲的文件,是全局可讀的,用戶可以通過USB鏈接上進(jìn)行修改.

共享,意味著所有的應(yīng)用都可以使用,并不是前面提到的每個應(yīng)用中的內(nèi)部存儲.
簡單來講,外部存儲其實(shí)分為了兩種文件類型,一種是公共的存儲文件夾,一種是給具體應(yīng)用的存儲文件夾(也分為file 與 cache )
區(qū)別:這部分外部存儲的就是getExtralxxx開頭的了,表明是外部存儲.
Ram:手機(jī)的運(yùn)行內(nèi)存字柠。
Rom:手機(jī)內(nèi)存探越,表現(xiàn)為內(nèi)部存儲空間,可以理解為電腦本身的硬盤窑业。
SD:外部存儲空間钦幔,可以理解為電腦的移動硬盤。
其實(shí)外部存儲系統(tǒng)給開發(fā)者使用的地方可以分為公共文件夾與應(yīng)用私有文件夾區(qū)域:
前言圖.png

一般來講就是在Storage/sdcard目錄下数冬,如圖所示节槐,DCIM、Download拐纱、Movies等等都是屬于公共文件夾,這部分文件夾對于當(dāng)前手機(jī)上所有的應(yīng)用都是公開的哥倔。Android/data目錄下秸架,有一個個以應(yīng)用名字命名的文件夾,里面可以存放當(dāng)前應(yīng)用的私有文件與緩存咆蒿。

1东抹、檢查介質(zhì)可用性

在使用外部存儲執(zhí)行任何工作之前,應(yīng)始終調(diào)用Environment.getExternalStorageState()方法以檢查介質(zhì)是否可用沃测,介質(zhì)可能處于缺失缭黔、只讀或者其他狀態(tài)。

/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state) ||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return true;
    }
    return false;
}

關(guān)于state可能值介紹:

MEDIA_UNKNOWN:  未知存儲狀態(tài)蒂破,例如路徑未被已知存儲介質(zhì)支持時馏谨。
MEDIA_REMOVED: 存儲介質(zhì)不存在的存儲狀態(tài)。
MEDIA_UNMOUNTED:  存儲狀態(tài)附迷,如果介質(zhì)存在但未安裝惧互。
MEDIA_CHECKING:  如果介質(zhì)存在并正在進(jìn)行磁盤檢查,則為存儲狀態(tài)喇伯。
MEDIA_NOFS:  存儲狀態(tài)喊儡,如果介質(zhì)存在但空白或正在使用不受支持的文件系統(tǒng)。
MEDIA_MOUNTED:  存儲狀態(tài)稻据,如果介質(zhì)存在并且以讀/寫訪問的方式安裝在其安裝點(diǎn)艾猜。
MEDIA_MOUNTED_READ_ONLY:  存儲狀態(tài),如果介質(zhì)存在并且以只讀訪問權(quán)限掛載到它的掛載點(diǎn)捻悯。
MEDIA_SHARED:  存儲狀態(tài)匆赃,如果介質(zhì)未安裝,并通過USB海量存儲共享秋度。
MEDIA_BAD_REMOVAL:  存儲狀態(tài)炸庞,如果介質(zhì)在卸載之前被刪除。
MEDIA_UNMOUNTABLE:   存儲狀態(tài)荚斯,如果介質(zhì)存在但無法安裝埠居。 通常查牌,如果介質(zhì)上的文件系統(tǒng)損壞,就會發(fā)生這種情況滥壕。
MEDIA_EJECTING:  存儲狀態(tài)纸颜,如果媒體正在被彈出的過程中。

getExternalStorageState()方法將會返回可能需要檢查的其他狀態(tài)绎橘,當(dāng)應(yīng)用需要訪問介質(zhì)時胁孙,可以使用這些狀態(tài)向用戶通知更多信息。
更多的關(guān)于Environment的使用介紹;

2称鳞、檢查權(quán)限
<user-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<user-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

兩點(diǎn)說明:
(1)android 6.0提出了運(yùn)行時權(quán)限涮较,需要開發(fā)過程中注意,可以參考文章
(2)從Android4.4開始冈止,如果應(yīng)用僅僅讀取或者寫入應(yīng)用的私有文件狂票,則不需要這些權(quán)限,只有使用外部存儲的公共文件夾時需要熙暴。

3闺属、在外部存儲公共文件位置存儲文件。

一般而言周霉,應(yīng)該將用戶可通過您的應(yīng)用獲取的新文件保存到設(shè)備上的“公共”位置掂器,以便其他應(yīng)用能夠在其中訪問這些文件,并且用戶也能輕松地從該設(shè)備復(fù)制這些文件俱箱。 執(zhí)行此操作時国瓮,應(yīng)使用共享的公共目錄之一,例如 Music/匠楚、Pictures/ 和 Ringtones/ 等巍膘。。

4芋簿、getExternalStoragePublicDirectory()方法,可與其他應(yīng)用共享

返回值:File
參數(shù):type
type可以有如下幾種選擇峡懈,其表示頂級公共目錄,當(dāng)做是標(biāo)準(zhǔn)目錄与斤,卻沒有強(qiáng)制選擇使用相應(yīng)的:

DIRECTORY_ALARMS:用于放置任何音頻文件的標(biāo)準(zhǔn)目錄肪康,該文件應(yīng)位于用戶可以選擇的鬧鈴文件列表(而不是普通音樂),的特定音頻文件撩穿。
DIRECTORY_DCIM:將設(shè)備安裝為相機(jī)時傳統(tǒng)照片和視頻位置磷支。
DIRECTORY_DOCUMENTS:放置用戶創(chuàng)建的文檔的標(biāo)準(zhǔn)目錄。
DIRECTORY_DOWNLOADS:用于放置用戶下載文件的標(biāo)準(zhǔn)目錄食寡,這是頂級公共目錄的約定雾狈。
DIRECTORY_MOVIES:用戶放置用戶電影的標(biāo)準(zhǔn)目錄,但是媒體掃描器會在任何目錄中查找和收集電影抵皱。
DIRECTORY_MUSIC:用于放置任何音頻文件的標(biāo)準(zhǔn)目錄善榛。
DIRECTORY_NOTIFICATIONS:放置任何音頻文件的標(biāo)準(zhǔn)目錄辩蛋,一般是用戶可以選擇的通知列表(而不是普通音樂)
DIRECTORY_PICIURES:放置可供用戶使用的圖片的標(biāo)準(zhǔn)目錄。但是媒體掃描器將在任何目錄中查找和收集圖片移盆。
DIRECTORY_PODCASTS:放置任何音頻的標(biāo)準(zhǔn)目錄悼院,是可供選擇的播客列表中(而不是普通音樂)。
DIRECTORY_RINGTONES:用于放置任何音頻的標(biāo)準(zhǔn)目錄咒循,該文件可供用戶選擇鈴聲列表据途。

Google官方給出的例子:

void createExternalStoragePublicPicture() {
    //創(chuàng)建了一個公共的放置圖片的路徑。
    // Context.getExternalMediaDir() 叙甸,下面會進(jìn)行解釋
   //上述方法的路徑:/storage/sdcard/Android/media/example.xlj.savecundemo
    File path = Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES);
  //在5.1上測試:/storage/sdcard/Pictures
  //在7.0上測試:/storage/emulated/0/Pictures
    File file = new File(path, "DemoPicture.jpg");
  //在5.1上測試: /storage/sdcard/Pictures/DemoPicture.jpg
  //在7.0上測試颖医;/storage/emulated/0/Pictures/DemoPicture.jpg

    try {
        // Make sure the Pictures directory exists.確保文件夾存在,下面的方法表示創(chuàng)建相應(yīng)的文件夾蚁署。具體的文件就要靠流的操作來完成了便脊。
        path.mkdirs();

        // Very simple code to copy a picture from the application's
        // resource into the external file.  Note that this code does
        // no error checking, and assumes the picture is small (does not
        // try to copy it in chunks).  Note that if external storage is
        // not currently mounted this will silently fail.
        InputStream is = getResources().openRawResource(R.drawable.balloons);
        OutputStream os = new FileOutputStream(file);
        byte[] data = new byte[is.available()];
        is.read(data);
        os.write(data);
        is.close();
        os.close();

        // Tell the media scanner about the new file so that it is
        // immediately available to the user.
        //告訴媒體掃描器有關(guān)新文件的信息,立即告訴用戶光戈。
       //下面代碼表示演示了手機(jī)多媒體掃描的流程。
        MediaScannerConnection.scanFile(this,
                new String[] { file.toString() }, null,
                new MediaScannerConnection.OnScanCompletedListener() {
            public void onScanCompleted(String path, Uri uri) {
                Log.i("ExternalStorage", "Scanned " + path + ":");
                Log.i("ExternalStorage", "-> uri=" + uri);
            }
        });
    } catch (IOException e) {
        // Unable to create file, likely because external storage is
        // not currently mounted.
        Log.w("ExternalStorage", "Error writing " + file, e);
    }
}

void deleteExternalStoragePublicPicture() {
    // Create a path where we will place our picture in the user's
    // public pictures directory and delete the file.  If external
    // storage is not currently mounted this will fail.
    File path = Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES);
    File file = new File(path, "DemoPicture.jpg");
    file.delete();
}

boolean hasExternalStoragePublicPicture() {
    // Create a path where we will place our picture in the user's
    // public pictures directory and check if the file exists.  If
    // external storage is not currently mounted this will think the
    // picture doesn't exist.
    File path = Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES);
    File file = new File(path, "DemoPicture.jpg");
    return file.exists();
}

例子

    //檢查設(shè)備介質(zhì)是否可以使用
    public boolean isExternalStorageWritable() {
        String state = Environment.getExternalStorageState();

        if (Environment.MEDIA_MOUNTED.equals(state)) {
            return true;
        }

        return false;
    }

先檢查設(shè)備介質(zhì)是否可用:

                if (isExternalStorageWritable()) {
                  //這是使用的運(yùn)行時權(quán)限一個注解方式動態(tài)申請權(quán)限
                    SDTestActivityPermissionsDispatcher.saveFileOneWithCheck(SDTestActivity.this);
                } else {
                    mTextView1.setText("設(shè)備不可以用");
                }

設(shè)備可用遂赠,執(zhí)行相應(yīng)的方法:

     //保存可以與其他應(yīng)用共享的文件
    @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
    public void saveFileOne() {
        //保在下載文件夾中
        File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "testwenjian");

     
        /**
         * File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),"testwenjian");
         * 5.1:/storage/sdcard/Download/testwenjian
         * 7.0:/storage/emulated/0/Download/testwenjian
         * 不管是file.mkdir還是file.mkdirs都是創(chuàng)建目錄的久妆,不是創(chuàng)建新的文件
         * 創(chuàng)建了一次新的之后,表示已經(jīng)存在了跷睦,那么就不會在創(chuàng)建了筷弦,會返回一個false,表示文件夾未創(chuàng)建成功抑诸,但是實(shí)際上文件已經(jīng)存在了烂琴。
          *具體效果如下圖一
         */
        if (file.mkdir()) {
            //表示文件創(chuàng)建成功了
            mTextView1.setText("文件夾創(chuàng)建成功了");
            //開始寫入文件
            File file1 = new File(file, "xlj.txt");
            Log.d("newtest", "hah: " + file1.toString());


            /****
             * File file1 = new File(file,"xlj.txt");
             * 5.0:/storage/sdcard/Download/testwenjian/xlj.txt
             * 7.0: /storage/emulated/0/Download/testwenjian/xlj.txt
             * 通過測試不難發(fā)現(xiàn),使用具體的文件通過具體的 流操作就可以完成了蜕乡, 是文件輸出流奸绷,其本身就具有再具體的file路徑下創(chuàng)建文件的功能。
             *具體效果見圖二:
             */

            String info = "恭喜您层玲,數(shù)據(jù)寫入成功了";
            try {
                FileOutputStream fileOutputStream = new FileOutputStream(file1);

                fileOutputStream.write(info.getBytes());

                fileOutputStream.close();

                mTextView1.setText("創(chuàng)建新的文件成功了");

            } catch (FileNotFoundException e) {
                e.printStackTrace();
                mTextView1.setText("創(chuàng)建新的文件失敗了");
            } catch (IOException e) {
                e.printStackTrace();

                mTextView1.setText("創(chuàng)建新的文件出現(xiàn)了IO異常了");
            }
        } else {
            mTextView1.setText("文件夾未創(chuàng)建成功");
            file.delete();
        }
    }

建立文件夾号醉,如圖一:


圖一

最后建立相應(yīng)的文件,如圖二:
在5.0系統(tǒng)下:


圖二

在7.0系統(tǒng)下:


7.0測試一外部存貯.png

另外,保存到外部存儲的公共區(qū)域上如果你想在媒體掃描程序中隱藏您的文件

在您的外部文件目錄中包含名為 `.nomedia` 的空文件(注意文件名中的點(diǎn)前綴)辛块。 這將阻止媒體掃描程序讀取您的媒體文件畔派,
并通過 MediaStore 內(nèi)容提供程序?qū)⑵涮峁┙o其他應(yīng)用。

4润绵、將數(shù)據(jù)保存在外部存儲上的應(yīng)用私有位置上,這樣這些文件只能本應(yīng)用使用,別的應(yīng)用程序無法訪問.

簡單來講就是五個方法线椰,五個方法得到想要操作的文件夾路徑,接下來就是流的操作尘盼,往你想用的文件夾路徑中創(chuàng)建文件或者讀取文件了憨愉,所以這里只介紹這五個方法烦绳,對于流的操作不在寫。

當(dāng)用戶卸載您的應(yīng)用時莱衩,此目錄及其內(nèi)容將被刪除爵嗅。此外,系統(tǒng)媒體掃描程序不回讀取這些目錄中的文件笨蚁,因此不能從MediaStore內(nèi)容提供程序訪問這些文件睹晒。同樣,不應(yīng)將這些目錄用于最終屬于用戶的媒體括细,例如使用您的應(yīng)用拍攝或編輯照片或者您的應(yīng)用購買的音樂等伪很,這些文件應(yīng)該放在公共的目錄中。

當(dāng)應(yīng)用卸載時候,這部分內(nèi)容也會刪除,另外,盡管MediaStore提供的程序不能訪問此部分內(nèi)容,但是別的應(yīng)用程序具有READ_XXX權(quán)限的仍可以獲取全部文件時,仍可以得到,所以并不是完全的保密.

(1)getExternalFilesDir(String type)

如果正在處理的文件不適合其他應(yīng)用使用(比如應(yīng)用使用的圖形紋理或者音效)奋单,則可以考慮使用外部存儲上的私有存儲目錄锉试。此方法需要傳入type參數(shù)指定子目錄的類型。如果不需要特定的目錄览濒,也可以傳遞null獲得應(yīng)用私有目錄的根目錄呆盖。也可以指定具體的比如DIRECTORY_MOVIES

從Android 4.4開始,讀取或者寫入應(yīng)用私有目錄中的文件不在需要READ_EXTERNAL_STORAGE或者WRITE_EXTERNAL_STORAGE權(quán)限贷笛,可以通過添加maxSdkVersion屬性來聲明应又,只在較低版本的Android中請求該權(quán)限:

<manifest...>
            <users-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE"
                                               andorid:maxSdkVersion = "18"/>
</manifest>

下面是一些打印的Log:進(jìn)行方法的展示

(1)getExternalFilesDir(null)
    5.0:
    /storage/sdcard/Android/data/example.xlj.savecundemo/files
   7.0:
    /storage/emulated/0/Android/data/example.xlj.savecundemo/files

(2)getExternalFilesDir(Environment.DIRECTORY_MOVIES)
     5.0:
     /storage/sdcard/Android/data/example.xlj.savecundemo/files/Movies
    7.0:
    /storage/emulated/0/Android/data/example.xlj.savecundemo/files/Movies

(3)getExternalFilesDir("xlj")
    /storage/sdcard/Android/data/example.xlj.savecundemo/files/xlj

下面是測試?yán)樱?/p>

 File file = new File(getExternalFilesDir(null), "DemoFile.jpg");

        try {
            // Very simple code to copy a picture from the application's
            // resource into the external file.  Note that this code does
            // no error checking, and assumes the picture is small (does not
            // try to copy it in chunks).  Note that if external storage is
            // not currently mounted this will silently fail.
            InputStream is = getResources().openRawResource(R.raw.my_my_name);
            OutputStream os = new FileOutputStream(file);
            byte[] data = new byte[is.available()];
            is.read(data);
            os.write(data);
            is.close();
            os.close();
        } catch (IOException e) {
            // Unable to create file, likely because external storage is
            // not currently mounted.
            Log.w("ExternalStorage", "Error writing " + file, e);
        }

在5.0系統(tǒng)下;


5.0系統(tǒng)之下.png

在7.0系統(tǒng)下:


7.0測試隱私文件存儲.png
(2)getExternalFilesDirs(String type)

與前面getExternalFilesDir方法一樣乏苦,不同的是此方法得到的是一個file[],一般取file[0]使用即可

有時株扛,已分配某個內(nèi)部存儲器分區(qū)用作外部存儲的設(shè)備可能提供了SD卡槽。在使用運(yùn)行 Android 4.3和更低版本的這類設(shè)備時汇荐,getExternalFilesDir()方法將僅提供內(nèi)部分區(qū)的訪問權(quán)限洞就,而您的應(yīng)用無法讀取或者寫入SD卡。

從Android 4.4開始掀淘,可以通過調(diào)用getEx ternalFilesDirs()來同時訪問這兩個位置旬蟋,該方法將會返回包含各個位置條目的File數(shù)組疗疟。數(shù)組中的第一個條目被視為外部主存儲栈雳,除非該位置已滿或者不可用嵌削,否則應(yīng)該使用該位置骑晶。

如果希望支持Android 4.3和更低版本的同時訪問兩個可能的位置耻煤,請使用支持庫中的靜態(tài)方法
ContextCompat.getExternalFilesDirs();在Android 4.3或者更低的版本中隐砸,此方法也會返回一個File數(shù)組膝捞,但其中始終包含一個條目鹰溜,只能在使用的時候使用File[0];

盡管MediaStore內(nèi)容提供程序不能訪問getExternalFilesDir()和getExternalFilesDirs()所提供的目錄架忌,但其他具有 READ_EXTERNAL_STORAGE權(quán)限的用用仍可訪問外部存儲上的所有文件吞彤,如上述文件,如果您想要完全限制您的文件訪問權(quán)限,則應(yīng)該講您的文件寫入到內(nèi)部存儲饰恕。

(3)getExternalCacherDir();
         5.0
         /storage/sdcard/Android/data/example.xlj.savecundemo/cache
         7.0
         /storage/emulated/0/Android/data/example.xlj.savecundemo/cache
    

與前面敘述的ContextCompat.getExternalFilesDirs()相似挠羔,您也可以通過調(diào)用ContextCompat.getExternalCacheDirs()來訪問外部存儲上的緩存目錄。

為了節(jié)省文件空間并保持應(yīng)用性能埋嵌,您應(yīng)該在應(yīng)用的整個生命周期內(nèi)仔細(xì)管理您的緩存文件并移除其中不再需要的文件破加。

調(diào)用此方法,得到的文件路徑如圖所示:
5.0系統(tǒng)之下:


測試存儲緩存隱私文件.png

7.0系統(tǒng)之下雹嗦;


7.0測試緩存存儲.png
(4)getExternalCacherDirs();

此方法與前面提到的getExternalCacherDirs()一樣范舀。其返回值也是Files[];

(5)getExternalMediaDir()

執(zhí)行此方法:

/storage/emulated/0/Android/media/example.xlj.savecundemo

7.0系統(tǒng)之下:


結(jié)果

發(fā)現(xiàn)此方法是在Android文件夾下建立了media文件夾,在其中建立了你自己應(yīng)用包名的文件夾了罪,你可以放數(shù)據(jù)锭环。前面提到的四個方法都是放在了Android/data/應(yīng)用包名/下

數(shù)據(jù)庫存儲

Android 提供了對于SQlite數(shù)據(jù)庫的完全支持,應(yīng)用中的任何類(不包括應(yīng)用外部的類)均可按名稱訪問您所創(chuàng)建的任何數(shù)據(jù)庫泊藕。

數(shù)據(jù)庫存儲涉及到兩個關(guān)鍵類:

SQLiteDatabase

此類SQLiteDatabase公開了管理SQLite數(shù)據(jù)庫的方法辅辩。SQLiteDatabase具有創(chuàng)建、刪除娃圆、執(zhí)行SQL命令以及執(zhí)行其他常見數(shù)據(jù)庫管理任務(wù)的方法玫锋。

SQLiteOpenHelper

實(shí)際操作是寫一個類繼承此類
SQLiteOpenHelper是管理數(shù)據(jù)庫創(chuàng)建和版本管理的助手類。

數(shù)據(jù)庫名稱在應(yīng)用中必須是唯一的讼呢,而不是跨所有應(yīng)用程序景醇。

比如自己寫的一個例子:

public class MySQLiteOpenHelper extends SQLiteOpenHelper {

    //構(gòu)造方法一
    public MySQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, DatabaseErrorHandler errorHandler) {
        super(context, name, factory, version, errorHandler);
    }

    //構(gòu)造方法二
    public MySQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
//        super(context, name, factory, version);
        //可以通過外界參數(shù)傳入創(chuàng)建,這里直接賦值了
        super(context,"db_openhelper",null,1);

    }

    //重寫的onCreate方法
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
             sqLiteDatabase.execSQL("create table if not exists tb_my(_id integer primary key autoincrement,title text)");
    }

    //用于更新數(shù)據(jù)庫版本
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
        //參數(shù)一表示以前的版本吝岭,oldVersion
        //參數(shù)二表示新的版本,newVersion
        if (i1 > i){
            sqLiteDatabase.execSQL("drop table if exists tb_my");
            onCreate(sqLiteDatabase);
        }
    }
}

在應(yīng)用中使用:

       //使用自定義的SQLiteOpenHelper
        MySQLiteOpenHelper mySQLiteOpenHelper = new MySQLiteOpenHelper(this,"",null,1);
        //執(zhí)行完上局話得到吧寺。窜管。什么反應(yīng)也沒有
        SQLiteDatabase sqLiteDatabase = mySQLiteOpenHelper.getReadableDatabase();
        //通過幫助類得到管理數(shù)據(jù)庫的方法。便會得到在內(nèi)部存儲中建立相應(yīng)的數(shù)據(jù)庫

        sqLiteDatabase.insert("tb_my","title",null);

結(jié)果:


O5{@J1246`E4CL(S5%7NRWC.png

寫給自己的備注:

關(guān)于數(shù)據(jù)庫這邊簡單記錄一下稚机,上面涉及到的兩個鏈接都是Google官方提供的資料幕帆,計劃在整理新的文檔,因為其涉及的還是比較多的赖条。整理完成后失乾,將Google提供的鏈接替換成自己文章的鏈接。

網(wǎng)絡(luò)存儲

所謂的網(wǎng)絡(luò)存儲簡單來講纬乍,就是將數(shù)據(jù)資料放在網(wǎng)絡(luò)服務(wù)器上碱茁。
由于市面上的網(wǎng)絡(luò)框架比較多,就不做詳細(xì)介紹了仿贬。下面提供兩個Google推薦的兩個參考類纽竣、

java.net.*
android.net.*

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蜓氨,更是在濱河造成了極大的恐慌聋袋,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件穴吹,死亡現(xiàn)場離奇詭異幽勒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)港令,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門啥容,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缠借,你說我怎么就攤上這事干毅。” “怎么了泼返?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵硝逢,是天一觀的道長。 經(jīng)常有香客問我绅喉,道長渠鸽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任柴罐,我火速辦了婚禮徽缚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘革屠。我一直安慰自己凿试,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布似芝。 她就那樣靜靜地躺著那婉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪党瓮。 梳的紋絲不亂的頭發(fā)上详炬,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機(jī)與錄音寞奸,去河邊找鬼呛谜。 笑死,一個胖子當(dāng)著我的面吹牛枪萄,可吹牛的內(nèi)容都是我干的隐岛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼呻引,長吁一口氣:“原來是場噩夢啊……” “哼礼仗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤元践,失蹤者是張志新(化名)和其女友劉穎韭脊,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體单旁,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沪羔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了象浑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔫饰。...
    茶點(diǎn)故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖愉豺,靈堂內(nèi)的尸體忽然破棺而出篓吁,到底是詐尸還是另有隱情,我是刑警寧澤蚪拦,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布杖剪,位于F島的核電站,受9級特大地震影響驰贷,放射性物質(zhì)發(fā)生泄漏盛嘿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一括袒、第九天 我趴在偏房一處隱蔽的房頂上張望次兆。 院中可真熱鬧,春花似錦锹锰、人聲如沸芥炭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚤认。三九已至,卻和暖如春糕伐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蘸嘶。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工良瞧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人训唱。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓褥蚯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親况增。 傳聞我的和親對象是個殘疾皇子赞庶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評論 2 354

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,124評論 25 707
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,414評論 0 17
  • 以前看到更多的論調(diào)說的都是要活在當(dāng)下,享受當(dāng)下的幸福與快樂歧强。 可又有話說:人無遠(yuǎn)慮必有近憂澜薄。 其實(shí)兩句話并不矛盾。...
    邵公子GZS閱讀 122評論 0 0
  • 作為“民國四大才女”之一的張愛玲,其作品以獨(dú)特的風(fēng)格聞名于世茅特,其小說大多表現(xiàn)男女之間的情感忘分,細(xì)膩而又冷峻,讀罷只覺...
    不思齋主人閱讀 374評論 0 0
  • 春鋤 一鋤寒冬去白修, 鳥語花香惹人醉妒峦。 不問曾經(jīng)苦, 只待馨蘭聞信回兵睛。 二鋤黃土語肯骇, 春雨時節(jié)攜手隨。 昨夜翩小樓卤恳,...
    8d660c73f57e閱讀 570評論 0 0