Android WebView 未从缓存中加载第二页
Posted
技术标签:
【中文标题】Android WebView 未从缓存中加载第二页【英文标题】:Android WebView not loading second page from cache 【发布时间】:2013-02-01 05:52:06 【问题描述】:我有一个仅包含网站的 android 应用。我希望该应用缓存网站页面以供离线使用。
我正在做一个简单的测试以查看缓存是否正常工作,但不幸的是,它无法加载我之前在离线模式下以在线模式加载的页面。为了让事情更清楚,我在在线模式下加载了以下 2 个页面。
webView.loadUrl("http://www.bmimobile.co.uk/why-bmi.php", getHeaders());
webView.loadUrl("http://www.bmimobile.co.uk/", getHeaders());
.
我希望“why-bmi.php”页面以及后续页面http://www.bmimobile.co.uk/ 被加载到缓存中。后一页上有一个指向第一页的链接。如果我随后退出应用程序并关闭网络适配器,然后返回应用程序,“http://www.bmimobile.co.uk/”页面会显示,但是当我单击“why-bmi”链接时,该页面不会显示。我简短的吐司消息显示“错误加载页面”。
谁能告诉我为什么 webview 没有缓存加载的页面供以后离线使用?
这是主要活动,我已经扩展了定义 appcachepath 的 Application 对象。
提前致谢
马特
package uk.bmi.mobile;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import android.app.Activity;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.util.Log;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class MainActivity extends Activity
private WebView webView;
private static final String TAG = MainActivity.class.getSimpleName();
ApplicationExt bmiAppObj;
//instruct server to set it's headers to make resources cachable
private Map<String, String> getHeaders()
Map<String, String> headers = new HashMap<String, String>();
headers.put("IS_ALEX_APP", "1");
return headers;
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e(TAG, "in onCreate in mainactivity");
//end of oncreate
private boolean isNetworkAvailable()
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null;
@Override
protected void onResume()
super.onResume();
Log.e(TAG, "in onResume in mainactivity");
webView = (WebView)findViewById(R.id.webView1);
bmiAppObj = (ApplicationExt)getApplication();
if(isNetworkAvailable() == true)
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);
webView.getSettings().setAppCacheEnabled(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().setjavascriptEnabled(true);
// Load the URLs inside the WebView, not in the external web browser
webView.setWebViewClient(new WebViewClient());
webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
webView.loadUrl("http://www.bmimobile.co.uk/why-bmi.php", getHeaders());
webView.loadUrl("http://www.bmimobile.co.uk/", getHeaders());
else
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);
webView.getSettings().setAppCacheEnabled(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().setJavaScriptEnabled(true);
// Load the URLs inside the WebView, not in the external web browser
webView.setWebViewClient(new WebViewClient());
webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ONLY);
webView.loadUrl("http://www.bmimobile.co.uk/", getHeaders());
@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);
//end of mainActivity
.
package uk.bmi.mobile;
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;
Webservice webservice;
BmiDB bmiDb;
@Override
public void onCreate()
super.onCreate();
Log.e(TAG, "inside appext");
webservice = new Webservice(this);
bmiDb = new BmiDB(this);
// 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;
//end of onCreate
@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();
.这是应用程序首次以在线模式加载时的日志记录
02-16 08:38:52.744: I/NONPRIME(8871): <CallBackProxy> Send to WebViewClient.
02-16 08:38:56.314: D/skia(8871): ----- started: [1 325] http://www.bmimobile.co.uk/images/mobile/bg-index.png
02-16 08:38:56.499: D/skia(8871): ----- started: [1 64] http://www.bmimobile.co.uk/CubeCore/modules/cubeMobile/images/bg-black-bar.png
02-16 08:38:56.509: D/skia(8871): ----- started: [26 20] http://www.bmimobile.co.uk/images/mobile/home-icon.png
02-16 08:38:56.529: D/skia(8871): ----- started: [275 189] http://www.bmimobile.co.uk/images/mobile/home-img.png
02-16 08:38:56.549: D/skia(8871): ----- started: [320 450] http://www.bmimobile.co.uk/images/mobile/welcome/bg-welcome.jpg
02-16 08:38:56.554: D/skia(8871): ----- started: [270 38] http://www.bmimobile.co.uk/images/mobile/welcome/next.png
02-16 08:38:56.584: D/skia(8871): ----- started: [16 17] http://www.bmimobile.co.uk/images/mobile/why.png
02-16 08:38:56.584: D/skia(8871): ----- started: [18 17] http://www.bmimobile.co.uk/images/mobile/services.png
02-16 08:38:56.584: D/skia(8871): ----- started: [20 15] http://www.bmimobile.co.uk/images/mobile/visit.png
02-16 08:38:56.589: D/skia(8871): ----- started: [20 15] http://www.bmimobile.co.uk/images/mobile/consultants.png
02-16 08:38:56.589: D/skia(8871): ----- started: [13 19] http://www.bmimobile.co.uk/images/mobile/contact.png
.
这是我退出应用程序关闭网络适配器然后以离线模式回到应用程序时的日志记录。
02-16 08:41:37.799: E/MainActivity(8871): in onResume in mainactivity
02-16 08:41:37.804: E/ApplicationExt(8871): extStorageAppCachePath = /storage/sdcard0/Android/data/uk.bmi.mobile/cache
02-16 08:41:37.804: E/MainActivity(8871): appCachePath = /storage/sdcard0/Android/data/uk.bmi.mobile/cache
02-16 08:41:37.834: W/dalvikvm(8871): disableGcForExternalAlloc: false
[编辑1] 实际上,在仔细检查日志记录时,它似乎在以在线模式加载时发生了变化。下面是 omline 模式下的 logcat。缓存存储似乎有问题。
02-19 15:16:10.497: E/ApplicationExt(5467): inside appext
02-19 15:16:10.687: E/ApplicationExt(5467): extStorageAppCachePath = /storage/sdcard0/Android/data/uk.bmi.mobile/cache
02-19 15:16:10.722: E/MainActivity(5467): in onCreate in mainactivity
02-19 15:16:10.727: E/MainActivity(5467): in onResume in mainactivity
02-19 15:16:10.737: E/ApplicationExt(5467): extStorageAppCachePath = /storage/sdcard0/Android/data/uk.bmi.mobile/cache
02-19 15:16:10.737: E/MainActivity(5467): appCachePath = /storage/sdcard0/Android/data/uk.bmi.mobile/cache
02-19 15:16:10.792: E/(5467): file /data/data/com.nvidia.NvCPLSvc/files/driverlist.txt: not found!
02-19 15:16:10.792: I/(5467): Attempting to load EGL implementation /system/lib//egl/libEGL_tegra_impl
02-19 15:16:10.807: I/(5467): Loaded EGL implementation /system/lib//egl/libEGL_tegra_impl
02-19 15:16:10.842: I/(5467): Loading GLESv2 implementation /system/lib//egl/libGLESv2_tegra_impl
02-19 15:16:10.882: E/SQLiteLog(5467): (1) no such table: CacheGroups
02-19 15:16:10.882: D/WebKit(5467): ERROR:
02-19 15:16:10.882: D/WebKit(5467): Application Cache Storage: failed to execute statement "DELETE FROM CacheGroups" error "no such table: CacheGroups"
02-19 15:16:10.882: D/WebKit(5467): external/webkit/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp(558) : bool WebCore::ApplicationCacheStorage::executeSQLCommand(const WTF::String&)
02-19 15:16:10.882: E/SQLiteLog(5467): (1) no such table: Caches
02-19 15:16:10.882: D/WebKit(5467): ERROR:
02-19 15:16:10.882: D/WebKit(5467): Application Cache Storage: failed to execute statement "DELETE FROM Caches" error "no such table: Caches"
02-19 15:16:10.882: D/WebKit(5467): external/webkit/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp(558) : bool WebCore::ApplicationCacheStorage::executeSQLCommand(const WTF::String&)
02-19 15:16:10.882: E/SQLiteLog(5467): (1) no such table: Origins
02-19 15:16:10.882: D/WebKit(5467): ERROR:
02-19 15:16:10.882: D/WebKit(5467): Application Cache Storage: failed to execute statement "DELETE FROM Origins" error "no such table: Origins"
02-19 15:16:10.882: D/WebKit(5467): external/webkit/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp(558) : bool WebCore::ApplicationCacheStorage::executeSQLCommand(const WTF::String&)
02-19 15:16:10.882: E/SQLiteLog(5467): (1) no such table: DeletedCacheResources
02-19 15:16:10.992: E/ApplicationExt(5467): extStorageAppCachePath = /storage/sdcard0/Android/data/uk.bmi.mobile/cache
02-19 15:16:11.022: W/dalvikvm(5467): disableGcForExternalAlloc: false
02-19 15:16:13.787: I/NONPRIME(5467): <CallBackProxy> Send to WebViewClient.
02-19 15:16:21.427: D/skia(5467): ----- started: [1 325] http://www.bmimobile.co.uk/images/mobile/bg-index.png
02-19 15:16:21.517: D/skia(5467): ----- started: [1 64] http://www.bmimobile.co.uk/CubeCore/modules/cubeMobile/images/bg-black-bar.png
02-19 15:16:21.542: D/skia(5467): ----- started: [26 20] http://www.bmimobile.co.uk/images/mobile/home-icon.png
02-19 15:16:21.577: D/skia(5467): ----- started: [275 189] http://www.bmimobile.co.uk/images/mobile/home-img.png
02-19 15:16:21.597: D/skia(5467): ----- started: [270 38] http://www.bmimobile.co.uk/images/mobile/welcome/next.png
02-19 15:16:21.677: D/skia(5467): ----- started: [16 17] http://www.bmimobile.co.uk/images/mobile/why.png
02-19 15:16:21.677: D/skia(5467): ----- started: [20 15] http://www.bmimobile.co.uk/images/mobile/visit.png
02-19 15:16:21.677: D/skia(5467): ----- started: [18 17] http://www.bmimobile.co.uk/images/mobile/services.png
02-19 15:16:21.687: D/skia(5467): ----- started: [20 15] http://www.bmimobile.co.uk/images/mobile/consultants.png
02-19 15:16:21.687: D/skia(5467): ----- started: [13 19] http://www.bmimobile.co.uk/images/mobile/contact.png
02-19 15:16:21.692: D/skia(5467): ----- started: [320 450] http://www.bmimobile.co.uk/images/mobile/welcome/bg-welcome.jpg
.
[注释] 如果我在在线模式下单击why-bmi按钮然后退出应用程序,关闭适配器然后再次单击why-bmi按钮然后它会显示“错误加载页面”消息。
但是,如果我更改为以下网址,则会显示我的 SO 页面。如果我单击指向我的赏金页面(此页面)的链接,然后离线,SO 页面将按您预期的方式显示,但如果您在离线模式下单击赏金链接,则它会显示。所以SO站点和bmi站点是有区别的。
if(isNetworkAvailable() == true)
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);
webView.getSettings().setAppCacheEnabled(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().setJavaScriptEnabled(true);
// Load the URLs inside the WebView, not in the external web browser
webView.setWebViewClient(new WebViewClient());
webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
//webView.loadUrl("http://www.bmimobile.co.uk/why-bmi.php", getHeaders());
//webView.loadUrl("http://www.bmimobile.co.uk/", getHeaders());
webView.loadUrl("http://***.com/users/532462/turtleboy?tab=bounties");
webView.loadUrl("http://***.com/users/532462/turtleboy");
else
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);
webView.getSettings().setAppCacheEnabled(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().setJavaScriptEnabled(true);
// Load the URLs inside the WebView, not in the external web browser
webView.setWebViewClient(new WebViewClient());
webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ONLY);
// webView.loadUrl("http://www.bmimobile.co.uk/", getHeaders());
webView.loadUrl("http://***.com/users/532462/turtleboy");
[编辑2]
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="uk.bmi.mobile"
android:versionCode="5"
android:versionName="1.0.4" >
<!-- GCM requires Android SDK version 2.2 (API level <img src="http://www.androidhive.info/wp-includes/images/smilies/icon_cool.gif" class="wp-smiley"> or above. -->
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="16" />
<!-- GCM connects to Internet Services. -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- GCM requires a Google account. -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<!-- Keeps the processor from sleeping when a message is received. -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Creates a custom permission so only this app can receive its messages. -->
<permission
android:name="uk.bmi.mobile.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="uk.bmi.mobile.permission.C2D_MESSAGE" />
<!-- This app has permission to register and receive data message. -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- Network State Permissions to detect Internet status -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Permission to vibrate -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permisson.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- Main activity. -->
<application
android:icon="@drawable/bmi_icon"
android:label="@string/app_name"
android:name="uk.bmi.mobile.ApplicationExt" >
<!-- Register Activity -->
<activity
android:name=".RegisterActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Main Activity -->
<activity
android:name="uk.bmi.mobile.MainActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name"
android:screenOrientation="portrait" >
</activity>
<receiver
android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<!-- Receives the actual messages. -->
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<!-- Receives the registration id. -->
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="uk.bmi.mobile" />
</intent-filter>
</receiver>
<service android:name="uk.bmi.mobile.GCMIntentService" />
</application>
</manifest>
【问题讨论】:
你设置了READ_EXTERNAL_STORAGE权限吗? @Yalla T. 嗨,我没有,我需要包括它吗? 不,我没有,我需要包含它吗? @YallaT。我现在已经包含了它,但没有区别 【参考方案1】:这不是您问题的确切答案,因为您询问的是 webview 缓存。但是,它可以达到相同的结果。
// saving page from web to file
File file = new File(this.getExternalFilesDir(null), "fileName.html");
FileUtils.copyURLToFile(new URL("http://www.bmimobile.co.uk/why-bmi.php"), file);
// loading saved file in webview
webview.loadUrl("file://" + file.getPath());
这是一种更灵活的方法,因为您可以控制加载、保存等等。
【讨论】:
FileUtils 来自 dependencies compile 'org.apache.commons:commons-io:1.3.2' 【参考方案2】:也许解决您的问题的方法是在加载 Web 视图的同时发出 HTTP 获取请求。
get 请求的结果可以持久存储在 sharedpreferences 中的字符串中,它将是您的 php 呈现的 HTML。
在你的android生命周期中,你可以判断应用是否离线,如果离线你可以从字符串中加载网站最后保存的版本
webview.loadData(yourSavedString, "text/html", "UTF-8");
虽然如果有图片,你将不得不做出额外的考虑,但如果图片不是动态的,你可以将它们存储在应用程序的资产文件夹中,并将保存的字符串中的 URL 替换为资产位置。
虽然它不能解决您的 webview 不缓存的原因,但它会完成相同的最终目标或离线查看页面
【讨论】:
【参考方案3】:另一个非常灵活和强大的选项是使用 HTML5 来管理应用程序中的缓存..
看看http://diveintohtml5.info/offline.html
您可以简单地在您的应用中启用缓存并从 Web 端本身管理缓存。
最好的问候
阿曼·高塔姆
【讨论】:
【参考方案4】:这有点傻。 来自
的 URL 中有重定向http://www.bmimobile.co.uk/
到
http://www.bmimobile.co.uk/index.php
你应该试试
webView.loadUrl("http://www.bmimobile.co.uk/why-bmi.php", getHeaders()); webView.loadUrl("http://www.bmimobile.co.uk/index.php", getHeaders());
【讨论】:
getHeaders()
是什么?【参考方案5】:
在某些情况下,WebView 无法原生缓存。如果页眉包含以下字段,WebView 将无法缓存该页的内容。
Cache-Control: no-store, no-cache
Pragma: no-cache
在这种情况下,您将不得不修改服务器上的页面属性以解决缓存问题。
【讨论】:
以上是关于Android WebView 未从缓存中加载第二页的主要内容,如果未能解决你的问题,请参考以下文章
Android WebView资源文件,如js和css文件缓存