-1

So here's the problem, I'm trying to implement caching for my app and I followed this tutorial Android: Cache network requests for offline access with Retrofit2 and OkHTTP3 to do it. Sadly, cached responses are not showing up when there is no internet connection, instead the

Toast.makeText(getContext(), getContext().getResources().getString(R.string.error_failed_to_load_posts), Toast.LENGTH_SHORT).show();

gets executed. So can anyone tell me what I am doing wrong? Or guide me to get an answer to this problem? I'll post additional info if needed if you ask me. Below is the entire code of the specific function.

private void loadHomeLoggedPosts(String uid) {
    progressBar.setVisibility(View.VISIBLE);

    OkHttpClient client = new OkHttpClient
            .Builder()
            .cache(new Cache(getContext().getCacheDir(), 10 * 1024 * 1024)) // 10 MB
            .addInterceptor(new Interceptor() {
                @Override public okhttp3.Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();
                    if (Utils.isNetworkAvailable(getContext())) {
                        request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build();
                    } else {
                        request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7).build();
                    }
                    return chain.proceed(request);
                }
            })
            .build();

    Gson gson = new GsonBuilder()
            .setLenient()
            .create();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(getResources().getString(R.string.app_base_url))
            .client(client)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build();

    RequestInterface request = retrofit.create(RequestInterface.class);
    Call<Classes> call = request.homeLoggedPosts(uid);
    call.enqueue(new Callback<Classes>() {
        @Override
        public void onResponse(Call<Classes> call, Response<Classes> response) {
            Classes jsonResponse = response.body();
            if (jsonResponse!=null) {
                homePosts = jsonResponse.getHomePosts();
                homeLoggedInAdapter = new HomeLoggedInAdapter(homePosts);
                recyclerView.setAdapter(homeLoggedInAdapter);
                homeLoggedInAdapter.notifyDataSetChanged();
            }
            else {
                Toast.makeText(getContext(), getContext().getResources().getString(R.string.error_failed_to_load_posts), Toast.LENGTH_SHORT).show();
            }
            progressBar.setVisibility(View.INVISIBLE);
        }

        @Override
        public void onFailure(Call<Classes> call, Throwable t) {
            progressBar.setVisibility(View.INVISIBLE);
            Toast.makeText(getContext(), getContext().getResources().getString(R.string.error_failed_to_load_posts), Toast.LENGTH_SHORT).show();
            Log.d("ERROR", t.getMessage());
        }
    });
}

EDIT: I've two functions in the same fragment calling as follows:

if (FirebaseAuth.getInstance().getCurrentUser()!=null) {
        loadHomeLoggedPosts(FirebaseAuth.getInstance().getCurrentUser().getUid());
    }
    else {
        loadHomeNotLoggedPosts();
    }

I've posted the code for loadHomeLoggedPosts above. loadHomeNotLoggedPosts is the same with he difference being this section only:

RequestInterface request = retrofit.create(RequestInterface.class);
Call<Classes> call = request.homeNotLoggedPosts();

Farmaan Elahis' answer works for loadHomeNotLoggedPosts , but not for loadHomeLoggedPosts. Any help is highly appreciated. Thanks!

Okhttp log: I've edited out some portions as the log was too big to post here fully.

Fist Startup:

