android offline cache

Reprinted from: http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1209/2136.html


Offline caching is to save the data received from the server locally when the network is unblocked, and directly read the data in the local file when the network is disconnected.
Save the network data locally:
You can write a method to save the data as a local file, save it in any directory of the android system (of course, it should be authorized), but in this case it is the easiest to use the openFileOutput method of Context Also most suitable for our scenario, the following saveObject method demonstrates how to use openFileOutput to save data in a local file:

saveObject

public static boolean saveObject(Serializable ser, String file) {
    FileOutputStream fos = null;
    ObjectOutputStream oos = null;
    try {
        fos = AppContext.getInstance().openFileOutput(file, AppContext.getInstance().MODE_PRIVATE);
        oos = new ObjectOutputStream(fos);
        oos.writeObject(ser);
        oos.flush();
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    } finally {
        try {
            oos.close();
        } catch (Exception e) {
        }
        try {
            fos.close();
        } catch (Exception e) {
        }
    }
}

openFileOutput can directly obtain a file path associated with the application (under /data/data/<package name>/files), and then use the ObjectOutputStream in java io to write the serialized object (writeObject) to the obtained file, You can see that the above implementation process has two key methods: openFileOutput, writeObject and the two key objects Context and ObjectOutputStream that call them. For serialization, please refer to this article: Java object serialization and deserialization practice.  

This is to save a serialized object locally. What does it have to do with our offline cache to save network data?
It matters, because most of the data obtained online can be converted into String type strings, and now the data returned by the server is generally a json format string. The String type of string is actually a serializable object. The following is an example of json data returned by the server (actually jcodecraeer):
{"url":"http://jcodecraeer.com/uploads/soft/android/CodeBox.apk","versionCode":"7","updateMessage":"增加离线缓存,分类筛选功能修正了版本兼容性问题 "}

Using the saveObject method above, we can save the data locally. In order to be able to take out this file, we must figure out how to name the saved file. If it is just the data of an article, we can directly name the file as this The id of the article, because the id is unique, in order not to conflict with other data as much as possible, you can also add a prefix before this id. For example, this article is under the java column. We can do this arc_java_id. If it is an article list, we can name it like this: article category_page number. In short, the principle of naming is to distinguish it from other offline data and be unique. Why not use url as the file name? URL must be unique, but URL does not necessarily conform to file naming conventions.


Here to explain how to read the local cache data


read cache when we need to know the file name on it, the following readObject method to achieve a read cache data based on the file name. In fact, many things correspond to the data saved above.


readObject

/**
 * 读取对象
 *
 * @param file
 * @return
 * @throws IOException
 */
public static Serializable readObject(String file) {
    FileInputStream fis = null;
    ObjectInputStream ois = null;
    try {
        fis = AppContext.getInstance().openFileInput(file);
        ois = new ObjectInputStream(fis);
        return (Serializable) ois.readObject();
    } catch (FileNotFoundException e) {
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            ois.close();
        } catch (Exception e) {
        }
        try {
            fis.close();
        } catch (Exception e) {
        }
    }
    return null;
}

Use

the following code demonstrates how to use the above knowledge storing and reading data network

String key = "codelist_" +  mCategory.getValue()  + "_" + + page ;
String result = "";
//cache
if (HttpUtil.isNetworkConnected()) {
        result = HttpUtil.http_get(AppContext.getInstance(), url );
        HttpUtil.saveObject(result, key);
        result = (String) HttpUtil.readObject(key);
} else {
    result = (String) HttpUtil.readObject(key);
    if (result == null)
        result = "erro";
}

When the network is unblocked, get data from the server (HttpUtil.http_get(AppContext.getInstance(), url )) and save the data locally (HttpUtil.saveObject), and when the network is not available, read the cached data directly from the local , Does not interact with the server.
Wherein HttpUtil is associated with the network tools, where it is involved in the three methods:
isNetworkConnected () determines whether the network is available
saveObject been given above achieved
readObject implementation given above
http_get url server reads the specified data
and AppContext .getInstance() is written by myself to facilitate obtaining the Context object in the static method of HttpUtil.
The key here is the file name.

Additional requirements

and sometimes we have such a demand, when the user reads the same data source within the specified time interval, from a local, over this time interval obtained from the network, the aim is to save the user's traffic, but also Avoid the interface delay caused by getting data from the network every time.


The following is how to determine whether the server data needs to be refreshed according to the time interval, true means not required, false means required (very awkward, is it related to the naming of isCacheDataFailure):
public static boolean isCacheDataFailure(String cachefile) {
    boolean failure = false;
    File data = AppContext.getInstance().getFileStreamPath(cachefile);
    if (data.exists()
            && (System.currentTimeMillis() - data.lastModified()) > CACHE_TIME)
        failure = true;
    else if (!data.exists())
        failure = true;
    return failure;
}

Compare the current time with the modification time of the file, CACHE_TIME is a fixed value (milliseconds), you can replace it with any int type.
Add this judgment condition, and then change the above code to:

String key = "codelist_" +  mCategory.getValue()  + "_" + + page ;
String result = "";
//cache
if (HttpUtil.isNetworkConnected() && HttpUtil.isCacheDataFailure(key)) {
        result = HttpUtil.http_get(AppContext.getInstance(), url );
        HttpUtil.saveObject(result, key);
        result = (String) HttpUtil.readObject(key);
} else {
    result = (String) HttpUtil.readObject(key);
    if (result == null)
        result = "erro";
}

Complete

the steps above for general applications has been good enough, but in high demand, we have to consider as time goes by, more and more data is cached, so we need to add deletion of expired cache function , The principle is to set a threshold. When saving the cache, determine whether the total amount of the current cache is greater than the threshold, and if so, delete the earlier cache.


This is a bit complicated to implement, you can consider a simpler solution, periodically check (or every time the user opens the program) the total amount of cache, when it is greater than the threshold, prompt the user to actively delete. I won't say much about the specific implementation.


Note: The first parameter of the openFileOutput() method is used to specify the file name and cannot include the path separator "/". If the file does not exist, Android will automatically create it. The created file is saved in the /data/data/<package name>/files directory, such as: /data/data/cn.itcast.action/files/itcast.txt, by clicking the Eclipse menu "Window"-"Show View"- "Other", expand the android folder in the dialog window, select the File Explorer view below, and then expand the /data/data/<package name>/files directory in the File Explorer view to see the file.
The second parameter of the openFileOutput() method is used to specify the operation mode. There are four modes, namely: Context.MODE_PRIVATE = 0
Context.MODE_APPEND = 32768
Context.MODE_WORLD_READABLE = 1
Context.MODE_WORLD_WRITEABLE = 2
Context.MODE_PRIVATE: It is the default operation mode, which means that the file is private data and can only be accessed by the application itself. In this mode, the written content will overwrite the content of the original file. If you want to append the newly written content to the original File. You can use Context.MODE_APPEND
Context.MODE_APPEND: the mode will check whether the file exists, and add content to the file if it exists, or create a new file.
Context.MODE_WORLD_READABLE and Context.MODE_WORLD_WRITEABLE are used to control whether other applications have permission to read and write the file.
MODE_WORLD_READABLE: indicates that the current file can be read by other applications; MODE_WORLD_WRITEABLE: indicates that the current file can be written by other applications.
If you want the file to be read and written by other applications, you can pass in:
openFileOutput("itcast.txt", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);

Guess you like

Origin blog.csdn.net/u012049463/article/details/50707645