浏览器保存网页的四种方式

来源:互联网 发布:misia 知乎 编辑:程序博客网 时间:2024/06/05 10:04
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://mikewang.blog.51cto.com/3826268/1206701

当我们使用浏览器浏览网页时,常常想保存内容,目的可能是离线阅读或者是收藏。之前的一个项目用到一些,一并总结。


方式一,Snapshot

4.0支持此方法saveViewState(),方法源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
     * Saves the view data to the output stream. The output is highly
     * version specific, and may not be able to be loaded by newer versions
     * of WebView.
     * @param stream The {@link OutputStream} to save to
     * @return True if saved successfully
     * @hide
     */
    public boolean saveViewState(OutputStream stream) {
        try {
            return ViewStateSerializer.serializeViewState(stream, this);
        catch (IOException e) {
            Log.w(LOGTAG, "Failed to saveViewState", e);
        }
        return false;
    }

4.0原生浏览器其实是把网页的内容存入到BLOB中,缺陷很明显,网页一大就保存不了,查了下源码,BLOB保存的数据有大小限制。4.0上,超过2M大小的网页就没法保存,源码截图如下:




但是4.1上,saveViewState() 更新了参数,增加了回调。方法源码如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
     * Saves the view data to the output stream. The output is highly
     * version specific, and may not be able to be loaded by newer versions
     * of WebView.
     * @param stream The {@link OutputStream} to save to
     * @param callback The {@link ValueCallback} to call with the result
     */
    public void saveViewState(OutputStream stream, ValueCallback<Boolean> callback) {
        if (mWebViewCore == null) {
            callback.onReceiveValue(false);
            return;
        }
        mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SAVE_VIEW_STATE,
                new WebViewCore.SaveViewStateRequest(stream, callback));
    }

4.1原生浏览器去snapshot的处理更好了,明白了这个限制之后,网页的data不存数据库了,改存文件了,所以网页太大不能保存的问题就解决了。

考虑到大部分的网页都比较小的情况下,且实现上快速,采用4.0的方法。

