Android缓存方案-网络请求缓存

本文探讨了在Android开发中如何实现网络请求缓存,以减少流量消耗和提高用户体验。作者提出两种缓存策略:文件缓存和数据库缓存,并详细解释了各自的实现步骤。文件缓存利用文件的最后修改时间判断是否需要更新数据,而数据库缓存则涉及表的创建和维护。文章还提及了Volley网络请求框架和Afinal数据库库的使用,并提供了部分代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一些思路与看法

在开发Android应用时,必不可少的就是去网络请求,我们的一般做法是这样的,进入一个页面,页面需要向后台获取数据,我们就用一个异步请求去获取数据,每次进入每次请求,可能我们不会去关心后台的网络请求量,也很少关心这样频繁访问会有多少的流量,在wifi环境下还好,这个是不花钱的,但是在移动流量是需要money的,用户说你这个应用跑流量太多了,你赶紧给我优化,没办法,客户是上帝!
然后我们就来想办法,怎么做呢,做数据缓存呗,减少网络访问。缓存怎么做呢,我们可以这样做,一个是文件存储,将你的json数据都存为文件,一个是数据库存储,将每次的访问的url作为数据库的ID,这样,你可以在需要数据的时候依据ID去数据库查询。好了,假如现在我们的缓存已经做好了,但是用户又说你这个保证不了数据的实时度啊,好吧!于是我们找到了解决办法,给页面增加一个功能按钮,点击刷新时去重新获取数据,这个对于列表的话一般用一个下拉刷新这样的控件。现在我们感觉可以了,但用户说,我不想去点击刷新.哎,我们继续想办法,怎么办呢。那我们就给他设置个文件的有效时间吧,比如说一个小时重新请求数据,或者更短,或者更长,这就要看你数据的更新度了。好了,现在我们的缓存思路基本有了。下面来实现
实现之前,我们先来比较一下数据库缓存和文件缓存,个人认为,数据库缓存比较麻烦,你还需要去依据建表什么的,数据库不要忘了必须有一个时间的字段,而文件存储就好办了,每个文件都有一个最后修改时间,我们只需要依据这个时间来决定是否重新请求。

下面来看具体实现

文件缓存

1、检测文件是否存在

    /**
     * 检测缓存文件是否存在
     * */
    private boolean checkExist() {
        File files = new File(cacheFile + "/" + fileName);
        if (files.exists()) {
            return true;
        } else {
            return false;
        }
    }

2、获取文件的最后修改时间,并且和当前时间作比较

            //文件存在,读取文件的最后修改时间
            File files = new File(cacheFile + "/" + fileName);
            long lastModify = files.lastModified();
            Date date = new Date(files.lastModified());
            Log.d("zhanglinshu", "date -- " + date);

            DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Log.d("zhanglinshu", "lastModify --" + df.format(date));

            Date currentDate = new Date();
            Log.d("zhanglinshu", "currentDate --" + df.format(currentDate));

            long time = currentDate.getTime() - date.getTime();
            //一分钟的time
            long min = time / (1000 * 60);
            Log.d("zhanglinshu", "diffMin" + min);

3、如果大于60分钟,也就是一个小时,则重新获取数据,否则直接读取缓存文件中的数据,当然这个时间视具体时间而定

if (min > 60) {
                request();
            } else {
                readFile();
            }

request()

  /**
     * 从网络获取数据
     */
    private void request() {
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(
                "http://gc.ditu.aliyun.com/geocoding?a=深圳市",
                null,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        Log.d("zhanglinshu", "reponse---->" + response.toString());
                        textGet.setText("写入前:" + response.toString());
                        writeFile(response.toString());
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {

                    }
                });

        mQueue.add(jsonObjectRequest);
    }

我这里采用的是Volley网络请求框架,只需要在你的build文件中添加下面代码即可

    compile 'com.mcxiaoke.volley:library:1.0.19'

writeFile()

    /**
     * 写文件
     */
    private void writeFile(String string) {
        checkExist();
        FileOutputStream fos = null;
        try {
            fos = openFileOutput(fileName, MODE_PRIVATE);
            byte[] bytes = string.getBytes();
            fos.write(bytes);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();

            }
        }
    }