08-27 18:35:29.426 32673-32673/com.appsoflife.microstories I/System.out: Cache CREATED!
08-27 18:35:29.452 32673-2498/com.appsoflife.microstories D/OkHttp: --> POST http://my_web_site.com/jacob/micro_stories/queries/home_logged.php http/1.1
08-27 18:35:29.452 32673-2498/com.appsoflife.microstories D/OkHttp: Content-Type: application/x-www-form-urlencoded
08-27 18:35:29.452 32673-2498/com.appsoflife.microstories D/OkHttp: Content-Length: 32
08-27 18:35:29.452 32673-2498/com.appsoflife.microstories D/OkHttp: uid=MY_USER_ID
08-27 18:35:29.452 32673-2498/com.appsoflife.microstories D/OkHttp: --> END POST (32-byte body)
08-27 18:35:29.460 32673-2363/com.appsoflife.microstories D/AppTracker: App Event: start
08-27 18:35:29.502 32673-32673/com.appsoflife.microstories E/RecyclerView: No adapter attached; skipping layout
08-27 18:35:29.592 32673-32679/com.appsoflife.microstories I/art: Do partial code cache collection, code=27KB, data=30KB
08-27 18:35:29.593 32673-32679/com.appsoflife.microstories I/art: After code cache collection, code=26KB, data=29KB
08-27 18:35:29.593 32673-32679/com.appsoflife.microstories I/art: Increasing code cache capacity to 128KB
08-27 18:35:29.614 32673-32673/com.appsoflife.microstories W/PropertyValuesHolder: Method set() with type float not found on target class class me.zhanghai.android.materialprogressbar.IndeterminateHorizontalProgressDrawable$RectTransformX
08-27 18:35:33.088 32673-2498/com.appsoflife.microstories D/OkHttp: <-- 200 OK http://my_web_site.com/jacob/micro_stories/queries/home_logged.php (3634ms)
08-27 18:35:33.088 32673-2498/com.appsoflife.microstories D/OkHttp: Date: Sun, 27 Aug 2017 13:05:29 GMT
08-27 18:35:33.088 32673-2498/com.appsoflife.microstories D/OkHttp: Server: Apache/2.4.25
08-27 18:35:33.088 32673-2498/com.appsoflife.microstories D/OkHttp: X-Powered-By: PHP/5.6.30
08-27 18:35:33.088 32673-2498/com.appsoflife.microstories D/OkHttp: Vary: Accept-Encoding,User-Agent
08-27 18:35:33.088 32673-2498/com.appsoflife.microstories D/OkHttp: Keep-Alive: timeout=5
08-27 18:35:33.088 32673-2498/com.appsoflife.microstories D/OkHttp: Connection: Keep-Alive
08-27 18:35:33.088 32673-2498/com.appsoflife.microstories D/OkHttp: Transfer-Encoding: chunked
08-27 18:35:33.088 32673-2498/com.appsoflife.microstories D/OkHttp: Content-Type: text/html; charset=UTF-8
08-27 18:35:33.088 32673-2498/com.appsoflife.microstories D/OkHttp: Cache-Control: max-age=120
08-27 18:35:37.890 32673-2653/com.appsoflife.microstories I/FirebaseCrash: Sending crashes
08-27 18:35:41.664 32673-32695/com.appsoflife.microstories W/art: Suspending all threads took: 9.530ms
08-27 18:35:41.681 32673-2498/com.appsoflife.microstories D/OkHttp: 

MY_JSON_OUTPUT_TOO_LONG_TO_POST

08-27 18:35:41.721 32673-2498/com.appsoflife.microstories D/OkHttp: <-- END HTTP (3297402-byte body)

SECOND TIME:

08-27 18:37:35.904 5089-5089/com.appsoflife.microstories I/System.out: Cache CREATED!
08-27 18:37:35.922 5089-5190/com.appsoflife.microstories D/OkHttp: --> POST http://my_web_site.com/jacob/micro_stories/queries/home_logged.php http/1.1
08-27 18:37:35.922 5089-5190/com.appsoflife.microstories D/OkHttp: Content-Type: application/x-www-form-urlencoded
08-27 18:37:35.922 5089-5190/com.appsoflife.microstories D/OkHttp: Content-Length: 32
08-27 18:37:35.922 5089-5190/com.appsoflife.microstories D/OkHttp: uid=MY_USER_ID
08-27 18:37:35.922 5089-5190/com.appsoflife.microstories D/OkHttp: --> END POST (32-byte body)
08-27 18:37:35.934 5089-5135/com.appsoflife.microstories D/AppTracker: App Event: start
08-27 18:37:35.979 5089-5089/com.appsoflife.microstories E/RecyclerView: No adapter attached; skipping layout
08-27 18:37:36.058 5089-5094/com.appsoflife.microstories I/art: Do partial code cache collection, code=26KB, data=30KB
08-27 18:37:36.058 5089-5094/com.appsoflife.microstories I/art: After code cache collection, code=25KB, data=29KB
08-27 18:37:36.058 5089-5094/com.appsoflife.microstories I/art: Increasing code cache capacity to 128KB
08-27 18:37:36.083 5089-5089/com.appsoflife.microstories W/PropertyValuesHolder: Method set() with type float not found on target class class me.zhanghai.android.materialprogressbar.IndeterminateHorizontalProgressDrawable$RectTransformX
08-27 18:37:39.860 5089-5190/com.appsoflife.microstories D/OkHttp: <-- 200 OK http://my_web_site.com/jacob/micro_stories/queries/home_logged.php (3937ms)
08-27 18:37:39.860 5089-5190/com.appsoflife.microstories D/OkHttp: Date: Sun, 27 Aug 2017 13:07:36 GMT
08-27 18:37:39.860 5089-5190/com.appsoflife.microstories D/OkHttp: Server: Apache/2.4.25
08-27 18:37:39.860 5089-5190/com.appsoflife.microstories D/OkHttp: X-Powered-By: PHP/5.6.30
08-27 18:37:39.860 5089-5190/com.appsoflife.microstories D/OkHttp: Vary: Accept-Encoding,User-Agent
08-27 18:37:39.860 5089-5190/com.appsoflife.microstories D/OkHttp: Keep-Alive: timeout=5
08-27 18:37:39.860 5089-5190/com.appsoflife.microstories D/OkHttp: Connection: Keep-Alive
08-27 18:37:39.860 5089-5190/com.appsoflife.microstories D/OkHttp: Transfer-Encoding: chunked
08-27 18:37:39.860 5089-5190/com.appsoflife.microstories D/OkHttp: Content-Type: text/html; charset=UTF-8
08-27 18:37:39.860 5089-5190/com.appsoflife.microstories D/OkHttp: Cache-Control: max-age=120
08-27 18:37:49.883 5089-5426/com.appsoflife.microstories I/FirebaseCrash: Sending crashes
08-27 18:37:51.044 5089-5190/com.appsoflife.microstories D/OkHttp: 

