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 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 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 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