readFile()

  /**
     * 读文件
     */

    private String readFile() {
        Log.d("zhanglinshu", "readFile " + readFile());
        FileInputStream fis = null;
        ByteArrayOutputStream stream = null;
        try {
            //获得输入流
            fis = openFileInput(fileName);
            stream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length = -1;
            while ((length = fis.read(buffer)) != -1) {
                //获取内存缓存区的数据
                stream.write(buffer, 0, length);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
                stream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        return stream.toString();
    }

上述就是一个简单的文件缓存策略,大家可以根据自己的需求做相应的调整和优化
下面给出我的布局文件
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <Button
        android:id="@+id/id_request_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:background="@drawable/btn_bg"
        android:text="请求数据"
        android:textColor="@android:color/white" />


    <TextView
        android:id="@+id/id_text_get"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:textSize="16sp" />

    <Button
        android:id="@+id/id_read_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:background="@drawable/btn_bg"
        android:text="读取数据"
        android:textColor="@android:color/white" />


    <TextView
        android:id="@+id/id_text_show"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:textSize="16sp" />


</LinearLayout>

btn_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="48dp" />

    <solid android:color="#FF6A4C" />

    <size
        android:width="48dp"
        android:height="48dp" />
</shape>
具体的效果界面看的不是太理想,需要大家自行测试,源码会在文章末尾出现。

数据库缓存

数据库缓存和文件缓存的思路一致,只不过讲文件读写替换为了数据库的增删查改
这里不再赘述过多。
数据库我这里采用的是Afinal
Afinal的用法很简单,关键是那个实体类,记得不要忘记setter getter方法,他是Afinal数据库的关键
具体使用代码中有体现,很简单很好用对于数据结构不太复杂的建议你去用Afinal
下面给出代码

public class DataBaseCacheActivity extends AppCompatActivity implements View.OnClickListener {
    RequestQueue mQueue;
    private TextView textGet;
    private Button buttonGet;
    private Button buttonShow;
    FinalDb db;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_data_base_cache);
        mQueue = Volley.newRequestQueue(this);
        db = FinalDb.create(this, "test.db", true);
        initViews();
        initData();
    }

    private void initData() {
        List<ResponseBean> lists = db.findAll(ResponseBean.class);
        ResponseBean responseBean = null;
        if (lists != null && lists.size() >= 0) {
            responseBean = lists.get(0);
        }
        Date date = new Date(responseBean.getDate());
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Log.d("zhanglinshu", "lastModify --" + df.format(date));

        Date currentDate = new Date();
        Log.d("zhanglinshu", "currentDate --" + df.format(currentDate));

        long time = currentDate.getTime() - date.getTime();
        //一分钟的time
        long min = time / (1000 * 60);
        Log.d("zhanglinshu", "diffMin" + min);

        if (min > 60) {
            request();
        } else {
            //直接用数据库中的数据
            textGet.setText("数据库中的数据 " + responseBean.toString());
        }
    }


    /**
     * 控件初始化
     */
    private void initViews() {
        buttonShow = (Button) findViewById(R.id.id_request_data_1);
        buttonShow.setOnClickListener(this);
        buttonGet = (Button) findViewById(R.id.id_read_data_1);
        buttonGet.setOnClickListener(this);

        textGet = (TextView) findViewById(R.id.id_text_show_1);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.id_request_data_1:
                request();
                break;
        }
    }

    private void request() {
        StringRequest stringRequest = new StringRequest(
                "http://gc.ditu.aliyun.com/geocoding?a=深圳市",
                new Response.Listener() {
                    @Override
                    public void onResponse(Object response) {
                        Log.d("zhanglinshu", "response --" + response.toString());
                        textGet.setText("网络请求的数据  " + response.toString());
                        Gson gson = new Gson();
                        ResponseBean responseBean = null;
                        Type type = new TypeToken<ResponseBean>() {
                        }.getType();
                        responseBean = gson.fromJson(response.toString(), type);
                        responseBean.setDate((new Date()).toString());
                        db.deleteAll(ResponseBean.class);
                        db.save(responseBean);
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {

                    }
                }
        );
        mQueue.add(stringRequest);
    }
}

最关键的实体类
responseBean.java

package com.example.lenovo.filecachetest;

import net.tsz.afinal.annotation.sqlite.Table;

import java.io.Serializable;

/**
 * Created by lenovo on 2016/6/30.
 */
@Table(name = "response")
public class ResponseBean implements Serializable {
    private int id;
    private String lon;
    private String level;
    private String alevel;
    private String address;
    private String lat;
    private String cityName;
    private String date;

    public ResponseBean() {
    }

    public String getLon() {
        return lon;
    }

    public void setLon(String lon) {
        this.lon = lon;
    }

    public String getLevel() {
        return level;
    }

    public void setLevel(String level) {
        this.level = level;
    }

    public String getAlevel() {
        return alevel;
    }

    public void setAlevel(String alevel) {
        this.alevel = alevel;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getLat() {
        return lat;
    }

    public void setLat(String lat) {
        this.lat = lat;
    }

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String cityName) {
        this.cityName = cityName;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }
}

至此两种数据缓存方案都介绍完了,
我个人还是比较中意文件缓存,他没有那么多的表去建立,省了好多麻烦事,哈哈,具体哪个方便,看你喽
源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值