synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
}
}
}
- CacheManager.trimCacheIfNeeded();
+ WebViewWorker.getHandler().sendEmptyMessage(
+ WebViewWorker.MSG_TRIM_CACHE);
break;
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
// the cache database. The directory could be recreated
// because the system flushed all the data/cache directories
// to free up disk space.
- WebViewCore.endCacheTransaction();
- mDataBase.clearCache();
- WebViewCore.startCacheTransaction();
+ // delete rows in the cache database
+ WebViewWorker.getHandler().sendEmptyMessage(
+ WebViewWorker.MSG_CLEAR_CACHE);
return true;
}
return false;
*
* @param disabled true to disable the cache
*/
- // only called from WebCore thread
static void setCacheDisabled(boolean disabled) {
if (disabled == mDisabled) {
return;
return mDisabled;
}
- // only called from WebCore thread
+ // only called from WebViewWorkerThread
// make sure to call enableTransaction/disableTransaction in pair
static boolean enableTransaction() {
if (++mRefCount == 1) {
return false;
}
- // only called from WebCore thread
+ // only called from WebViewWorkerThread
// make sure to call enableTransaction/disableTransaction in pair
static boolean disableTransaction() {
- if (mRefCount == 0) {
- Log.e(LOGTAG, "disableTransaction is out of sync");
- }
if (--mRefCount == 0) {
mDataBase.endCacheTransaction();
return true;
return false;
}
- // only called from WebCore thread
- // make sure to call startCacheTransaction/endCacheTransaction in pair
- public static boolean startCacheTransaction() {
+ // only called from WebViewWorkerThread
+ // make sure to call startTransaction/endTransaction in pair
+ static boolean startTransaction() {
return mDataBase.startCacheTransaction();
}
- // only called from WebCore thread
- // make sure to call startCacheTransaction/endCacheTransaction in pair
- public static boolean endCacheTransaction() {
+ // only called from WebViewWorkerThread
+ // make sure to call startTransaction/endTransaction in pair
+ static boolean endTransaction() {
boolean ret = mDataBase.endCacheTransaction();
if (++mTrimCacheCount >= TRIM_CACHE_INTERVAL) {
mTrimCacheCount = 0;
return ret;
}
+ // only called from WebCore Thread
+ // make sure to call startCacheTransaction/endCacheTransaction in pair
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ public static boolean startCacheTransaction() {
+ return false;
+ }
+
+ // only called from WebCore Thread
+ // make sure to call startCacheTransaction/endCacheTransaction in pair
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ public static boolean endCacheTransaction() {
+ return false;
+ }
+
/**
* Given a url, returns the CacheResult if exists. Otherwise returns null.
* If headers are provided and a cache needs validation,
*
* @return the CacheResult for a given url
*/
- // only called from WebCore thread
public static CacheResult getCacheFile(String url,
Map<String, String> headers) {
return getCacheFile(url, 0, headers);
}
- // only called from WebCore thread
static CacheResult getCacheFile(String url, long postIdentifier,
Map<String, String> headers) {
if (mDisabled) {
* @hide - hide createCacheFile since it has a parameter of type headers, which is
* in a hidden package.
*/
- // only called from WebCore thread
public static CacheResult createCacheFile(String url, int statusCode,
Headers headers, String mimeType, boolean forceCache) {
return createCacheFile(url, statusCode, headers, mimeType, 0,
forceCache);
}
- // only called from WebCore thread
static CacheResult createCacheFile(String url, int statusCode,
Headers headers, String mimeType, long postIdentifier,
boolean forceCache) {
* Save the info of a cache file for a given url to the CacheMap so that it
* can be reused later
*/
- // only called from WebCore thread
public static void saveCacheFile(String url, CacheResult cacheRet) {
saveCacheFile(url, 0, cacheRet);
}
- // only called from WebCore thread
static void saveCacheFile(String url, long postIdentifier,
CacheResult cacheRet) {
try {
*
* @return true if it succeeds
*/
- // only called from WebCore thread
static boolean removeAllCacheFiles() {
// Note, this is called before init() when the database is
// created or upgraded.
mClearCacheOnInit = true;
return true;
}
- // delete cache in a separate thread to not block UI.
+ // delete rows in the cache database
+ WebViewWorker.getHandler().sendEmptyMessage(
+ WebViewWorker.MSG_CLEAR_CACHE);
+ // delete cache files in a separate thread to not block UI.
final Runnable clearCache = new Runnable() {
public void run() {
// delete all cache files
} catch (SecurityException e) {
// Ignore SecurityExceptions.
}
- // delete database
- mDataBase.clearCache();
}
};
new Thread(clearCache).start();
/**
* Return true if the cache is empty.
*/
- // only called from WebCore thread
static boolean cacheEmpty() {
return mDataBase.hasCache();
}
- // only called from WebCore thread
static void trimCacheIfNeeded() {
if (mDataBase.getCacheTotalSize() > CACHE_THRESHOLD) {
- ArrayList<String> pathList = mDataBase.trimCache(CACHE_TRIM_AMOUNT);
+ List<String> pathList = mDataBase.trimCache(CACHE_TRIM_AMOUNT);
int size = pathList.size();
for (int i = 0; i < size; i++) {
File f = new File(mBaseDir, pathList.get(i));
Log.e(LOGTAG, f.getPath() + " delete failed.");
}
}
+ // remove the unreferenced files in the cache directory
+ final List<String> fileList = mDataBase.getAllCacheFileNames();
+ if (fileList == null) return;
+ String[] toDelete = mBaseDir.list(new FilenameFilter() {
+ public boolean accept(File dir, String filename) {
+ if (fileList.contains(filename)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ });
+ if (toDelete == null) return;
+ size = toDelete.length;
+ for (int i = 0; i < size; i++) {
+ File f = new File(mBaseDir, toDelete[i]);
+ if (!f.delete()) {
+ Log.e(LOGTAG, f.getPath() + " delete failed.");
+ }
+ }
}
}
+ static void clearCache() {
+ // delete database
+ mDataBase.clearCache();
+ }
+
private static boolean checkCacheRedirect(int statusCode) {
if (statusCode == 301 || statusCode == 302 || statusCode == 307) {
// as 303 can't be cached, we do not return true
return false;
}
mNetwork = Network.getInstance(mListener.getContext());
- return handleHTTPLoad();
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_HTTPLOADER, this).sendToTarget();
+ return true;
} else if (handleLocalFile(url, mListener, mSettings)) {
return true;
}
}
if (URLUtil.isAssetUrl(url)) {
// load asset in a separate thread as it involves IO
- new FileLoader(url, loadListener, FileLoader.TYPE_ASSET, true)
- .enqueue();
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER,
+ new FileLoader(url, loadListener, FileLoader.TYPE_ASSET,
+ true)).sendToTarget();
return true;
} else if (URLUtil.isResourceUrl(url)) {
// load resource in a separate thread as it involves IO
- new FileLoader(url, loadListener, FileLoader.TYPE_RES, true)
- .enqueue();
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER,
+ new FileLoader(url, loadListener, FileLoader.TYPE_RES,
+ true)).sendToTarget();
return true;
} else if (URLUtil.isFileUrl(url)) {
// load file in a separate thread as it involves IO
- new FileLoader(url, loadListener, FileLoader.TYPE_FILE, settings
- .getAllowFileAccess()).enqueue();
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER,
+ new FileLoader(url, loadListener, FileLoader.TYPE_FILE,
+ settings.getAllowFileAccess())).sendToTarget();
return true;
} else if (URLUtil.isContentUrl(url)) {
// Send the raw url to the ContentLoader because it will do a
// permission check and the url has to match.
// load content in a separate thread as it involves IO
- new ContentLoader(loadListener.url(), loadListener).enqueue();
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER,
+ new ContentLoader(loadListener.url(), loadListener))
+ .sendToTarget();
return true;
} else if (URLUtil.isDataUrl(url)) {
// load data in the current thread to reduce the latency
}
return false;
}
-
- private boolean handleHTTPLoad() {
+
+ boolean handleHTTPLoad() {
if (mHeaders == null) {
mHeaders = new HashMap<String, String>();
}
CacheLoader cacheLoader =
new CacheLoader(mListener, result);
mListener.setCacheLoader(cacheLoader);
- cacheLoader.load();
+ // Load the cached file in a separate thread
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER, cacheLoader).sendToTarget();
}
/*
private boolean mCancelled; // The request has been cancelled.
private boolean mAuthFailed; // indicates that the prev. auth failed
private CacheLoader mCacheLoader;
- private CacheManager.CacheResult mCacheResult;
private boolean mFromCache = false;
private HttpAuthHeader mAuthHeader;
private int mErrorID = OK;
*/
public void headers(Headers headers) {
if (DebugFlags.LOAD_LISTENER) Log.v(LOGTAG, "LoadListener.headers");
+ // call db (setCookie) in the non-WebCore thread
+ if (mCancelled) return;
+ ArrayList<String> cookies = headers.getSetCookie();
+ for (int i = 0; i < cookies.size(); ++i) {
+ CookieManager.getInstance().setCookie(mUri, cookies.get(i));
+ }
sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS, headers));
}
if (mCancelled) return;
mHeaders = headers;
- ArrayList<String> cookies = headers.getSetCookie();
- for (int i = 0; i < cookies.size(); ++i) {
- CookieManager.getInstance().setCookie(mUri, cookies.get(i));
- }
-
long contentLength = headers.getContentLength();
if (contentLength != Headers.NO_CONTENT_LENGTH) {
mContentLength = contentLength;
if (!mFromCache && mRequestHandle != null
&& (!mRequestHandle.getMethod().equals("POST")
|| mPostIdentifier != 0)) {
- mCacheResult = CacheManager.createCacheFile(mUrl, mStatusCode,
- headers, mMimeType, mPostIdentifier, false);
- }
- if (mCacheResult != null) {
- mCacheResult.encoding = mEncoding;
+ WebViewWorker.CacheCreateData data = new WebViewWorker.CacheCreateData();
+ data.mListener = this;
+ data.mUrl = mUrl;
+ data.mMimeType = mMimeType;
+ data.mStatusCode = mStatusCode;
+ data.mPostId = mPostIdentifier;
+ data.mHeaders = headers;
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_CREATE_CACHE, data).sendToTarget();
}
+ WebViewWorker.CacheEncoding ce = new WebViewWorker.CacheEncoding();
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_UPDATE_CACHE_ENCODING, ce).sendToTarget();
}
commitHeadersCheckRedirect();
}
// ask for it, so make sure we have a valid CacheLoader
// before calling it.
if (mCacheLoader != null) {
- mCacheLoader.load();
+ // Load the cached file in a separate thread
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader)
+ .sendToTarget();
mFromCache = true;
if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener cache load url=" + url());
Log.v(LOGTAG, "FrameLoader: HTTP URL in cache " +
"and usable: " + url());
}
- // Load the cached file
- mCacheLoader.load();
+ // Load the cached file in a separate thread
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader)
+ .sendToTarget();
mFromCache = true;
return true;
}
* WebCore.
*/
void downloadFile() {
- // Setting the Cache Result to null ensures that this
- // content is not added to the cache
- if (mCacheResult != null) {
- CacheManager.cleanupCacheFile(mCacheResult);
- mCacheResult = null;
- }
+ // remove the cache
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget();
// Inform the client that they should download a file
mBrowserFrame.getCallbackProxy().onDownloadStart(url(),
if (c == null) break;
if (c.mLength != 0) {
- if (mCacheResult != null) {
- mCacheResult.contentLength += c.mLength;
- if (mCacheResult.contentLength > CacheManager.CACHE_MAX_SIZE) {
- CacheManager.cleanupCacheFile(mCacheResult);
- mCacheResult = null;
- } else {
- try {
- mCacheResult.outStream
- .write(c.mArray, 0, c.mLength);
- } catch (IOException e) {
- CacheManager.cleanupCacheFile(mCacheResult);
- mCacheResult = null;
- }
- }
- }
nativeAddData(c.mArray, c.mLength);
+ WebViewWorker.CacheData data = new WebViewWorker.CacheData();
+ data.mListener = this;
+ data.mChunk = c;
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_APPEND_CACHE, data).sendToTarget();
+ } else {
+ c.release();
}
- c.release();
checker.responseAlert("res nativeAddData");
}
}
* cancellation or errors during the load.
*/
void tearDown() {
- if (mCacheResult != null) {
- if (getErrorID() == OK) {
- CacheManager.saveCacheFile(mUrl, mPostIdentifier, mCacheResult);
- } else {
- CacheManager.cleanupCacheFile(mCacheResult);
- }
-
- // we need to reset mCacheResult to be null
- // resource loader's tearDown will call into WebCore's
- // nativeFinish, which in turn calls loader.cancel().
- // If we don't reset mCacheFile, the file will be deleted.
- mCacheResult = null;
+ if (getErrorID() == OK) {
+ WebViewWorker.CacheSaveData data = new WebViewWorker.CacheSaveData();
+ data.mListener = this;
+ data.mUrl = mUrl;
+ data.mPostId = mPostIdentifier;
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_SAVE_CACHE, data).sendToTarget();
+ } else {
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget();
}
if (mNativeLoader != 0) {
PerfChecker checker = new PerfChecker();
mRequestHandle = null;
}
- if (mCacheResult != null) {
- CacheManager.cleanupCacheFile(mCacheResult);
- mCacheResult = null;
- }
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget();
mCancelled = true;
clearNativeLoader();
}
// Cache the redirect response
- if (mCacheResult != null) {
- if (getErrorID() == OK) {
- CacheManager.saveCacheFile(mUrl, mPostIdentifier,
- mCacheResult);
- } else {
- CacheManager.cleanupCacheFile(mCacheResult);
- }
- mCacheResult = null;
+ if (getErrorID() == OK) {
+ WebViewWorker.CacheSaveData data = new WebViewWorker.CacheSaveData();
+ data.mListener = this;
+ data.mUrl = mUrl;
+ data.mPostId = mPostIdentifier;
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_SAVE_CACHE, data).sendToTarget();
+ } else {
+ WebViewWorker.getHandler().obtainMessage(
+ WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget();
}
// This will strip the anchor
import android.net.http.EventHandler;
import android.net.http.Headers;
import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
import android.os.Message;
import java.io.IOException;
// Handler which will be initialized in the thread where load() is called.
private Handler mHandler;
- // Handler which will be used to load StreamLoader in a separate thread
- private static StreamQueueHandler sStreamQueueHandler;
-
- private static final Object sStreamQueueLock = new Object();
-
/**
* Constructor. Although this class calls the LoadListener, it only calls
* the EventHandler Interface methods. LoadListener concrete class is used
abstract protected void buildHeaders(Headers headers);
/**
- * Calling this method to load this StreamLoader in a separate
- * "StreamLoadingThread".
- */
- final void enqueue() {
- synchronized (sStreamQueueLock) {
- if (sStreamQueueHandler == null) {
- HandlerThread thread = new HandlerThread(
- StreamQueueHandler.THREAD_NAME,
- android.os.Process.THREAD_PRIORITY_DEFAULT +
- android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
- thread.start();
- sStreamQueueHandler = new StreamQueueHandler(thread.getLooper());
- }
- }
-
- sStreamQueueHandler.obtainMessage(StreamQueueHandler.MSG_ADD_LOADER,
- this).sendToTarget();
- }
-
- /**
* Calling this method starts the load of the content for this StreamLoader.
* This method simply creates a Handler in the current thread and posts a
* message to send the status and returns immediately.
}
mLoadListener.endData();
}
-
- private static class StreamQueueHandler extends Handler {
- private static final String THREAD_NAME = "StreamLoadingThread";
-
- private static final int MSG_ADD_LOADER = 101;
-
- StreamQueueHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_ADD_LOADER) {
- StreamLoader loader = (StreamLoader) msg.obj;
- loader.load();
- }
- }
- }
}
private static final int INITIALIZE = 0;
private static final int REDUCE_PRIORITY = 1;
private static final int RESUME_PRIORITY = 2;
- private static final int CACHE_TICKER = 3;
- private static final int BLOCK_CACHE_TICKER = 4;
- private static final int RESUME_CACHE_TICKER = 5;
-
- private static final int CACHE_TICKER_INTERVAL = 60 * 1000; // 1 minute
-
- private static boolean mCacheTickersBlocked = true;
public void run() {
Looper.prepare();
Process.setThreadPriority(
Process.THREAD_PRIORITY_DEFAULT);
break;
-
- case CACHE_TICKER:
- if (!mCacheTickersBlocked) {
- CacheManager.endCacheTransaction();
- CacheManager.startCacheTransaction();
- sendMessageDelayed(
- obtainMessage(CACHE_TICKER),
- CACHE_TICKER_INTERVAL);
- }
- break;
-
- case BLOCK_CACHE_TICKER:
- if (CacheManager.endCacheTransaction()) {
- mCacheTickersBlocked = true;
- }
- break;
-
- case RESUME_CACHE_TICKER:
- if (CacheManager.startCacheTransaction()) {
- mCacheTickersBlocked = false;
- }
- break;
}
}
};
Process.setThreadPriority(mTid,
Process.THREAD_PRIORITY_BACKGROUND);
pauseTimers();
- if (CacheManager.disableTransaction()) {
- WebCoreThread.mCacheTickersBlocked = true;
- sWebCoreHandler.removeMessages(
- WebCoreThread.CACHE_TICKER);
- }
+ WebViewWorker.getHandler().sendEmptyMessage(
+ WebViewWorker.MSG_PAUSE_CACHE_TRANSACTION);
break;
case RESUME_TIMERS:
Process.setThreadPriority(mTid, mSavedPriority);
resumeTimers();
- if (CacheManager.enableTransaction()) {
- WebCoreThread.mCacheTickersBlocked = false;
- sWebCoreHandler.sendMessageDelayed(
- sWebCoreHandler.obtainMessage(
- WebCoreThread.CACHE_TICKER),
- WebCoreThread.CACHE_TICKER_INTERVAL);
- }
+ WebViewWorker.getHandler().sendEmptyMessage(
+ WebViewWorker.MSG_RESUME_CACHE_TRANSACTION);
break;
case ON_PAUSE:
.obtainMessage(WebCoreThread.RESUME_PRIORITY));
}
- static void startCacheTransaction() {
- sWebCoreHandler.sendMessage(sWebCoreHandler
- .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER));
- }
-
- static void endCacheTransaction() {
- sWebCoreHandler.sendMessage(sWebCoreHandler
- .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER));
- }
-
static void pauseUpdatePicture(WebViewCore core) {
// Note: there is one possible failure mode. If pauseUpdatePicture() is
// called from UI thread while WEBKIT_DRAW is just pulled out of the
sendUpdateTextEntry();
// as CacheManager can behave based on database transaction, we need to
// call tick() to trigger endTransaction
- sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER);
- sWebCoreHandler.sendMessage(sWebCoreHandler
- .obtainMessage(WebCoreThread.CACHE_TICKER));
+ WebViewWorker.getHandler().removeMessages(
+ WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
+ WebViewWorker.getHandler().sendEmptyMessage(
+ WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
contentDraw();
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Set;
import java.util.Map.Entry;
}
if (mCacheDatabase != null) {
+ // use read_uncommitted to speed up READ
+ mCacheDatabase.execSQL("PRAGMA read_uncommitted = true;");
+ // as only READ can be called in the non-WebViewWorkerThread,
+ // and read_uncommitted is used, we can turn off database lock
+ // to use transaction.
+ mCacheDatabase.setLockingEnabled(false);
+
// use InsertHelper for faster insertion
mCacheInserter = new DatabaseUtils.InsertHelper(mCacheDatabase,
"cache");
}
//
- // cache functions, can only be called from WebCoreThread
+ // cache functions
//
+ // only called from WebViewWorkerThread
boolean startCacheTransaction() {
if (++mCacheTransactionRefcount == 1) {
+ if (!Thread.currentThread().equals(
+ WebViewWorker.getHandler().getLooper().getThread())) {
+ Log.w(LOGTAG, "startCacheTransaction should be called from "
+ + "WebViewWorkerThread instead of from "
+ + Thread.currentThread().getName());
+ }
mCacheDatabase.beginTransaction();
return true;
}
return false;
}
+ // only called from WebViewWorkerThread
boolean endCacheTransaction() {
if (--mCacheTransactionRefcount == 0) {
+ if (!Thread.currentThread().equals(
+ WebViewWorker.getHandler().getLooper().getThread())) {
+ Log.w(LOGTAG, "endCacheTransaction should be called from "
+ + "WebViewWorkerThread instead of from "
+ + Thread.currentThread().getName());
+ }
try {
mCacheDatabase.setTransactionSuccessful();
} finally {
return size;
}
- ArrayList<String> trimCache(long amount) {
+ List<String> trimCache(long amount) {
ArrayList<String> pathList = new ArrayList<String>(100);
Cursor cursor = mCacheDatabase.rawQuery(
"SELECT contentlength, filepath FROM cache ORDER BY expires ASC",
return pathList;
}
+ List<String> getAllCacheFileNames() {
+ ArrayList<String> pathList = null;
+ Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath FROM cache",
+ null);
+ if (cursor != null && cursor.moveToFirst()) {
+ pathList = new ArrayList<String>(cursor.getCount());
+ do {
+ pathList.add(cursor.getString(0));
+ } while (cursor.moveToNext());
+ }
+ cursor.close();
+ return pathList;
+ }
+
//
// password functions
//
--- /dev/null
+/*
+ * 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;
+ }
+ }
+ }
+}