JSON_OUTPUT_TOO_LONG_TO_POST

read: unexpected EOF!
Jacob Celestine
  • 1,758
  • 13
  • 23

2 Answers2

3

You can use the below code to build you okhttp client which can have both online cache which is valid for 2 minutes as well as offline cache whcih is valid for 7 days.

PS: You need to consider opening the app for the first time with internet connected show that it doesn't throw exception

  private static OkHttpClient provideOkHttpClient () 
        { 
            return new OkHttpClient.Builder() 
                    .addInterceptor( provideOfflineCacheInterceptor() ) 
                    .addNetworkInterceptor( provideCacheInterceptor() ) 
                    .cache( provideCache() ) 
                    .build(); 
        } 

        private static Cache provideCache ()
        { 
            Cache cache = null;
            try 
            { 
                cache = new Cache( new File( getApplicationContext().getInstance().getCacheDir(), "http-cache" ),
                        10 * 1024 * 1024 ); // 10 MB 
            } 
            catch (Exception e)
            { 
                Timber.e( e, "Could not create Cache!" );
            } 
            return cache;
        } 

        public static Interceptor provideCacheInterceptor () 
        { 
            return new Interceptor() 
            { 
                @Override 
                public Response intercept (Chain chain) throws IOException
                { 
                    Response response = chain.proceed( chain.request() );

                    // re-write response header to force use of cache 
                    CacheControl cacheControl = new CacheControl.Builder()
                            .maxAge( 2, TimeUnit.MINUTES )
                            .build(); 

                    return response.newBuilder()
                            .header( CACHE_CONTROL, cacheControl.toString() )
                            .build(); 
                } 
            }; 
        } 

        public static Interceptor provideOfflineCacheInterceptor () 
        { 
            return new Interceptor() 
            { 
                @Override 
                public Response intercept (Chain chain) throws IOException
                { 
                    Request request = chain.request();

                    if ( !AndroidUtils.isNetworkAvailable() ) 
                    { 
                        CacheControl cacheControl = new CacheControl.Builder()
                                .maxStale( 7, TimeUnit.DAYS )
                                .build(); 

                        request = request.newBuilder() 
                                .cacheControl( cacheControl )
                                .build(); 
                    } 

                    return chain.proceed( request );
                } 
            }; 
        }
Farmaan Elahi
  • 1,620
  • 16
  • 18
  • Thanks it worked! Funny story, I had already tried that code from [SsKarthi/Retrofit2.0-with-Cache](https://github.com/SsKarthi/Retrofit2.0-with-Cache) , but at that time it didn't work, now it did :D – Jacob Celestine Aug 27 '17 at 11:31
  • 1
    This code is from caster.io android tutorial.They are really good.You should check them out – Farmaan Elahi Aug 27 '17 at 11:59
  • The answer worked when I wasn't passing anything using retrofit, but when I do, it doesn't work. I says failed to resolve the url when offline and loads data again when online. Help? – Jacob Celestine Aug 27 '17 at 12:11
  • Can you elaborate your issue a bit?I think you are using different base URL.If the base URL change on the request, it will not find anything related to cache as caches are referenced using base URL – Farmaan Elahi Aug 27 '17 at 12:17
  • I've edited the question with more info. Please do check it. – Jacob Celestine Aug 27 '17 at 12:35
  • Is it happening just the first time we the app is installed and opened or every time?It would be better if you post okhttp log cat.You can find logger for okhttp online. – Farmaan Elahi Aug 27 '17 at 12:42
  • I've added the log, please check it. – Jacob Celestine Aug 27 '17 at 13:24
  • I think the issue is with your server.The response header should be "Connection":"close" instead of "Keep-Alive – Farmaan Elahi Aug 27 '17 at 13:49
  • okay, thank you so much for the tip i'll see what I can do about this :) – Jacob Celestine Aug 27 '17 at 13:57
0

POST calls does not get cached in Retrofit. Its in documentation

Abhinav Pawar
  • 421
  • 3
  • 12