安卓WebView无法从缓存加载第二个页面

12
我有一个仅包含网站的Android应用程序。我希望该应用程序缓存网站页面以供离线使用。
我正在进行简单的测试,以查看缓存是否有效,但不幸的是,在离线模式下无法加载之前在线加载过的页面。为了让事情更清楚,我在在线模式下加载以下两个页面。
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。提前感谢您的帮助。Matt
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] 实际上,仔细检查日志后发现,在在线模式下加载时已经发生了变化。以下是在线模式下的日志记录。缓存存储似乎存在问题。

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

[注] 如果我在在线模式下点击“为什么要计算BMI”按钮,然后退出应用程序,关闭适配器,再次点击“为什么要计算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://stackoverflow.com/users/532462/turtleboy?tab=bounties");
            webView.loadUrl("http://stackoverflow.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://stackoverflow.com/users/532462/turtleboy");


            }


    }

[edit2]

    <?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" alt="8)" 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.
@Yalla T. 你好,我没有,我需要包含它吗? - turtleboy
我没有,我需要包含它吗? - turtleboy
@YallaT。我现在已经包含了它,但没有任何区别。 - turtleboy
5个回答

9

虽然您询问的是有关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());

这是一种更灵活的方法,因为您可以控制加载、保存和更多操作。


1
文件工具类 from <br/>依赖关系 { 编译 'org.apache.commons:commons-io:1.3.2' } - Evgenii Vorobei

1

另一个非常灵活和强大的选项是使用HTML5来管理应用程序中的缓存。

请查看http://diveintohtml5.info/offline.html

您可以简单地在应用程序中启用缓存,并从Web端自行管理缓存。

此致

Aman Gautam


1

1
getHeaders() 是什么? - Ravi Vaniya

1
也许解决您问题的方法是在Webview加载时进行HTTP get请求。将get请求的结果以字符串形式存储在sharedpreferences中,它将是您的php渲染的HTML。
在您的Android生命周期中,您可以确定应用程序是否离线。如果应用程序离线,则可以从字符串中加载站点的最后保存版本。 webview.loadData(yourSavedString, "text/html", "UTF-8"); 虽然如果有图像,您将需要进行额外的考虑,但如果图像不是动态的,则可以将它们存储在应用程序的asset文件夹中,并将保存的字符串中的URL替换为资产位置。
虽然这并不能解决您的Webview为什么无法缓存的问题,但它将实现相同的目标,即离线查看页面。

0

有时候 WebView 无法本地缓存。如果页面头包含以下字段,则 WebView 将无法缓存该页面的内容。

Cache-Control: no-store, no-cache
Pragma: no-cache

在这种情况下,您需要修改服务器上的页面属性来解决缓存问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接