• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.webkit;
18 
19 import java.io.IOException;
20 import java.util.HashMap;
21 import java.util.Map;
22 
23 import android.net.http.Headers;
24 import android.os.Handler;
25 import android.os.HandlerThread;
26 import android.os.Looper;
27 import android.os.Message;
28 
29 /**
30  * WebViewWorker executes in a separate thread other than UI and WebViewCore. To
31  * avoid blocking UI or WebKit's execution, the caller can send a message to
32  * WebViewWorker.getHandler() and it will be handled in the WebViewWorkerThread.
33  */
34 final class WebViewWorker extends Handler {
35 
36     private static final String THREAD_NAME = "WebViewWorkerThread";
37 
38     private static WebViewWorker sWorkerHandler;
39 
40     private static Map<LoadListener, CacheManager.CacheResult> mCacheResultMap
41             = new HashMap<LoadListener, CacheManager.CacheResult>();
42 
43     /**
44      * Package level class to be used while creating a cache entry.
45      */
46     static class CacheCreateData {
47         LoadListener mListener;
48         String mUrl;
49         String mMimeType;
50         int mStatusCode;
51         long mPostId;
52         Headers mHeaders;
53     }
54 
55     /**
56      * Package level class to be used while saving a cache entry.
57      */
58     static class CacheSaveData {
59         LoadListener mListener;
60         String mUrl;
61         long mPostId;
62     }
63 
64     /**
65      * Package level class to be used while updating a cache entry's encoding.
66      */
67     static class CacheEncoding {
68         LoadListener mListener;
69         String mEncoding;
70     }
71 
72     /**
73      * Package level class to be used while appending data to a cache entry.
74      */
75     static class CacheData {
76         LoadListener mListener;
77         ByteArrayBuilder.Chunk mChunk;
78     }
79 
getHandler()80     static synchronized WebViewWorker getHandler() {
81         if (sWorkerHandler == null) {
82             HandlerThread thread = new HandlerThread(THREAD_NAME,
83                     android.os.Process.THREAD_PRIORITY_DEFAULT
84                             + android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
85             thread.start();
86             sWorkerHandler = new WebViewWorker(thread.getLooper());
87         }
88         return sWorkerHandler;
89     }
90 
WebViewWorker(Looper looper)91     private WebViewWorker(Looper looper) {
92         super(looper);
93     }
94 
95     // trigger transaction once a minute
96     private static final int CACHE_TRANSACTION_TICKER_INTERVAL = 60 * 1000;
97 
98     private static boolean mCacheTickersBlocked = true;
99 
100     // message ids
101     static final int MSG_ADD_STREAMLOADER = 101;
102     static final int MSG_ADD_HTTPLOADER = 102;
103     static final int MSG_CREATE_CACHE = 103;
104     static final int MSG_UPDATE_CACHE_ENCODING = 104;
105     static final int MSG_APPEND_CACHE = 105;
106     static final int MSG_SAVE_CACHE = 106;
107     static final int MSG_REMOVE_CACHE = 107;
108     static final int MSG_TRIM_CACHE = 108;
109     static final int MSG_CLEAR_CACHE = 109;
110     static final int MSG_CACHE_TRANSACTION_TICKER = 110;
111     static final int MSG_PAUSE_CACHE_TRANSACTION = 111;
112     static final int MSG_RESUME_CACHE_TRANSACTION = 112;
113 
114     @Override
handleMessage(Message msg)115     public void handleMessage(Message msg) {
116         switch(msg.what) {
117             case MSG_ADD_STREAMLOADER: {
118                 StreamLoader loader = (StreamLoader) msg.obj;
119                 loader.load();
120                 break;
121             }
122             case MSG_ADD_HTTPLOADER: {
123                 FrameLoader loader = (FrameLoader) msg.obj;
124                 loader.handleHTTPLoad();
125                 break;
126             }
127             case MSG_CREATE_CACHE: {
128                 CacheCreateData data = (CacheCreateData) msg.obj;
129                 CacheManager.CacheResult cache = CacheManager.createCacheFile(
130                         data.mUrl, data.mStatusCode, data.mHeaders,
131                         data.mMimeType, data.mPostId, false);
132                 if (cache != null) {
133                     mCacheResultMap.put(data.mListener, cache);
134                 } else {
135                     mCacheResultMap.remove(data.mListener);
136                 }
137                 break;
138             }
139             case MSG_UPDATE_CACHE_ENCODING: {
140                 CacheEncoding data = (CacheEncoding) msg.obj;
141                 CacheManager.CacheResult cache = mCacheResultMap
142                         .get(data.mListener);
143                 if (cache != null) {
144                     cache.encoding = data.mEncoding;
145                 }
146                 break;
147             }
148             case MSG_APPEND_CACHE: {
149                 CacheData data = (CacheData) msg.obj;
150                 CacheManager.CacheResult cache = mCacheResultMap
151                         .get(data.mListener);
152                 if (cache != null) {
153                     cache.contentLength += data.mChunk.mLength;
154                     if (cache.contentLength > CacheManager.CACHE_MAX_SIZE) {
155                         CacheManager.cleanupCacheFile(cache);
156                         mCacheResultMap.remove(data.mListener);
157                     } else {
158                         try {
159                             cache.outStream.write(data.mChunk.mArray, 0,
160                                     data.mChunk.mLength);
161                         } catch (IOException e) {
162                             CacheManager.cleanupCacheFile(cache);
163                             mCacheResultMap.remove(data.mListener);
164                         }
165                     }
166                 }
167                 data.mChunk.release();
168                 break;
169             }
170             case MSG_SAVE_CACHE: {
171                 CacheSaveData data = (CacheSaveData) msg.obj;
172                 CacheManager.CacheResult cache = mCacheResultMap
173                         .get(data.mListener);
174                 if (cache != null) {
175                     CacheManager.saveCacheFile(data.mUrl, data.mPostId, cache);
176                     mCacheResultMap.remove(data.mListener);
177                 }
178                 break;
179             }
180             case MSG_REMOVE_CACHE: {
181                 LoadListener listener = (LoadListener) msg.obj;
182                 CacheManager.CacheResult cache = mCacheResultMap.get(listener);
183                 if (cache != null) {
184                     CacheManager.cleanupCacheFile(cache);
185                     mCacheResultMap.remove(listener);
186                 }
187                 break;
188             }
189             case MSG_TRIM_CACHE: {
190                 CacheManager.trimCacheIfNeeded();
191                 break;
192             }
193             case MSG_CLEAR_CACHE: {
194                 CacheManager.clearCache();
195                 break;
196             }
197             case MSG_CACHE_TRANSACTION_TICKER: {
198                 if (!mCacheTickersBlocked) {
199                     CacheManager.endTransaction();
200                     CacheManager.startTransaction();
201                     sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER,
202                             CACHE_TRANSACTION_TICKER_INTERVAL);
203                 }
204                 break;
205             }
206             case MSG_PAUSE_CACHE_TRANSACTION: {
207                 if (CacheManager.disableTransaction()) {
208                     mCacheTickersBlocked = true;
209                     removeMessages(MSG_CACHE_TRANSACTION_TICKER);
210                 }
211                 break;
212             }
213             case MSG_RESUME_CACHE_TRANSACTION: {
214                 if (CacheManager.enableTransaction()) {
215                     mCacheTickersBlocked = false;
216                     sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER,
217                             CACHE_TRANSACTION_TICKER_INTERVAL);
218                 }
219                 break;
220             }
221         }
222     }
223 }
224