如何从包含android中API响应的缓存中获取改造缓存数据

Posted

技术标签:

【中文标题】如何从包含android中API响应的缓存中获取改造缓存数据【英文标题】:How to fetch retrofit cache data from cache memory which contains API response in android 【发布时间】:2017-05-09 16:39:54 【问题描述】:

该类包含用于调用 API 的 APIClient 实例,但在获取缓存时存在一个问题。我想在设备未连接到网络时从缓存中获取数据。

private static Retrofit retrofit = null;
private static final String CACHE_CONTROL = "Cache-Control";

public static Retrofit getClient(Context context)

    if (retrofit==null) 
        retrofit = new Retrofit.Builder()
                .baseUrl(URLS.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(provideOkHttpClient(context))
                .build();
    
    return retrofit;


/**
 * Add Client for adding Authentication headers.
 * @return Retrofit
 */
public static Retrofit getAthenticationClient()

    if (retrofit==null) 
        retrofit = new Retrofit.Builder()
                .baseUrl(URLS.BASE_URL)
                .client(ApiIntercepters.AddAuthenticationHeader())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    
    return retrofit;


public static OkHttpClient provideOkHttpClient(Context context)

    return new OkHttpClient.Builder()
            .addNetworkInterceptor(provideCacheInterceptor())
            .cache( provideCache(context))
            .build();


private static Cache provideCache (Context context)

    Cache cache = null;
    try
    
        //setup cache
        File httpCacheDirectory = new File(context.getCacheDir(), "responses");
        int cacheSize = 10 * 1024 * 1024; // 10 MiB
        cache = new Cache(httpCacheDirectory, cacheSize);

    
    catch (Exception e)
    
        Log.e( "Injector :-> ", "Could not create Cache!" );
    
    return cache;


public static Interceptor provideCacheInterceptor ()

    return new Interceptor()
    
        @Override
        public Response intercept (Chain chain) throws IOException
        
            Response originalResponse = chain.proceed(chain.request());
            if (RetrofitDemoApp.hasNetwork()) 
                int maxAge = 60; // read from cache for 1 minute
                return originalResponse.newBuilder()
                        .header("Cache-Control", "public, max-age=" + maxAge)
                        .build();
             else 
                int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
                return originalResponse.newBuilder()
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                        .build();
            
        
    ;

【问题讨论】:

为什么在检索数据时不保存数据以备后用? 您能告诉我如何在改造中使用高速缓存保存数据吗?我是 Retrofit 缓存管理的新手。 看看这里***.com/questions/23429046/… 谢谢!!!我正在获得解决方案,但在获得响应时,我在离线模式下收到 504 错误:@Override public void onResponse(Call call, Response response) parameters.DismissLoader();尝试 if (response.code() == 200) GetRegisterResponse(response.body().string()); catch (IOException e) e.printStackTrace(); 表示网关超时。我的猜测是你在你的 URL 中犯了一个错误,遗漏了一个符号或空格或一些微不足道的东西。当您调试 onResponse 方法时,请查看响应对象并复制 URL。在网络浏览器中测试上述 URL,看看它是否正常工作。即你得到你想要的回应。 【参考方案1】:

这是一个我也想要的解决方案,我实现了它,它可以正常工作,将数据存储在缓存中,然后获取数据。

检查下面的代码

CacheManager.java

 public class CacheManager 

    private Context context;
    private static final String TAG = CacheManager.class.getSimpleName();

    public CacheManager(Context context) 
        this.context = context;
    

    public void writeJson(Object object, Type type, String fileName) 
        File file = new File(context.getCacheDir(), fileName);
        OutputStream outputStream = null;
        Gson gson = new GsonBuilder().enableComplexMapKeySerialization().setPrettyPrinting().create();
        try 
            outputStream = new FileOutputStream(file);
            BufferedWriter bufferedWriter;
            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) 
                bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream,
                        StandardCharsets.UTF_8));
             else 
                bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8"));
            

            gson.toJson(object, type, bufferedWriter);
            bufferedWriter.close();

         catch (FileNotFoundException e) 
            Log.i(TAG,""+e);
         catch (IOException e) 
            Log.i(TAG,""+e);
         finally 
            if (outputStream != null) 
                try 
                    outputStream.flush();
                    outputStream.close();
                 catch (IOException e) 
                    Log.i(TAG,""+e);
                
            
        

    


    public Object readJson(Type type, String fileName) 
        Object jsonData = null;

        File file = new File(context.getCacheDir(), fileName);
        InputStream inputStream = null;
        Gson gson = new GsonBuilder().enableComplexMapKeySerialization().setPrettyPrinting().create();
        try 
            inputStream = new FileInputStream(file);
            InputStreamReader streamReader;
            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) 
                streamReader = new InputStreamReader(inputStream,
                        StandardCharsets.UTF_8);
             else 
                streamReader = new InputStreamReader(inputStream, "UTF-8");
            

            jsonData = gson.fromJson(streamReader, type);
            streamReader.close();

         catch (FileNotFoundException e) 
            e.printStackTrace();
            if (DEBUG) Log.e(TAG, "loadJson, FileNotFoundException e: '" + e + "'");
         catch (IOException e) 
            e.printStackTrace();
            if (DEBUG) Log.e(TAG, "loadJson, IOException e: '" + e + "'");
         finally 
            if (inputStream != null) 
                try 
                    inputStream.close();
                 catch (IOException e) 
                    if (DEBUG) Log.e(TAG, "loadJson, finally, e: '" + e + "'");
                
            
        
        return jsonData;
    



