Android緩存機制——一般存儲實現(xiàn)

一晨另、Android緩存機制

Android緩存分為內(nèi)存緩存和文件緩存(磁盤緩存)鹃栽。在早期档玻,各大圖片緩存框架流行之前怀泊,常用的內(nèi)存緩存方式是軟引用(SoftReference)和弱引用(WeakReference),如大部分的使用方式:HashMap<String url, SoftReference<Drawable>> imageCache;這種形式误趴。從Android 2.3(Level 9)開始霹琼,垃圾回收器更傾向于回收SoftReference或WeakReference對象,這使得SoftReference和WeakReference變得不是那么實用有效凉当。同時枣申,到了Android 3.0(Level 11)之后,圖片數(shù)據(jù)Bitmap被放置到了內(nèi)存的堆區(qū)域看杭,而堆區(qū)域的內(nèi)存是由GC管理的忠藤,開發(fā)者也就不需要進行圖片資源的釋放工作,但這也使得圖片數(shù)據(jù)的釋放無法預知楼雹,增加了造成OOM的可能模孩。因此尖阔,在Android3.1以后,Android推出了LruCache這個內(nèi)存緩存類榨咐,LruCache中的對象是強引用的介却。

二、二級緩存工作機制

所謂二級緩存實際上并不復雜块茁,當Android端需要獲得數(shù)據(jù)時比如獲取網(wǎng)絡中的圖片齿坷,我們首先從內(nèi)存中查找(按鍵查找),內(nèi)存中沒有的再從磁盤文件或sqlite中去查找龟劲,若磁盤中也沒有才通過網(wǎng)絡獲任赶摹;當獲得來自網(wǎng)絡的數(shù)據(jù)昌跌,就以key-value對的方式先緩存到內(nèi)存(一級緩存)仰禀,同時緩存到文件或sqlite中(二級緩存)。注意:內(nèi)存緩存會造成堆內(nèi)存泄露蚕愤,所有一級緩存通常要嚴格控制緩存的大小答恶,一般控制在系統(tǒng)內(nèi)存的1/4。

三萍诱、離線緩存

離線緩存就是在網(wǎng)絡暢通的情況下將從服務器收到的數(shù)據(jù)保存到本地悬嗓,當網(wǎng)絡斷開之后直接讀取本地文件中的數(shù)據(jù)。本質(zhì)就是要控制好文件的存儲裕坊、讀取包竹。

我們的應用程序一般會產(chǎn)生以下幾種類型的數(shù)據(jù):

file-普通的文件存儲
database-數(shù)據(jù)庫文件(.db文件)
sharedPreference-配置數(shù)據(jù)(.xml文件)
cache-圖片緩存文件

應用內(nèi)數(shù)據(jù)的所有路徑:

/data/data/com.xxx.xxx/cache - 應用內(nèi)緩存(注:對應方法getCacheDir())
/data/data/com.xxx.xxx/databases - 應用內(nèi)數(shù)據(jù)庫
/data/data/com.xxx.xxx/shared_prefs - 應用內(nèi)配置文件
/data/data/com.xxx.xxx/files - 應用內(nèi)文件(注:對應方法getFilesDir())
SD卡的文件(開發(fā)者自定義的)

不管是內(nèi)置還是外置SD卡,獲取路徑的方法是一樣:

獲取SD卡根目錄:Environment.getExternalStorageDirectory().getAbsolutePath();
外部Cache路徑:/mnt/sdcard/android/data/com.xxx.xxx/cache 一般存儲緩存數(shù)據(jù)(注:通過getExternalCacheDir()獲燃)
外部File路徑:/mnt/sdcard/android/data/com.xxx.xxx/files 存儲長時間存在的數(shù)據(jù) (注:通過getExternalFilesDir(String type)獲取周瞎, type為特定類型,可以是以下任何一種
Environment.DIRECTORY_MUSIC, Environment.DIRECTORY_PODCASTS, Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS, Environment.DIRECTORY_PICTURES, or Environment.DIRECTORY_MOVIES. )