主代码采用反射调用4.0 的saveViewState()方法,方法名改为mySaveViewState(),代码如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public boolean mySaveViewState(OutputStream stream) {
        boolean ret = false;
        if (mMethodSaveViewState == null)
            try {
                mMethodSaveViewState = WebView.class.getDeclaredMethod(
                        "saveViewState", OutputStream.class);
                mMethodSaveViewState.setAccessible(true);
            catch (NoSuchMethodException e) {
            }
        if (mMethodSaveViewState != null) {
            try {
                ret = (Boolean) mMethodSaveViewState.invoke(this, stream);
            catch (IllegalArgumentException e) {
                e.printStackTrace();
            catch (IllegalAccessException e) {
                e.printStackTrace();
            catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        return ret;
    }


方式二,Stream(file)

因为2.3上不支持saveViewState()方法,所以采用SavePicture(),SavePicture()方法其实来自SaveState(),在webview前进后退后者其他操作时,会保存相应的状态。

方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
    * Save the state of this WebView used in
    * {@link android.app.Activity#onSaveInstanceState}. Please note that this
    * method no longer stores the display data for this WebView. The previous
    * behavior could potentially leak files if {@link #restoreState} was never
    * called. See {@link #savePicture} and {@link #restorePicture} for saving
    * and restoring the display data.
    * @param outState The Bundle to store the WebView state.
    * @return The same copy of the back/forward list used to save the state. If
    *         saveState fails, the returned list will be null.
    * @see #savePicture
    * @see #restorePicture
    */
   public WebBackForwardList saveState(Bundle outState) {
       if (outState == null) {
           return null;
       }
       // We grab a copy of the back/forward list because a client of WebView
       // may have invalidated the history list by calling clearHistory.
       WebBackForwardList list = copyBackForwardList();
       final int currentIndex = list.getCurrentIndex();
       final int size = list.getSize();
       // We should fail saving the state if the list is empty or the index is
       // not in a valid range.
       if (currentIndex < 0 || currentIndex >= size || size == 0) {
           return null;
       }
       outState.putInt("index", currentIndex);
       // FIXME: This should just be a byte[][] instead of ArrayList but
       // Parcel.java does not have the code to handle multi-dimensional
       // arrays.
       ArrayList<byte[]> history = new ArrayList<byte[]>(size);
       for (int i = 0; i < size; i++) {
           WebHistoryItem item = list.getItemAtIndex(i);
           if (null == item) {
               // FIXME: this shouldn't happen
               // need to determine how item got set to null
               Log.w(LOGTAG, "saveState: Unexpected null history item.");
               return null;
           }
           byte[] data = item.getFlattenedData();
           if (data == null) {
               // It would be very odd to not have any data for a given history
               // item. And we will fail to rebuild the history list without
               // flattened data.
               return null;
           }
           history.add(data);
       }
       outState.putSerializable("history", history);
       if (mCertificate != null) {
           outState.putBundle("certificate",
                              SslCertificate.saveState(mCertificate));
       }
       return list;
   }
   /**
    * Save the current display data to the Bundle given. Used in conjunction
    * with {@link #saveState}.
    * @param b A Bundle to store the display data.
    * @param dest The file to store the serialized picture data. Will be
    *             overwritten with this WebView's picture data.
    * @return True if the picture was successfully saved.
    */
   public boolean savePicture(Bundle b, final File dest) {
       if (dest == null || b == null) {
           return false;
       }
       final Picture p = capturePicture();
       // Use a temporary file while writing to ensure the destination file
       // contains valid data.
       final File temp = new File(dest.getPath() + ".writing");
       new Thread(new Runnable() {
           public void run() {
               FileOutputStream out = null;
               try {
                   out = new FileOutputStream(temp);
                   p.writeToStream(out);
                   // Writing the picture succeeded, rename the temporary file
                   // to the destination.
                   temp.renameTo(dest);
               catch (Exception e) {
                   // too late to do anything about it.
               finally {
                   if (out != null) {
                       try {
                           out.close();
                       catch (Exception e) {
                           // Can't do anything about that
                       }
                   }
                   temp.delete();
               }
           }
       }).start();
       // now update the bundle
       b.putInt("scrollX", mScrollX);
       b.putInt("scrollY", mScrollY);
       b.putFloat("scale", mActualScale);
       b.putFloat("textwrapScale", mTextWrapScale);
       b.putBoolean("overview", mInZoomOverview);
       return true;
   }

具体实现上,我们需要传入一个bundle,保存相应的状态。


方式三: png

保存成图片的缺陷很大,放缩的失真,不能编辑。Webview有capturePicture()方法支持。源码中此方法的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
    * Return a new picture that captures the current display of the webview.
    * This is a copy of the display, and will be unaffected if the webview
    * later loads a different URL.
    *
    * @return a picture containing the current contents of the view. Note this
    *         picture is of the entire document, and is not restricted to the
    *         bounds of the view.
    */
   public Picture capturePicture() {
       if (null == mWebViewCore) return null// check for out of memory tab
       return mWebViewCore.copyContentPicture();
   }

传入picture,具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//FIXME draw on canvas can not run in a non-ui thread
        boolean saveAsPng(Context ctx, ContentValues values, Picture picture){
            String path = UUID.randomUUID().toString();
            OutputStream outs = null;
            try {
                if (null == picture)
                    return false;
                String png = path + ".png";
                outs = ctx.openFileOutput(png, Context.MODE_PRIVATE);
                PictureDrawable pictureDrawable = new PictureDrawable(
                        picture);
                Bitmap bitmap = null;
                bitmap = Bitmap.createBitmap(
                        pictureDrawable.getIntrinsicWidth(),
                        pictureDrawable.getIntrinsicHeight(),
                        Config.ARGB_8888);
                Canvas canvas = new Canvas(bitmap);
                canvas.drawPicture(pictureDrawable.getPicture());
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, outs);
                outs.flush();
                outs.close();
                bitmap.recycle();
                bitmap = null;
                values.put(SavedPageColumns.TYPE, SavedPage.TYPE_PNG);
                values.put(SavedPageColumns.PATH, png);
            catch (Exception e) {
                Log.w(LOGTAG, "Failed to save page ", e);
                if (outs != null) {
                    try {
                        outs.close();
                    catch (IOException ignore) {
                    }
                }
                File file = ctx.getFileStreamPath(path);
                if (file != null && file.exists() && !file.delete()) {
                    file.deleteOnExit();
                }
                return false;
            }
            return true;
        }


方法四: 保存html网页

直接将url给下载管理器即可。

本文出自 “小新专栏” 博客,请务必保留此出处http://mikewang.blog.51cto.com/3826268/1206701

0 0
原创粉丝点击