プッシュ通知用に GCM に登録するアプリがあります。そのコードは正常に動作し、私の問題の原因ではありません。登録が完了すると、アプリは WebView に Web サイトを表示するだけです。これまでのところ、デバイスのネットワーク アダプタがオンになっている場合、これはすべてうまく機能します。
ネットワーク アダプタがオフになっている場合、WebView はキャッシュから読み込まれます。この部分は機能していません。以下のチュートリアルに従い、その一部を少し抜粋して、SD カードに保存されたキャッシュに Web サイトのページを保存しました。
アプリを実行すると、Web サイトが表示されます。その後、ネットワーク アダプターをオフにしてサイトにアクセスすると、表示されません。SD カードのキャッシュ ディレクトリを確認すると、サイトの一部がキャッシュに書き込まれています。
オフラインのときにサイトがキャッシュから読み込まれないのはなぜですか?
アプリがアンインストールされるか、Androidが別の方法で決定するまで、Webページをキャッシュに保持することは可能ですか?
このチュートリアルでは、sdcard にディレクトリを作成し、そこに書き込む方法を示します。ディレクトリは存在しますが、webview はオフライン モードでこのキャッシュから復元されません。
以下は、webview を表示するメインアクティビティを呼び出す方法です。
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();
.
完全な MainActivity は次のとおりです。最初の部分は、GCM プッシュ通知を処理するコードであり、すべて機能するため、無視できます。WebView がキャッシュから読み込まれない理由を知る必要があります。
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);
}
}
.
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();
}
}
}
少しロギングを含めると思いました。これは、ネットワーク アダプタをオンにして新規インストールした場合のログです。
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
.
これは、ネットワーク アダプターをオフにした後のログです。
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)
私が気付いたことの 1 つは、何でもないかもしれませんが、registeractivity は MainActivity を呼び出すアクティビティです。RegisterActivity には、MainActivity の Intent を起動するボタンがあります。ネットワーク アダプタがオフになった後、MainActivity で onCreate を呼び出す registerActivity のボタンを押す必要があります。MainActivity の新しいインスタンスのようなものですか?? これは、webview のキャッシュに影響を与えている可能性があります。事実上、メイン アクティビティの onDestroy が途中で呼び出されている必要があります。
また、もう一つ気になったことがあります。
01-29 14:17:03.580: D/OpenGLRenderer(16904): Flushing caches (mode 0)