| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.webkit; |
| |
| import java.io.IOException; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import android.net.http.Headers; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.os.Looper; |
| import android.os.Message; |
| |
| /** |
| * WebViewWorker executes in a separate thread other than UI and WebViewCore. To |
| * avoid blocking UI or WebKit's execution, the caller can send a message to |
| * WebViewWorker.getHandler() and it will be handled in the WebViewWorkerThread. |
| */ |
| final class WebViewWorker extends Handler { |
| |
| private static final String THREAD_NAME = "WebViewWorkerThread"; |
| |
| private static WebViewWorker sWorkerHandler; |
| |
| private static Map<LoadListener, CacheManager.CacheResult> mCacheResultMap |
| = new HashMap<LoadListener, CacheManager.CacheResult>(); |
| |
| /** |
| * Package level class to be used while creating a cache entry. |
| */ |
| static class CacheCreateData { |
| LoadListener mListener; |
| String mUrl; |
| String mMimeType; |
| int mStatusCode; |
| long mPostId; |
| Headers mHeaders; |
| } |
| |
| /** |
| * Package level class to be used while saving a cache entry. |
| */ |
| static class CacheSaveData { |
| LoadListener mListener; |
| String mUrl; |
| long mPostId; |
| } |
| |
| /** |
| * Package level class to be used while updating a cache entry's encoding. |
| */ |
| static class CacheEncoding { |
| LoadListener mListener; |
| String mEncoding; |
| } |
| |
| /** |
| * Package level class to be used while appending data to a cache entry. |
| */ |
| static class CacheData { |
| LoadListener mListener; |
| ByteArrayBuilder.Chunk mChunk; |
| } |
| |
| static synchronized WebViewWorker getHandler() { |
| if (sWorkerHandler == null) { |
| HandlerThread thread = new HandlerThread(THREAD_NAME, |
| android.os.Process.THREAD_PRIORITY_DEFAULT |
| + android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE); |
| thread.start(); |
| sWorkerHandler = new WebViewWorker(thread.getLooper()); |
| } |
| return sWorkerHandler; |
| } |
| |
| private WebViewWorker(Looper looper) { |
| super(looper); |
| } |
| |
| // trigger transaction once a minute |
| private static final int CACHE_TRANSACTION_TICKER_INTERVAL = 60 * 1000; |
| |
| private static boolean mCacheTickersBlocked = true; |
| |
| // message ids |
| static final int MSG_ADD_STREAMLOADER = 101; |
| static final int MSG_ADD_HTTPLOADER = 102; |
| static final int MSG_CREATE_CACHE = 103; |
| static final int MSG_UPDATE_CACHE_ENCODING = 104; |
| static final int MSG_APPEND_CACHE = 105; |
| static final int MSG_SAVE_CACHE = 106; |
| static final int MSG_REMOVE_CACHE = 107; |
| static final int MSG_TRIM_CACHE = 108; |
| static final int MSG_CLEAR_CACHE = 109; |
| static final int MSG_CACHE_TRANSACTION_TICKER = 110; |
| static final int MSG_PAUSE_CACHE_TRANSACTION = 111; |
| static final int MSG_RESUME_CACHE_TRANSACTION = 112; |
| |
| @Override |
| public void handleMessage(Message msg) { |
| switch(msg.what) { |
| case MSG_ADD_STREAMLOADER: { |
| StreamLoader loader = (StreamLoader) msg.obj; |
| loader.load(); |
| break; |
| } |
| case MSG_ADD_HTTPLOADER: { |
| FrameLoader loader = (FrameLoader) msg.obj; |
| loader.handleHTTPLoad(); |
| break; |
| } |
| case MSG_CREATE_CACHE: { |
| CacheCreateData data = (CacheCreateData) msg.obj; |
| CacheManager.CacheResult cache = CacheManager.createCacheFile( |
| data.mUrl, data.mStatusCode, data.mHeaders, |
| data.mMimeType, data.mPostId, false); |
| if (cache != null) { |
| mCacheResultMap.put(data.mListener, cache); |
| } else { |
| mCacheResultMap.remove(data.mListener); |
| } |
| break; |
| } |
| case MSG_UPDATE_CACHE_ENCODING: { |
| CacheEncoding data = (CacheEncoding) msg.obj; |
| CacheManager.CacheResult cache = mCacheResultMap |
| .get(data.mListener); |
| if (cache != null) { |
| cache.encoding = data.mEncoding; |
| } |
| break; |
| } |
| case MSG_APPEND_CACHE: { |
| CacheData data = (CacheData) msg.obj; |
| CacheManager.CacheResult cache = mCacheResultMap |
| .get(data.mListener); |
| if (cache != null) { |
| cache.contentLength += data.mChunk.mLength; |
| if (cache.contentLength > CacheManager.CACHE_MAX_SIZE) { |
| CacheManager.cleanupCacheFile(cache); |
| mCacheResultMap.remove(data.mListener); |
| } else { |
| try { |
| cache.outStream.write(data.mChunk.mArray, 0, |
| data.mChunk.mLength); |
| } catch (IOException e) { |
| CacheManager.cleanupCacheFile(cache); |
| mCacheResultMap.remove(data.mListener); |
| } |
| } |
| } |
| data.mChunk.release(); |
| break; |
| } |
| case MSG_SAVE_CACHE: { |
| CacheSaveData data = (CacheSaveData) msg.obj; |
| CacheManager.CacheResult cache = mCacheResultMap |
| .get(data.mListener); |
| if (cache != null) { |
| CacheManager.saveCacheFile(data.mUrl, data.mPostId, cache); |
| mCacheResultMap.remove(data.mListener); |
| } |
| break; |
| } |
| case MSG_REMOVE_CACHE: { |
| LoadListener listener = (LoadListener) msg.obj; |
| CacheManager.CacheResult cache = mCacheResultMap.get(listener); |
| if (cache != null) { |
| CacheManager.cleanupCacheFile(cache); |
| mCacheResultMap.remove(listener); |
| } |
| break; |
| } |
| case MSG_TRIM_CACHE: { |
| CacheManager.trimCacheIfNeeded(); |
| break; |
| } |
| case MSG_CLEAR_CACHE: { |
| CacheManager.clearCache(); |
| break; |
| } |
| case MSG_CACHE_TRANSACTION_TICKER: { |
| if (!mCacheTickersBlocked) { |
| CacheManager.endTransaction(); |
| CacheManager.startTransaction(); |
| sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER, |
| CACHE_TRANSACTION_TICKER_INTERVAL); |
| } |
| break; |
| } |
| case MSG_PAUSE_CACHE_TRANSACTION: { |
| if (CacheManager.disableTransaction()) { |
| mCacheTickersBlocked = true; |
| removeMessages(MSG_CACHE_TRANSACTION_TICKER); |
| } |
| break; |
| } |
| case MSG_RESUME_CACHE_TRANSACTION: { |
| if (CacheManager.enableTransaction()) { |
| mCacheTickersBlocked = false; |
| sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER, |
| CACHE_TRANSACTION_TICKER_INTERVAL); |
| } |
| break; |
| } |
| } |
| } |
| } |