四饵蒂、Android常用存儲:

Android平臺進行數(shù)據(jù)存儲的五大方式,分別如下:

  • 1 使用SharedPreferences存儲數(shù)據(jù)

  • 2 文件存儲數(shù)據(jù)

  • 3 SQLite數(shù)據(jù)庫存儲數(shù)據(jù)

  • 4 使用ContentProvider存儲數(shù)據(jù)

  • 5 網(wǎng)絡存儲數(shù)據(jù)

以下介紹了前兩種的使用声诸,數(shù)據(jù)庫可以使用庫比較方便,如:greenDao; 其他不過多介紹退盯。主要針對緩存做介紹彼乌。

(一)、SharedPreferences詳解

使用 SharedPreferences 保存key-value對的步驟一般是這樣:
使用Activity類的getSharedPreferences方法獲取到 SharedPreferences 對象渊迁,指定文件名和訪問權限
獲得SharedPreferences.Editor對象慰照,并使用該對象的 putXxx方法保存key-value對。
通過SharedPreferences.Editor的commit方法保存(提交)key-value對琉朽。

(1)獲取SharedPreferences的兩種方式:

1 調(diào)用Context對象的getSharedPreferences()方法
2 調(diào)用Activity對象的getPreferences()方法

(2)兩種方式的區(qū)別:

  • 調(diào)用Context對象的getSharedPreferences()方法獲得的SharedPreferences對象可以被同一應用程序下的其他組件共享.

  • 調(diào)用Activity對象的getPreferences()方法獲得的SharedPreferences對象只能在該Activity中使用.

(3)SharedPreferences的四種操作模式:

Context.MODE_PRIVATE

Context.MODE_APPEND

Context.MODE_WORLD_READABLE

Context.MODE_WORLD_WRITEABLE
  • Context.MODE_PRIVATE:為默認操作模式,代表該文件是私有數(shù)據(jù),只能被應用本身訪問,在該模式下,寫入的內(nèi)容會覆蓋原文件的內(nèi)容

  • Context.MODE_APPEND:模式會檢查文件是否存在,存在就往文件追加內(nèi)容,否則就創(chuàng)建新文件.

  • Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用來控制其他應用是否有權限讀寫該文件.

  • MODE_WORLD_READABLE:表示當前文件可以被其他應用讀取.

  • MODE_WORLD_WRITEABLE:表示當前文件可以被其他應用寫入.

(4)將數(shù)據(jù)保存至SharedPreferences:

SharedPreferences sp = getSharedPreferences("sp_demo", Context.MODE_PRIVATE);
sp.edit().putString("name", "小張").putInt("age", 11).commit();

或者下面的寫法也可以

SharedPreferences preferences=getSharedPreferences("user",Context.MODE_PRIVATE);

Editor editor=preferences.edit();

String name="xixi";

String age="22";

editor.putString("name", name);

editor.putString("age", age);

editor.commit();

(5)從SharedPreferences獲取數(shù)據(jù):

SharedPreferences preferences=getSharedPreferences("user", Context.MODE_PRIVATE);

String name=preferences.getString("name", "defaultname");

String age=preferences.getString("age", "0");

(二)毒租、文件存儲(File)詳解

文件存儲是 Android 中最基本的一種數(shù)據(jù)存儲方式,它不對存儲的內(nèi)容進行任何的格式化處理漓骚,所有數(shù)據(jù)都是原封不動的保存到文件當中的蝌衔。它比較適合用于存儲一些簡單的文本數(shù)據(jù)或二進制數(shù)據(jù)。如果你想使用文件存儲方式來保存一些較為復雜的文本數(shù)據(jù)蝌蹂,就需要定義一套自己的格式規(guī)范噩斟,方便于之后將文件重新解析出來。

