diff options
| -rw-r--r-- | api/current.xml | 4 | ||||
| -rw-r--r-- | core/java/android/webkit/BrowserFrame.java | 3 | ||||
| -rw-r--r-- | core/java/android/webkit/CacheManager.java | 92 | ||||
| -rw-r--r-- | core/java/android/webkit/FrameLoader.java | 35 | ||||
| -rw-r--r-- | core/java/android/webkit/LoadListener.java | 118 | ||||
| -rw-r--r-- | core/java/android/webkit/StreamLoader.java | 45 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewCore.java | 62 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewDatabase.java | 40 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewWorker.java | 223 |
9 files changed, 418 insertions, 204 deletions
diff --git a/api/current.xml b/api/current.xml index 3b90f38106fd..89af2acddc34 100644 --- a/api/current.xml +++ b/api/current.xml @@ -189783,7 +189783,7 @@ synchronized="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </method> @@ -189835,7 +189835,7 @@ synchronized="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </method> diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 3f1672af29bf..1c0d55f3392f 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -407,7 +407,8 @@ class BrowserFrame extends Handler { } } } - CacheManager.trimCacheIfNeeded(); + WebViewWorker.getHandler().sendEmptyMessage( + WebViewWorker.MSG_TRIM_CACHE); break; } diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java index 647556b5ad55..1c59c10685d8 100644 --- a/core/java/android/webkit/CacheManager.java +++ b/core/java/android/webkit/CacheManager.java @@ -25,10 +25,11 @@ import java.io.File; 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; @@ -200,9 +201,9 @@ public final class CacheManager { // 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; @@ -223,7 +224,6 @@ public final class CacheManager { * * @param disabled true to disable the cache */ - // only called from WebCore thread static void setCacheDisabled(boolean disabled) { if (disabled == mDisabled) { return; @@ -243,7 +243,7 @@ public final class CacheManager { 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) { @@ -253,12 +253,9 @@ public final class CacheManager { 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; @@ -266,15 +263,15 @@ public final class CacheManager { 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; @@ -283,6 +280,26 @@ public final class CacheManager { 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, @@ -291,13 +308,11 @@ public final class CacheManager { * * @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) { @@ -368,14 +383,12 @@ public final class CacheManager { * @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) { @@ -435,12 +448,10 @@ public final class CacheManager { * 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 { @@ -489,7 +500,6 @@ public final class CacheManager { * * @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. @@ -499,7 +509,10 @@ public final class CacheManager { 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 @@ -517,8 +530,6 @@ public final class CacheManager { } catch (SecurityException e) { // Ignore SecurityExceptions. } - // delete database - mDataBase.clearCache(); } }; new Thread(clearCache).start(); @@ -528,15 +539,13 @@ public final class CacheManager { /** * 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)); @@ -544,9 +553,34 @@ public final class CacheManager { 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 diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java index b13c4055bc8a..790363227bb9 100644 --- a/core/java/android/webkit/FrameLoader.java +++ b/core/java/android/webkit/FrameLoader.java @@ -110,7 +110,9 @@ class FrameLoader { 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; } @@ -142,24 +144,33 @@ class FrameLoader { } 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 @@ -172,8 +183,8 @@ class FrameLoader { } return false; } - - private boolean handleHTTPLoad() { + + boolean handleHTTPLoad() { if (mHeaders == null) { mHeaders = new HashMap<String, String>(); } @@ -229,7 +240,9 @@ class FrameLoader { 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(); } /* diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java index cdc6608ea1f6..8bacee4be8f4 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -101,7 +101,6 @@ class LoadListener extends Handler implements EventHandler { 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; @@ -301,6 +300,12 @@ class LoadListener extends Handler implements EventHandler { */ 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)); } @@ -316,11 +321,6 @@ class LoadListener extends Handler implements EventHandler { 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; @@ -454,12 +454,19 @@ class LoadListener extends Handler implements EventHandler { 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(); } @@ -649,7 +656,10 @@ class LoadListener extends Handler implements EventHandler { // 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()); @@ -708,8 +718,10 @@ class LoadListener extends Handler implements EventHandler { 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; } @@ -934,12 +946,9 @@ class LoadListener extends Handler implements EventHandler { * 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(), @@ -1098,24 +1107,15 @@ class LoadListener extends Handler implements EventHandler { 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"); } } @@ -1125,18 +1125,16 @@ class LoadListener extends Handler implements EventHandler { * 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(); @@ -1194,10 +1192,8 @@ class LoadListener extends Handler implements EventHandler { mRequestHandle = null; } - if (mCacheResult != null) { - CacheManager.cleanupCacheFile(mCacheResult); - mCacheResult = null; - } + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget(); mCancelled = true; clearNativeLoader(); @@ -1258,14 +1254,16 @@ class LoadListener extends Handler implements EventHandler { } // 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 diff --git a/core/java/android/webkit/StreamLoader.java b/core/java/android/webkit/StreamLoader.java index 4c3299773dd9..7bcd50dd4677 100644 --- a/core/java/android/webkit/StreamLoader.java +++ b/core/java/android/webkit/StreamLoader.java @@ -20,8 +20,6 @@ import android.content.Context; 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; @@ -61,11 +59,6 @@ abstract class StreamLoader implements Handler.Callback { // 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 @@ -97,26 +90,6 @@ abstract class StreamLoader implements Handler.Callback { 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. @@ -228,22 +201,4 @@ abstract class StreamLoader implements Handler.Callback { } 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(); - } - } - } } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 4606bc6ffa1b..71f69feeb19d 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -587,13 +587,6 @@ final class WebViewCore { 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(); @@ -619,28 +612,6 @@ final class WebViewCore { 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; } } }; @@ -1092,23 +1063,15 @@ final class WebViewCore { 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: @@ -1851,16 +1814,6 @@ final class WebViewCore { .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 @@ -1992,9 +1945,10 @@ final class WebViewCore { 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(); } diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java index 110e4f817c8f..a8709312b3b4 100644 --- a/core/java/android/webkit/WebViewDatabase.java +++ b/core/java/android/webkit/WebViewDatabase.java @@ -19,6 +19,7 @@ package android.webkit; 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; @@ -234,6 +235,13 @@ public class WebViewDatabase { } 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"); @@ -548,19 +556,33 @@ public class WebViewDatabase { } // - // 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 { @@ -684,7 +706,7 @@ public class WebViewDatabase { 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", @@ -727,6 +749,20 @@ public class WebViewDatabase { 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 // diff --git a/core/java/android/webkit/WebViewWorker.java b/core/java/android/webkit/WebViewWorker.java new file mode 100644 index 000000000000..c488150f182e --- /dev/null +++ b/core/java/android/webkit/WebViewWorker.java @@ -0,0 +1,223 @@ +/* + * 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; + } + } + } +} |