用于在缓存中存储和获取数据

MainActivity.java


if (checkInternetConnection(getContext())) 
            progressBar.setVisibility(View.VISIBLE);
            Api mApiService = RetrofitClient.getClient(Api.BASE_URL).create(Api.class);

            Call<ApiModel> call = mApiService.getCountry();

            call.enqueue(new Callback<ApiModel>() 
                @Override
                public void onResponse(Call<ApiModel> call, Response<ApiModel> response) 
                    countryList = response.body();

                    countryListData = countryList.data;


                    CacheManager cacheManager = new CacheManager(MainActivity.this);

                    //store data in cache
                    Type type = new TypeToken<ApiModel>() 
                    .getType();
                    cacheManager.writeJson(response.body(), type, "latest.json");

                    adapter = new MyListAdapter(getApplicationContext(), countryListData);
                    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
                    recyclerView.setHasFixedSize(true);
                    recyclerView.setLayoutManager(linearLayoutManager);
                    recyclerView.setAdapter(adapter);

                    progressBar.setVisibility(View.GONE);
                    Log.i(TAG, "onResponse: SuccessFull");

                

                @Override
                public void onFailure(Call<ApiModel> call, Throwable t) 
                    Toast.makeText(getApplicationContext(), "An error has occurred", Toast.LENGTH_LONG).show();

                    //api failed to return data due to network problem or something else, display data from cache file
                    Type type = new TypeToken<ApiModel>() 
                    .getType();
                    countryList = (ApiModel) cacheManager.readJson(type, "latest.json");


                    Log.i(TAG, "onFailure: " + t);
                    progressBar.setVisibility(View.GONE);
                

            );
         else 

            //No internet connected then fetch data if exists
            Type type = new TypeToken<ApiModel>() 
            .getType();
            countryList = (ApiModel) cacheManager.readJson(type, "latest.json");
            System.out.println("cacheData" + countryList.data.get(1));
            if (countryList != null) 
                countryListData = countryList.data;
                adapter = new MyListAdapter(getApplicationContext(), countryListData);
                LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
                recyclerView.setHasFixedSize(true);
                recyclerView.setLayoutManager(linearLayoutManager);
                recyclerView.setAdapter(adapter);
            
        

【讨论】:

以上是关于如何从包含android中API响应的缓存中获取改造缓存数据的主要内容,如果未能解决你的问题,请参考以下文章

使用OkHttp拦截器和Retrofit进行缓存

使用OkHttp拦截器和Retrofit进行缓存

如何从服务器获取和设置 android 中的 API(从服务器获取 int 值)?如何绑定和实现这个

如何在 android 中获取 XML 响应字符串以及 Kotlin 数据模型?

数据库或文件系统中的缓存API响应

如何从片段中的 JSON 响应中的对象获取数据