Home | History | Annotate | Download | only in webkit
      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