webView nie wyświetla strony w trybie offline
Mam aplikację, która rejestruje się w GCM dla powiadomień push. Ten kod działa dobrze i nie jest źródłem mojego problemu. Po zarejestrowaniu aplikacja po prostu wyświetla stronę internetową w WebView. Jak dotąd wszystko dobrze działa, jeśli karta sieciowa urządzenia jest włączona.
Jeśli karta sieciowa jest wyłączona, przeglądarka internetowa powinna ładować się z pamięci podręcznej. Ta część nie działa. Po zapoznaniu się z poniższym samouczkiem pobrałem z niego części, aby zapisać strony w pamięci podręcznej zapisanej na karcie SD.
Jeśli uruchomię aplikację, zostanie wyświetlona strona internetowa. Jeśli następnie wyłączę kartę sieciową i odwiedzę witrynę, nie będzie ona wyświetlana. Sprawdzam katalog pamięci podręcznej na karcie SD i części strony zostały zapisane w pamięci podręcznej.
Dlaczego witryna nie ładuje się z pamięci podręcznej w trybie offline?
Czy możliwe jest przechowywanie stron internetowych w pamięci podręcznej, dopóki aplikacja nie zostanie odinstalowana lub Android nie zdecyduje inaczej?
Samouczek pokazuje, jak utworzyć katalog na sdcard i napisać do niego. Katalogi istnieją, ale widok sieci Web nie jest przywracany z tej pamięci podręcznej w trybie offline.
poniżej jest to, jak nazywam działanie główne, które wyświetla widok internetowy.
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();
.
Oto pełna MainActivity. Możesz zignorować pierwszą część, ponieważ jest to kod, który obsługuje elementy powiadomień wypychanych GCM, które działają. Muszę wiedzieć, dlaczego przeglądarka internetowa nie będzie ładowana z pamięci podręcznej.
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<Void, Void, Void> 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<Void, Void, Void>() {
@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", "> " + 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);
}
}
.
Oto kod z samouczka powyżej, który zapisuje na karcie SD.
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();
}
}
}
Myślałem, że dołączę trochę logowania. To jest rejestracja z nowej instalacji przy włączonej karcie sieciowej.
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] >> nScreenWidth = 720
01-29 14:13:18.390: D/webview(16904): [InitTabEffectPivot] >> 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): <CallBackProxy> 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®Id=APA91bFytZvnkmsEVHSnw5VRxIwfPRmPsSRVrgRNRa5ww3oqCfnLEZMaBBNtQtlZoK4RD4VQXfAfKDsQUa53PwQBH_rwuI_gEEdHQhxadpBVmJ7L3Dxy90Bzryg_aCWx20-cdZ32cbiiaGR3zUMjpqZizYQbnARN4w&name=hardcoded client name' to http://mobilewebexpert.co.uk/pushtest/register.php
01-29 14:13:19.500: E/URL(16904): > 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
.
To jest logowanie po wyłączeniu karty sieciowej.
01-29 14:16:57.080: E/RegisterActivity(16904): in onresume in registeractivity
01-29 14:17:03.490: D/webview(16904): [InitTabEffectPivot] >> nScreenWidth = 720
01-29 14:17:03.490: D/webview(16904): [InitTabEffectPivot] >> 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)
Jedną z rzeczy, które zauważyłem, może być nic, ale aktywność rejestracyjna to działanie, które wywołuje MainActivity. RegisterActivity ma tylko przycisk, który uruchamia Intent for MainActivity. Po wyłączeniu karty sieciowej muszę nacisnąć przycisk na registerAcivity, który wywołuje Cateate w MainActivity. To jest jak nowa instancja MainActivity ?? Czy może to mieć wpływ na buforowanie widoku internetowego. W efekcie onDestroy głównej działalności musiał zostać wywołany po drodze.
Również inna rzecz przykuła moją uwagę.
01-29 14:17:03.580: D/OpenGLRenderer(16904): Flushing caches (mode 0)