(1)Context 提供的文件存儲方法(openFileOutput孤个、openFileInput)

  • Context 類中提供了一個openFileOutput()方法剃允,可以用于將數(shù)據(jù)存儲到指定的文件中。
  • 這個方法接收兩個參數(shù)齐鲤,第一個參數(shù)是文件創(chuàng)建時使用的名稱斥废,注意這里指定的文件名不可以包含路徑,因為所有文件都是默認儲存到 /data/data/<包名>/files/ 目錄下的给郊。
  • 第二個參數(shù)是文件的操作模式牡肉,主要有兩種模式可選,MODE_PRIVATE(覆蓋原文) 和 MODE_APPEND(追加內(nèi)容) 淆九。
  • openFileOutput() 方法返回的是一個 FileOutputStream 對象统锤,得到這個對象之后就可以使用 Java 流的方式將數(shù)據(jù)寫入到文件中了。

(I)將一段文本內(nèi)容保存到文件中:

    public void save() {
        String data = "hello to save";
        FileOutputStream out = null;
        BufferedWriter writer = null;
        try {
            //設置文件名稱炭庙,以及存儲方式
            out = openFileOutput("data", Context.MODE_PRIVATE);
            //創(chuàng)建一個OutputStreamWriter對象饲窿,傳入BufferedWriter的構造器中
            writer = new BufferedWriter(new OutputStreamWriter(out));
            //向文件中寫入數(shù)據(jù)
            writer.write(data);
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

注意:文件默認會存儲到/data/data/package/files中;

  • 類似于將數(shù)據(jù)存儲到文件中苛茂,Context 類中還提供了一個 openFileInput()方法姿骏,用于從文件中讀取數(shù)據(jù)结借。

  • openFileInput() 方法只接收一個參數(shù),即要讀取的文件名鸦泳,然后系統(tǒng)會自動到 /data/data/<包名>/files/ 目錄下去加載這個文件,并返回一個 FileInputStream 對象迹卢。

(II)展示如何從文件中讀取文本數(shù)據(jù):

    public String load() {
        FileInputStream in = null;
        BufferedReader reader = null;
        StringBuilder content = new StringBuilder();
        try {
            //設置將要打開的存儲文件名稱
            in = openFileInput("data");
            //FileInputStream -> InputStreamReader ->BufferedReader
            reader = new BufferedReader(new InputStreamReader(in));
            String line = new String();
            //讀取每一行數(shù)據(jù)辽故,并追加到StringBuilder對象中,直到結束
            while ((line = reader.readLine()) != null) {
                content.append(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return content.toString();
    }
(III)四種文件保存的模式腐碱。

Context.MODE_PRIVATE 為默認操作模式誊垢,代表該文件是私有數(shù)據(jù),只能被應用本身訪問症见,在該模式下寫入的內(nèi)容會覆蓋原文件的內(nèi)容喂走。
Context.MODE_APPEND 檢查文件是否存在,存在就往文件追加內(nèi)容谋作,否則就創(chuàng)建新文件芋肠。
MODE_WORLD_READABLE 表示當前文件可以被其他應用讀取。
MODE_WORLD_WRITEABLE 表示當前文件可以被其他應用寫入遵蚜。

(2)內(nèi)部存儲:一般IO流操作File

注意內(nèi)部存儲不是內(nèi)存帖池。內(nèi)部存儲位于系統(tǒng)中很特殊的一個位置奈惑,如果你想將文件存儲于內(nèi)部存儲中,那么文件默認只能被你的應用訪問到睡汹,且一個應用所創(chuàng)建的所有文件都在和應用包名相同的目錄下肴甸。也就是說應用創(chuàng)建于內(nèi)部存儲的文件,與這個應用是關聯(lián)起來的囚巴。當一個應用卸載之后原在,內(nèi)部存儲中的這些文件也被刪除。從技術上來講如果你在創(chuàng)建內(nèi)部存儲文件的時候?qū)⑽募傩栽O置成可讀彤叉,其他app能夠訪問自己應用的數(shù)據(jù)庶柿,前提是他知道你這個應用的包名,如果一個文件的屬性是私有(private)秽浇,那么即使知道包名其他應用也無法訪問浮庐。 內(nèi)部存儲空間十分有限,因而顯得可貴兼呵,另外兔辅,它也是系統(tǒng)本身和系統(tǒng)應用程序主要的數(shù)據(jù)存儲所在地,一旦內(nèi)部存儲空間耗盡击喂,手機也就無法使用了维苔。所以對于內(nèi)部存儲空間,我們要盡量避免使用懂昂。Shared Preferences和SQLite數(shù)據(jù)庫都是存儲在內(nèi)部存儲空間上的介时。內(nèi)部存儲一般用Context來獲取和操作。

getFilesDir()獲取你app的內(nèi)部存儲空間凌彬,相當于你的應用在內(nèi)部存儲上的根目錄沸柔。

應用安裝后都會在Android 根目錄生成 /data/data/packagename,當前應用讀取不需要讀寫權限

注意:
有些開發(fā)者可能看到過應用的根目錄為 /data/user/0/packagename 的情況铲敛,這里解釋一下褐澎,Android 4.2 版本添加了同一設備可以登錄不同用戶的功能(由于專利原因僅限于平板電腦,手機不支持此功能)伐蒋,所以為了區(qū)分不同用戶在同一應用中的設置和存儲的數(shù)據(jù)工三,添加了該系列的路徑,該路徑指向 /data/data/packagename

  • getFileDir() 方法得到的是該目錄下 files 文件夾的 File 對象
  • getChacheDir() 方法得到的是該目錄下 cache 文件夾的 File 對象
  • 直接調(diào)用ContextWrapper的 openFileOutput(String name,int mode) 也會在該目錄下 files 文件夾下創(chuàng)建相應的文件先鱼,并且是私密的俭正。可以修改為其他應用可訪問的焙畔,通過 openFileOutput 方法的 mode 參數(shù)來完成

注意:

  1. 該目錄只有 root 權限下可以查看掸读,會隨著應用卸載刪除

  2. 應用程序詳情中清除數(shù)據(jù)會將 packagename 下所有數(shù)據(jù)以及內(nèi)置存儲、外置 SD 卡存儲空間中 /Android/data/packagename 的整個目錄刪除刪除

  3. 應用程序詳情中清除緩存會將 packagename/cache 目錄下所有數(shù)據(jù)以及內(nèi)置存儲、外置 SD 卡存儲空間中 /Android/data/packagename/cache 的整個目錄刪除

如果是要創(chuàng)建一個文件儿惫,如下

File file = newFile(context.getFilesDir(), filename);`

安卓還為我們提供了一個簡便方法 openFileOutput()`來讀寫應用在內(nèi)部存儲空間上的文件澡罚,下面是一個向文件中寫入文本的例子:(參考以上)

String filename = "myfile";
String string = "Hello world!";
FileOutputStream outputStream;
try{
outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
outputStream.write(string.getBytes());
outputStream.close();
} catch(Exception e) {
e.printStackTrace();
}

內(nèi)部存儲的其他一些操作:

A.列出所有的已創(chuàng)建的文件,這個可能不容易想到,Context居然有這樣的方法肾请。

String[] files = Context.fileList();
for(String file : files) {
Log.e(TAG, "file is "+ file);
}

B.刪除文件始苇,能創(chuàng)建就要能夠刪除,當然也會提供了刪除文件的接口筐喳,它也非常簡單,只需要提供文件名

if(Context.deleteFile(filename)) {
Log.e(TAG, "delete file "+ filename + " sucessfully“);
} else {
Log.e(TAG, "failed to deletefile " + filename);
}

C.創(chuàng)建一個目錄函喉,需要傳入目錄名稱避归,它返回 一個文件對象用到操作路徑

File workDir = Context.getDir(dirName, Context.MODE_PRIVATE);
Log.e(TAG, "workdir "+ workDir.getAbsolutePath();

總結一下文件相關操作,可以得出以下三個特點:

  1. 文件操作只需要向函數(shù)提供文件名管呵,所以程序自己只需要維護文件名即可梳毙;
  2. 不用自己去創(chuàng)建文件對象和輸入、輸出流捐下,提供文件名就可以返回File對象或輸入輸出流
  3. 對于路徑操作返回的都是文件對象账锹。

(3)外部存儲:IO(讀寫sdcard上的文件)

手機自帶 ROM 的存儲路徑

1、getExternalCacheDir() 方法坷襟,獲取內(nèi)置存儲卡中 /Android/data/packagename/cache 目錄的路徑奸柬,4.4及之后讀寫不需要權限,會隨著應用卸載刪除
2婴程、getExternalFilesDir() 方法廓奕,獲取內(nèi)置存儲卡中 /Android/data/packagename/files 目錄的路徑,4.4之后讀寫不需要權限档叔,會隨著應用卸載刪除桌粉,該方法參數(shù)為 "null" 時不指定子文件夾,指定時創(chuàng)建子文件夾保存文件衙四。創(chuàng)建的文件其他應用只要有讀寫權限也可以讀取铃肯,如果要私密的就使用內(nèi)部存儲。
3传蹈、Environment.getExternalStorageDirectory() 方法得到的是內(nèi)置存儲目錄的根路徑目錄,讀寫需要權限押逼,不會隨著應用卸載刪除
4、Environment.getExternalStoragePublicDirectory() 方法得到的是內(nèi)置存儲目錄根路徑下的特定類型文件的公共目錄卡睦,讀寫需要權限,不會隨著應用卸載刪除
5宴胧、注意,內(nèi)置存儲中 /Android/data/packagenaem/ 路徑的讀寫 Android 版本不同表锻,需要權限不同恕齐,所以開發(fā)時應始終聲明讀寫權限

其中讀寫步驟按如下進行:

1、調(diào)用Environment的getExternalStorageState()方法判斷手機上是否插了sd卡,且應用程序具有讀寫SD卡的權限,如下代碼將返回true

Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)

2显歧、調(diào)用Environment.getExternalStorageDirectory()方法來獲取外部存儲器仪或,也就是SD卡的目錄,或者使用"/mnt/sdcard/"目錄

3、使用IO流操作SD卡上的文件

注意點:手機應該已插入SD卡士骤,對于模擬器而言范删,可通過mksdcard命令來創(chuàng)建虛擬存儲卡
必須在AndroidManifest.xml上配置讀寫SD卡的權限

<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

(I)文件寫操作函數(shù)

    private void write(String content) {
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) { // 如果sdcard存在
            File file = new File(Environment.getExternalStorageDirectory()
                    .toString()
                    + File.separator
                    + DIR
                    + File.separator
                    + FILENAME); // 定義File類對象
            if (!file.getParentFile().exists()) { // 父文件夾不存在
                file.getParentFile().mkdirs(); // 創(chuàng)建文件夾
            }
            PrintStream out = null; // 打印流對象用于輸出
            try {
                out = new PrintStream(new FileOutputStream(file, true)); // 追加文件
                out.println(content);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (out != null) {
                    out.close(); // 關閉打印流
                }
            }
        } else { // SDCard不存在,使用Toast提示用戶
            Toast.makeText(this, "保存失敗拷肌,SD卡不存在到旦!", Toast.LENGTH_LONG).show();
        }
    }

II)文件讀操作函數(shù)

    private String read() {

        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) { // 如果sdcard存在
            File file = new File(Environment.getExternalStorageDirectory()
                    .toString()
                    + File.separator
                    + DIR
                    + File.separator
                    + FILENAME); // 定義File類對象
            if (!file.getParentFile().exists()) { // 父文件夾不存在
                file.getParentFile().mkdirs(); // 創(chuàng)建文件夾
            }
            Scanner scan = null; // 掃描輸入
            StringBuilder sb = new StringBuilder();
            try {
                scan = new Scanner(new FileInputStream(file)); // 實例化Scanner
                while (scan.hasNext()) { // 循環(huán)讀取
                    sb.append(scan.next() + "\n"); // 設置文本
                }
                return sb.toString();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (scan != null) {
                    scan.close(); // 關閉打印流
                }
            }
        } else { // SDCard不存在,使用Toast提示用戶
            Toast.makeText(this, "讀取失敗巨缘,SD卡不存在添忘!", Toast.LENGTH_LONG).show();
        }
        return null;
    }


五、總結的一些使用代碼:

常用存儲:
小的數(shù)據(jù)體若锁,參數(shù)搁骑,值(存儲在shared_prefs)
網(wǎng)絡數(shù)據(jù)json(這種數(shù)據(jù)一般不大,存儲在文件中file(.txt等等) )
圖片又固、音樂仲器、視頻(建議數(shù)據(jù)較大的存儲在SD卡中,圖片(有框架的用框架))

(一)小的數(shù)據(jù)(SharedPrefs)

工具類

public class AppConfig {
    private static AppConfig appConfig;

    private SharedPreferences preferences;

    /**
     * App根目錄.
     */
    public String APP_PATH_ROOT;

    private AppConfig() {
        preferences = MyApplication.getInstance().getSharedPreferences("test", Context.MODE_PRIVATE);

        APP_PATH_ROOT = FileUtil.getRootPath(MyApplication.getInstance()).getAbsolutePath() + File.separator +
                "test";
    }

    public synchronized static AppConfig getInstance() {
        if (appConfig == null)
            appConfig = new AppConfig();
        return appConfig;
    }

/*
    public void initialize() {
        IOUtils.createFolder(APP_PATH_ROOT);
    }*/

    public void putInt(String key, int value) {
        preferences.edit().putInt(key, value).commit();
    }

    public int getInt(String key, int defValue) {
        return preferences.getInt(key, defValue);
    }

    public void putString(String key, String value) {
        preferences.edit().putString(key, value).commit();
    }

    public String getString(String key, String defValue) {
        return preferences.getString(key, defValue);
    }

    public void putBoolean(String key, boolean value) {
        preferences.edit().putBoolean(key, value).commit();
    }



    public boolean getBoolean(String key, boolean defValue) {
        return preferences.getBoolean(key, defValue);
    }

    public void putLong(String key, long value) {
        preferences.edit().putLong(key, value).commit();
    }

    public long getLong(String key, long defValue) {
        return preferences.getLong(key, defValue);
    }

    public void putFloat(String key, float value) {
        preferences.edit().putFloat(key, value).commit();
    }

    public float getFloat(String key, float defValue) {
        return preferences.getFloat(key, defValue);
    }


}

(二)文件存儲——網(wǎng)絡數(shù)據(jù)(string-->txt)

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

/**
     * 存儲文件:.txt
     *
     * @param fileName 文件名稱
     * @param content  存儲的文件內(nèi)容
     */

    public static void wirteCache(String fileName, String content) {
        //設置緩存
        File file = new File(MyApplication.getInstance().getCacheDir(), fileName + ".txt");
        if (file.exists()) {
            boolean isdelete = file.delete();
            if (isdelete) {
                Log.e("delete", "success");
            }
        } else {
            byte[] bytes = content.getBytes();
            FileOutputStream output = null;
            try {

                output = new FileOutputStream(file);
                output.write(bytes, 0, bytes.length);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    output.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

讀取數(shù)據(jù)

/**
     * 讀取緩存數(shù)據(jù)
     *
     * @param fileName
     * @return
     */


    public static String readCache(String fileName) {
        String result = null;
        // 讀取文件
        File file = new File(MyApplication.getInstance().getCacheDir() + File.separator
                + fileName + ".txt");
        if (file.exists()) {
            FileInputStream in = null;
            try {
                in = new FileInputStream(file);
                byte[] buffer;
                buffer = new byte[in.available()];
                int count = 0;
                while ((count = in.read(buffer)) > 0) {
                    result = new String(buffer);
                }
            } catch (Exception e) {

                e.printStackTrace();
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } else {
            //不存在緩存
            return "";
        }
        return result;
    }

(三)文件存儲——圖片仰冠,音樂乏冀,視頻

存儲

  /**
     * 文件緩存策略
     *
     * @param url        文件路徑
     * @param fileName   文件名稱
     * @param fileFormat 文件格式
     */
    public static void downFile(String url, String fileName, String fileFormat) {
        try {
            String path = "";
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
                //判斷sd卡是否存在,不存在洋只,存儲在應用內(nèi)部
                path = Environment.getExternalStorageDirectory() + "/";
                Log.e(TAG, "Storage file");
            } else {
                path = MyApplication.getInstance().getFilesDir() + "/";
                Log.e(TAG, "APP file");
            }
            File fileDir = new File(path + "test");
            if (!fileDir.exists()) {
                fileDir.mkdir();
            }
            File file = new File(fileDir.getAbsolutePath() + "/" + fileName + "." + fileFormat);
            if (file.exists()) {
            } else {
                URL myURL = new URL(url);
                URLConnection conn = myURL.openConnection();
                conn.connect();
                InputStream is = conn.getInputStream();
                int fileSize = conn.getContentLength();
                if (fileSize <= 0)
                    throw new RuntimeException("can not know the file`s size");
                if (is == null)
                    throw new RuntimeException("stream is null");
                FileOutputStream fos = new FileOutputStream(file.getPath());
                byte buf[] = new byte[1024];
                while (true) {
                    // 循環(huán)讀取
                    int numread = is.read(buf);
                    if (numread == -1) {
                        break;
                    }
                    fos.write(buf, 0, numread);
                }
                try {
                    is.close();
                } catch (Exception ex) {
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

讀取

 /**
     * 獲取文件地址
     *
     * @param fileName   文件名稱
     * @param fileFormat 文件格式
     * @return
     */
    public static String getDownFilePath(String fileName, String fileFormat) {
        String path = "";
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            //判斷sd卡是否存在煤辨,不存在,存儲在應用內(nèi)部
            path = Environment.getExternalStorageDirectory() + "/";
        } else {
            path = MyApplication.getInstance().getFilesDir() + "/";
            Log.e(TAG, "cache file");
        }
        // 讀取文件
        File file = new File(path + "test" + File.separator
                + fileName + "." + fileFormat);
        if (file.exists()) {
            return file.getAbsolutePath();
        } else {
            return "";
        }
    }
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末木张,一起剝皮案震驚了整個濱河市众辨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌舷礼,老刑警劉巖鹃彻,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異妻献,居然都是意外死亡蛛株,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門育拨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谨履,“玉大人,你說我怎么就攤上這事熬丧∷袼冢” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長害捕。 經(jīng)常有香客問我绿淋,道長,這世上最難降的妖魔是什么尝盼? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任吞滞,我火速辦了婚禮,結果婚禮上盾沫,老公的妹妹穿的比我還像新娘裁赠。我一直安慰自己,他們只是感情好赴精,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布组贺。 她就那樣靜靜地躺著,像睡著了一般祖娘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上啊奄,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天渐苏,我揣著相機與錄音,去河邊找鬼菇夸。 笑死琼富,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的庄新。 我是一名探鬼主播鞠眉,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼择诈!你這毒婦竟也來了械蹋?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤羞芍,失蹤者是張志新(化名)和其女友劉穎哗戈,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荷科,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡唯咬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了畏浆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胆胰。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖刻获,靈堂內(nèi)的尸體忽然破棺而出蜀涨,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布勉盅,位于F島的核電站佑颇,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏草娜。R本人自食惡果不足惜挑胸,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宰闰。 院中可真熱鬧茬贵,春花似錦、人聲如沸移袍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽葡盗。三九已至螟左,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間觅够,已是汗流浹背胶背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留喘先,地道東北人钳吟。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像窘拯,于是被迫代替她去往敵國和親红且。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345