Note that there are some explanatory texts on larger screens.

plurals
  1. POwebView not displaying website when offline
    primarykey
    data
    text
    <p>I have an app that registers with GCM for push notifications. That code works fine and is not the source of my problem. Once registered, the app simply displays a website in a WebView. So far so good all this works if the device's network adapter is switched on.</p> <p>If the network adapter is switched off then the webview should load from cache. This part is not working. I have followed the tutorial below and taken parts from it in a bit to save the website pages in a cache saved on the sdcard.</p> <p>If i run the app the web site is displayed. If i then turn off the network adapter and visit the site, it will not display. I check the cache directory on the sdcard and parts of the site have been written to the cache.</p> <p>Why doesn't the site load from cache when off-line?</p> <p>Is it possible to keep the webpages it the cache until say the app is uninstalled or android decides otherwise?</p> <p><a href="http://www.devahead.com/blog/">tutorial</a></p> <p>The tutorial shows you how to create a directory on the sdcard and write to it. The directories do exist but the webview is not restored from this cache in offline mode.</p> <p>below is how i call the mainactivity that displays the webview.</p> <pre><code>Intent i = new Intent(getApplicationContext(), MainActivity.class); //i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); i.putExtra("name", "hardcoded client name"); i.putExtra("email", "hardcoded email"); startActivity(i); finish(); </code></pre> <p>.</p> <p>Here is the full MainActivity. You can ignore the first part as that's the code that handle the GCM push notification stuff, that all works. I need to know why the webview will not load from cache.</p> <pre><code> import static com.bmi.bmitestapp.CommonUtilities.DISPLAY_MESSAGE_ACTION; import static com.bmi.bmitestapp.CommonUtilities.EXTRA_MESSAGE; import static com.bmi.bmitestapp.CommonUtilities.SENDER_ID; import java.io.File; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.TextView; import android.widget.Toast; import android.webkit.*; import com.google.android.gcm.GCMRegistrar; public class MainActivity extends Activity { private static final String TAG = MainActivity.class.getSimpleName(); // label to display gcm messages TextView lblMessage; WebView webView; // Asyntask AsyncTask&lt;Void, Void, Void&gt; mRegisterTask; // Alert dialog manager AlertDialogManager alert = new AlertDialogManager(); // Connection detector ConnectionDetector cd; public static String name; public static String email; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.e(TAG, "in onCreate in mainactivity"); cd = new ConnectionDetector(getApplicationContext()); // Check if Internet present // if (!cd.isConnectingToInternet()) { // // Internet Connection is not present // alert.showAlertDialog(MainActivity.this, // "Internet Connection Error", // "Please connect to working Internet connection", false); // // stop executing code by return // return; // } // Getting name, email from intent Intent i = getIntent(); name = i.getStringExtra("name"); email = i.getStringExtra("email"); // Make sure the device has the proper dependencies. GCMRegistrar.checkDevice(this); // Make sure the manifest was properly set - comment out this line // while developing the app, then uncomment it when it's ready. GCMRegistrar.checkManifest(this); lblMessage = (TextView) findViewById(R.id.lblMessage); registerReceiver(mHandleMessageReceiver, new IntentFilter( DISPLAY_MESSAGE_ACTION)); // Get GCM registration id final String regId = GCMRegistrar.getRegistrationId(this); // Check if regid already presents if (regId.equals("")) { // Registration is not present, register now with GCM GCMRegistrar.register(this, SENDER_ID); } else { // Device is already registered on GCM if (GCMRegistrar.isRegisteredOnServer(this)) { // Skips registration. Toast.makeText(getApplicationContext(), "Already registered with GCM", Toast.LENGTH_LONG).show(); } else { // Try to register again, but not in the UI thread. // It's also necessary to cancel the thread onDestroy(), // hence the use of AsyncTask instead of a raw thread. final Context context = this; mRegisterTask = new AsyncTask&lt;Void, Void, Void&gt;() { @Override protected Void doInBackground(Void... params) { // Register on our server // On server creates a new user ServerUtilities.register(context, name, email, regId); return null; } @Override protected void onPostExecute(Void result) { mRegisterTask = null; } }; mRegisterTask.execute(null, null, null); } } webView = (WebView)findViewById(R.id.webView1); // Initialize the WebView webView.getSettings().setSupportZoom(true); webView.getSettings().setBuiltInZoomControls(true); webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY); webView.setScrollbarFadingEnabled(true); webView.getSettings().setLoadsImagesAutomatically(true); webView.getSettings().setDomStorageEnabled(true); // Set cache size to 8 mb by default. should be more than enough webView.getSettings().setAppCacheMaxSize(1024*1024*8); // This next one is crazy. It's the DEFAULT location for your app's cache // But it didn't work for me without this line. // UPDATE: no hardcoded path. Thanks to Kevin Hawkins String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath(); Log.e(TAG, "appCachePath = " + appCachePath); webView.getSettings().setAppCachePath(appCachePath); webView.getSettings().setAllowFileAccess(true); webView.getSettings().setAppCacheEnabled(true); // Load the URLs inside the WebView, not in the external web browser webView.setWebViewClient(new WebViewClient()); if (savedInstanceState == null) { if(isNetworkAvailable() == true){ Log.e(TAG, "we have a network connection"); webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT); webView.loadUrl("http://bmi.cubecore.co.uk"); } else { Log.e(TAG, "we don't have a network connection"); webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ONLY); webView.loadUrl("http://bmi.cubecore.co.uk"); } } } //end of oncreate private boolean isNetworkAvailable() { ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); return activeNetworkInfo != null; } /** * Receiving push messages * */ private final BroadcastReceiver mHandleMessageReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String newMessage = intent.getExtras().getString(EXTRA_MESSAGE); // Waking up mobile if it is sleeping WakeLocker.acquire(getApplicationContext()); /** * Take appropriate action on this message * depending upon your app requirement * For now i am just displaying it on the screen * */ // Showing received message lblMessage.append(newMessage + "\n"); Toast.makeText(getApplicationContext(), "New Message: " + newMessage, Toast.LENGTH_LONG).show(); // Releasing wake lock WakeLocker.release(); } }; @Override protected void onDestroy() { super.onDestroy(); // Clear the cache (this clears the WebViews cache for the entire application) //webView.clearCache(false); if (mRegisterTask != null) { mRegisterTask.cancel(true); } try { unregisterReceiver(mHandleMessageReceiver); GCMRegistrar.onDestroy(this); } catch (Exception e) { Log.e("UnRegister Receiver Error", "&gt; " + e.getMessage()); } } @Override public void onBackPressed() { super.onBackPressed(); } @Override protected void onResume() { super.onResume(); Log.e(TAG, "in onResume in mainactivity"); } @Override public File getCacheDir() { // NOTE: this method is used in Android 2.1 Log.e(TAG, "getcachedir"); return getApplicationContext().getCacheDir(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // Save the state of the WebView webView.saveState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); // Restore the state of the WebView webView.restoreState(savedInstanceState); } } </code></pre> <p>.</p> <p>Here's the code from the tutorial above that writes to the sdcard.</p> <pre><code>package com.bmi.bmitestapp; import java.io.File; import android.app.Application; import android.os.Environment; import android.util.Log; public class ApplicationExt extends Application { private static final String TAG = ApplicationExt.class.getSimpleName(); // NOTE: the content of this path will be deleted // when the application is uninstalled (Android 2.2 and higher) protected File extStorageAppBasePath; protected File extStorageAppCachePath; @Override public void onCreate() { super.onCreate(); Log.e(TAG, "inside appext"); // Check if the external storage is writeable if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { // Retrieve the base path for the application in the external storage File externalStorageDir = Environment.getExternalStorageDirectory(); if (externalStorageDir != null) { // {SD_PATH}/Android/data/com.devahead.androidwebviewcacheonsd extStorageAppBasePath = new File(externalStorageDir.getAbsolutePath() + File.separator + "Android" + File.separator + "data" + File.separator + getPackageName()); } if (extStorageAppBasePath != null) { // {SD_PATH}/Android/data/com.devahead.androidwebviewcacheonsd/cache extStorageAppCachePath = new File(extStorageAppBasePath.getAbsolutePath() + File.separator + "cache"); boolean isCachePathAvailable = true; if (!extStorageAppCachePath.exists()) { // Create the cache path on the external storage isCachePathAvailable = extStorageAppCachePath.mkdirs(); } if (!isCachePathAvailable) { // Unable to create the cache path extStorageAppCachePath = null; } } } } @Override public File getCacheDir() { // NOTE: this method is used in Android 2.2 and higher if (extStorageAppCachePath != null) { // Use the external storage for the cache Log.e(TAG, "extStorageAppCachePath = " + extStorageAppCachePath); return extStorageAppCachePath; } else { // /data/data/com.devahead.androidwebviewcacheonsd/cache return super.getCacheDir(); } } } </code></pre> <p>I thought i'd include a little logging. This is the logging from a fresh install with the network adapter ON.</p> <pre><code>01-29 14:13:10.220: D/dalvikvm(16904): Late-enabling CheckJNI 01-29 14:13:10.470: E/ApplicationExt(16904): inside appext 01-29 14:13:10.720: E/RegisterActivity(16904): in onresume in registeractivity 01-29 14:13:10.880: D/libEGL(16904): loaded /system/lib/egl/libEGL_tegra.so 01-29 14:13:11.000: D/libEGL(16904): loaded /system/lib/egl/libGLESv1_CM_tegra.so 01-29 14:13:11.030: D/libEGL(16904): loaded /system/lib/egl/libGLESv2_tegra.so 01-29 14:13:11.070: D/OpenGLRenderer(16904): Enabling debug mode 0 01-29 14:13:18.390: E/ApplicationExt(16904): extStorageAppCachePath = /mnt/sdcard/Android/data/com.bmi.bmitestapp/cache 01-29 14:13:18.390: D/webview(16904): [InitTabEffectPivot] &gt;&gt; nScreenWidth = 720 01-29 14:13:18.390: D/webview(16904): [InitTabEffectPivot] &gt;&gt; nScreenHeight = 1280 01-29 14:13:18.390: D/SqliteDatabaseCpp(16904): Registering sqlite logging func: /data/data/com.bmi.bmitestapp/databases/webview.db 01-29 14:13:18.390: D/SqliteDatabaseCpp(16904): DB info: open db, path = /data/data/com.bmi.bmitestapp/databases , key = sefraes, flag = 6, cannot stat file, errno = 2, message = No such file or directory 01-29 14:13:18.400: E/MainActivity(16904): in onCreate in mainactivity 01-29 14:13:18.400: D/SqliteDatabaseCpp(16904): DB info: path = /data/data/com.bmi.bmitestapp/databases , key = sefraes, handle: 0x1f9a858, type: w, r/w: (0,1), mode: delete, disk free size: 1276 M 01-29 14:13:18.400: D/GCMRegistrar(16904): resetting backoff for com.bmi.bmitestapp 01-29 14:13:18.420: V/GCMRegistrar(16904): Registering app com.bmi.bmitestapp of senders 598080744593 01-29 14:13:18.430: E/ApplicationExt(16904): extStorageAppCachePath = /mnt/sdcard/Android/data/com.bmi.bmitestapp/cache 01-29 14:13:18.430: E/MainActivity(16904): appCachePath = /mnt/sdcard/Android/data/com.bmi.bmitestapp/cache 01-29 14:13:18.430: E/MainActivity(16904): we have a network connection 01-29 14:13:18.430: E/MainActivity(16904): in onResume in mainactivity 01-29 14:13:18.490: W/webcore(16904): java.lang.Throwable: EventHub.removeMessages(int what = 107) is not supported before the WebViewCore is set up. 01-29 14:13:18.490: W/webcore(16904): at android.webkit.WebViewCore$EventHub.removeMessages(WebViewCore.java:1974) 01-29 14:13:18.490: W/webcore(16904): at android.webkit.WebViewCore$EventHub.access$9100(WebViewCore.java:1008) 01-29 14:13:18.490: W/webcore(16904): at android.webkit.WebViewCore.removeMessages(WebViewCore.java:2215) 01-29 14:13:18.490: W/webcore(16904): at android.webkit.WebView.sendOurVisibleRect(WebView.java:3285) 01-29 14:13:18.490: W/webcore(16904): at android.webkit.ZoomManager.setZoomScale(ZoomManager.java:772) 01-29 14:13:18.490: W/webcore(16904): at android.webkit.ZoomManager.access$1900(ZoomManager.java:59) 01-29 14:13:18.490: W/webcore(16904): at android.webkit.ZoomManager$PostScale.run(ZoomManager.java:1345) 01-29 14:13:18.490: W/webcore(16904): at android.os.Handler.handleCallback(Handler.java:608) 01-29 14:13:18.490: W/webcore(16904): at android.os.Handler.dispatchMessage(Handler.java:92) 01-29 14:13:18.490: W/webcore(16904): at android.os.Looper.loop(Looper.java:156) 01-29 14:13:18.490: W/webcore(16904): at android.app.ActivityThread.main(ActivityThread.java:5045) 01-29 14:13:18.490: W/webcore(16904): at java.lang.reflect.Method.invokeNative(Native Method) 01-29 14:13:18.490: W/webcore(16904): at java.lang.reflect.Method.invoke(Method.java:511) 01-29 14:13:18.490: W/webcore(16904): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 01-29 14:13:18.490: W/webcore(16904): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 01-29 14:13:18.490: W/webcore(16904): at dalvik.system.NativeStart.main(Native Method) 01-29 14:13:18.500: W/webcore(16904): java.lang.Throwable: EventHub.removeMessages(int what = 105) is not supported before the WebViewCore is set up. 01-29 14:13:18.500: W/webcore(16904): at android.webkit.WebViewCore$EventHub.removeMessages(WebViewCore.java:1974) 01-29 14:13:18.500: W/webcore(16904): at android.webkit.WebViewCore$EventHub.access$9100(WebViewCore.java:1008) 01-29 14:13:18.500: W/webcore(16904): at android.webkit.WebViewCore.removeMessages(WebViewCore.java:2215) 01-29 14:13:18.500: W/webcore(16904): at android.webkit.WebView.sendViewSizeZoom(WebView.java:3520) 01-29 14:13:18.500: W/webcore(16904): at android.webkit.ZoomManager.setZoomScale(ZoomManager.java:778) 01-29 14:13:18.500: W/webcore(16904): at android.webkit.ZoomManager.access$1900(ZoomManager.java:59) 01-29 14:13:18.500: W/webcore(16904): at android.webkit.ZoomManager$PostScale.run(ZoomManager.java:1345) 01-29 14:13:18.500: W/webcore(16904): at android.os.Handler.handleCallback(Handler.java:608) 01-29 14:13:18.500: W/webcore(16904): at android.os.Handler.dispatchMessage(Handler.java:92) 01-29 14:13:18.500: W/webcore(16904): at android.os.Looper.loop(Looper.java:156) 01-29 14:13:18.500: W/webcore(16904): at android.app.ActivityThread.main(ActivityThread.java:5045) 01-29 14:13:18.500: W/webcore(16904): at java.lang.reflect.Method.invokeNative(Native Method) 01-29 14:13:18.500: W/webcore(16904): at java.lang.reflect.Method.invoke(Method.java:511) 01-29 14:13:18.500: W/webcore(16904): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 01-29 14:13:18.500: W/webcore(16904): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 01-29 14:13:18.500: W/webcore(16904): at dalvik.system.NativeStart.main(Native Method) 01-29 14:13:18.530: D/OpenGLRenderer(16904): Flushing caches (mode 0) 01-29 14:13:18.570: I/SqliteDatabaseCpp(16904): sqlite returned: error code = 1, msg = no such table: CacheGroups 01-29 14:13:18.570: I/SqliteDatabaseCpp(16904): sqlite returned: error code = 1, msg = no such table: Caches 01-29 14:13:18.570: I/SqliteDatabaseCpp(16904): sqlite returned: error code = 1, msg = no such table: Origins 01-29 14:13:18.570: I/SqliteDatabaseCpp(16904): sqlite returned: error code = 1, msg = no such table: DeletedCacheResources 01-29 14:13:18.850: E/ApplicationExt(16904): extStorageAppCachePath = /mnt/sdcard/Android/data/com.bmi.bmitestapp/cache 01-29 14:13:19.330: I/PRIME(16904): &lt;CallBackProxy&gt; Send to WebViewClient. 01-29 14:13:19.370: V/GCMBroadcastReceiver(16904): onReceive: com.google.android.c2dm.intent.REGISTRATION 01-29 14:13:19.370: V/GCMBroadcastReceiver(16904): GCM IntentService class: com.bmi.bmitestapp.GCMIntentService 01-29 14:13:19.370: V/GCMBaseIntentService(16904): Acquiring wakelock 01-29 14:13:19.460: V/GCMBaseIntentService(16904): Intent service name: GCMIntentService-598080744593-1 01-29 14:13:19.470: E/GCMRegistrar(16904): internal error: retry receiver class not set yet 01-29 14:13:19.480: V/GCMRegistrar(16904): Registering receiver 01-29 14:13:19.480: D/GCMBaseIntentService(16904): handleRegistration: registrationId = APA91bFytZvnkmsEVHSnw5VRxIwfPRmPsSRVrgRNRa5ww3oqCfnLEZMaBBNtQtlZoK4RD4VQXfAfKDsQUa53PwQBH_rwuI_gEEdHQhxadpBVmJ7L3Dxy90Bzryg_aCWx20-cdZ32cbiiaGR3zUMjpqZizYQbnARN4w, error = null, unregistered = null 01-29 14:13:19.480: D/GCMRegistrar(16904): resetting backoff for com.bmi.bmitestapp 01-29 14:13:19.480: V/GCMRegistrar(16904): Saving regId on app version 1 01-29 14:13:19.490: I/GCMIntentService(16904): Device registered: regId = APA91bFytZvnkmsEVHSnw5VRxIwfPRmPsSRVrgRNRa5ww3oqCfnLEZMaBBNtQtlZoK4RD4VQXfAfKDsQUa53PwQBH_rwuI_gEEdHQhxadpBVmJ7L3Dxy90Bzryg_aCWx20-cdZ32cbiiaGR3zUMjpqZizYQbnARN4w 01-29 14:13:19.490: D/NAME(16904): hardcoded client name 01-29 14:13:19.490: I/bmi GCM(16904): registering device (regId = APA91bFytZvnkmsEVHSnw5VRxIwfPRmPsSRVrgRNRa5ww3oqCfnLEZMaBBNtQtlZoK4RD4VQXfAfKDsQUa53PwQBH_rwuI_gEEdHQhxadpBVmJ7L3Dxy90Bzryg_aCWx20-cdZ32cbiiaGR3zUMjpqZizYQbnARN4w) 01-29 14:13:19.490: D/bmi GCM(16904): Attempt #1 to register 01-29 14:13:19.500: V/bmi GCM(16904): Posting 'email=hardcoded email&amp;regId=APA91bFytZvnkmsEVHSnw5VRxIwfPRmPsSRVrgRNRa5ww3oqCfnLEZMaBBNtQtlZoK4RD4VQXfAfKDsQUa53PwQBH_rwuI_gEEdHQhxadpBVmJ7L3Dxy90Bzryg_aCWx20-cdZ32cbiiaGR3zUMjpqZizYQbnARN4w&amp;name=hardcoded client name' to http://mobilewebexpert.co.uk/pushtest/register.php 01-29 14:13:19.500: E/URL(16904): &gt; http://mobilewebexpert.co.uk/pushtest/register.php 01-29 14:13:20.610: V/GCMRegistrar(16904): Setting registeredOnServer status as true until 2013-02-05 14:13:20.617 01-29 14:13:20.620: V/GCMBaseIntentService(16904): Releasing wakelock 01-29 14:13:21.470: D/skia(16904): notifyPluginsOnFrameLoad not postponed 01-29 14:13:21.570: D/SQLiteDatabase(16904): Create pool connection 01-29 14:13:21.570: D/SqliteDatabaseCpp(16904): DB info: open db, path = /data/data/com.bmi.bmitestapp/databases , key = sefraes, flag = 1, file size = 12288 01-29 14:13:21.570: D/SqliteDatabaseCpp(16904): DB info: path = /data/data/com.bmi.bmitestapp/databases , key = sefraes, handle: 0x2797350, type: r, r/w: (1,1), mode: wal, disk free size: 1276 M </code></pre> <p>.</p> <p>This is the logging after i have turned off the network adapter.</p> <pre><code>01-29 14:16:57.080: E/RegisterActivity(16904): in onresume in registeractivity 01-29 14:17:03.490: D/webview(16904): [InitTabEffectPivot] &gt;&gt; nScreenWidth = 720 01-29 14:17:03.490: D/webview(16904): [InitTabEffectPivot] &gt;&gt; nScreenHeight = 1280 01-29 14:17:03.490: E/MainActivity(16904): in onCreate in mainactivity 01-29 14:17:03.490: V/GCMRegistrar(16904): Is registered on server: true 01-29 14:17:03.500: E/ApplicationExt(16904): extStorageAppCachePath = /mnt/sdcard/Android/data/com.bmi.bmitestapp/cache 01-29 14:17:03.500: E/MainActivity(16904): appCachePath = /mnt/sdcard/Android/data/com.bmi.bmitestapp/cache 01-29 14:17:03.500: E/MainActivity(16904): we don't have a network connection 01-29 14:17:03.500: E/MainActivity(16904): in onResume in mainactivity 01-29 14:17:03.500: D/chromium(16904): Unknown chromium error: -400 01-29 14:17:03.540: D/skia(16904): notifyPluginsOnFrameLoad not postponed 01-29 14:17:03.580: D/OpenGLRenderer(16904): Flushing caches (mode 0) </code></pre> <p>One thing i've noticed, it might be nothing, but registeractivity is the activity that calls MainActivity. RegisterActivity just has a button that fires an Intent for MainActivity. After the network adapter is off i have to press the button on registerAcivity, which calls onCreate in MainActivity. It is like it's a new instance of MainActivity?? Could this be effecting the caching of the webview. Effectively the onDestroy of the main activity must have been called along the way.</p> <p>Also another thing has caught my attention.</p> <pre><code>01-29 14:17:03.580: D/OpenGLRenderer(16904): Flushing caches (mode 0) </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload