1 /* 2 * Copyright (C) 2015 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.support.v7.util; 18 19 import android.os.Handler; 20 import android.os.Looper; 21 import android.support.v4.content.ParallelExecutorCompat; 22 import android.util.Log; 23 24 import java.util.concurrent.Executor; 25 import java.util.concurrent.atomic.AtomicBoolean; 26 27 class MessageThreadUtil<T> implements ThreadUtil<T> { 28 getMainThreadProxy(final MainThreadCallback<T> callback)29 public MainThreadCallback<T> getMainThreadProxy(final MainThreadCallback<T> callback) { 30 return new MainThreadCallback<T>() { 31 final private MessageQueue mQueue = new MessageQueue(); 32 final private Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); 33 34 private static final int UPDATE_ITEM_COUNT = 1; 35 private static final int ADD_TILE = 2; 36 private static final int REMOVE_TILE = 3; 37 38 @Override 39 public void updateItemCount(int generation, int itemCount) { 40 sendMessage(SyncQueueItem.obtainMessage(UPDATE_ITEM_COUNT, generation, itemCount)); 41 } 42 43 @Override 44 public void addTile(int generation, TileList.Tile<T> tile) { 45 sendMessage(SyncQueueItem.obtainMessage(ADD_TILE, generation, tile)); 46 } 47 48 @Override 49 public void removeTile(int generation, int position) { 50 sendMessage(SyncQueueItem.obtainMessage(REMOVE_TILE, generation, position)); 51 } 52 53 private void sendMessage(SyncQueueItem msg) { 54 mQueue.sendMessage(msg); 55 mMainThreadHandler.post(mMainThreadRunnable); 56 } 57 58 private Runnable mMainThreadRunnable = new Runnable() { 59 @Override 60 public void run() { 61 SyncQueueItem msg = mQueue.next(); 62 while (msg != null) { 63 switch (msg.what) { 64 case UPDATE_ITEM_COUNT: 65 callback.updateItemCount(msg.arg1, msg.arg2); 66 break; 67 case ADD_TILE: 68 //noinspection unchecked 69 callback.addTile(msg.arg1, (TileList.Tile<T>) msg.data); 70 break; 71 case REMOVE_TILE: 72 callback.removeTile(msg.arg1, msg.arg2); 73 break; 74 default: 75 Log.e("ThreadUtil", "Unsupported message, what=" + msg.what); 76 } 77 msg = mQueue.next(); 78 } 79 } 80 }; 81 }; 82 } 83 getBackgroundProxy(final BackgroundCallback<T> callback)84 public BackgroundCallback<T> getBackgroundProxy(final BackgroundCallback<T> callback) { 85 return new BackgroundCallback<T>() { 86 final private MessageQueue mQueue = new MessageQueue(); 87 final private Executor mExecutor = ParallelExecutorCompat.getParallelExecutor(); 88 AtomicBoolean mBackgroundRunning = new AtomicBoolean(false); 89 90 private static final int REFRESH = 1; 91 private static final int UPDATE_RANGE = 2; 92 private static final int LOAD_TILE = 3; 93 private static final int RECYCLE_TILE = 4; 94 95 @Override 96 public void refresh(int generation) { 97 sendMessageAtFrontOfQueue(SyncQueueItem.obtainMessage(REFRESH, generation, null)); 98 } 99 100 @Override 101 public void updateRange(int rangeStart, int rangeEnd, 102 int extRangeStart, int extRangeEnd, int scrollHint) { 103 sendMessageAtFrontOfQueue(SyncQueueItem.obtainMessage(UPDATE_RANGE, 104 rangeStart, rangeEnd, extRangeStart, extRangeEnd, scrollHint, null)); 105 } 106 107 @Override 108 public void loadTile(int position, int scrollHint) { 109 sendMessage(SyncQueueItem.obtainMessage(LOAD_TILE, position, scrollHint)); 110 } 111 112 @Override 113 public void recycleTile(TileList.Tile<T> tile) { 114 sendMessage(SyncQueueItem.obtainMessage(RECYCLE_TILE, 0, tile)); 115 } 116 117 private void sendMessage(SyncQueueItem msg) { 118 mQueue.sendMessage(msg); 119 maybeExecuteBackgroundRunnable(); 120 } 121 122 private void sendMessageAtFrontOfQueue(SyncQueueItem msg) { 123 mQueue.sendMessageAtFrontOfQueue(msg); 124 maybeExecuteBackgroundRunnable(); 125 } 126 127 private void maybeExecuteBackgroundRunnable() { 128 if (mBackgroundRunning.compareAndSet(false, true)) { 129 mExecutor.execute(mBackgroundRunnable); 130 } 131 } 132 133 private Runnable mBackgroundRunnable = new Runnable() { 134 @Override 135 public void run() { 136 while (true) { 137 SyncQueueItem msg = mQueue.next(); 138 if (msg == null) { 139 break; 140 } 141 switch (msg.what) { 142 case REFRESH: 143 mQueue.removeMessages(REFRESH); 144 callback.refresh(msg.arg1); 145 break; 146 case UPDATE_RANGE: 147 mQueue.removeMessages(UPDATE_RANGE); 148 mQueue.removeMessages(LOAD_TILE); 149 callback.updateRange( 150 msg.arg1, msg.arg2, msg.arg3, msg.arg4, msg.arg5); 151 break; 152 case LOAD_TILE: 153 callback.loadTile(msg.arg1, msg.arg2); 154 break; 155 case RECYCLE_TILE: 156 //noinspection unchecked 157 callback.recycleTile((TileList.Tile<T>) msg.data); 158 break; 159 default: 160 Log.e("ThreadUtil", "Unsupported message, what=" + msg.what); 161 } 162 } 163 mBackgroundRunning.set(false); 164 } 165 }; 166 }; 167 } 168 169 /** 170 * Replica of android.os.Message. Unfortunately, cannot use it without a Handler and don't want 171 * to create a thread just for this component. 172 */ 173 static class SyncQueueItem { 174 175 private static SyncQueueItem sPool; 176 private static final Object sPoolLock = new Object(); 177 private SyncQueueItem next; 178 public int what; 179 public int arg1; 180 public int arg2; 181 public int arg3; 182 public int arg4; 183 public int arg5; 184 public Object data; 185 186 void recycle() { 187 next = null; 188 what = arg1 = arg2 = arg3 = arg4 = arg5 = 0; 189 data = null; 190 synchronized (sPoolLock) { 191 if (sPool != null) { 192 next = sPool; 193 } 194 sPool = this; 195 } 196 } 197 198 static SyncQueueItem obtainMessage(int what, int arg1, int arg2, int arg3, int arg4, 199 int arg5, Object data) { 200 synchronized (sPoolLock) { 201 final SyncQueueItem item; 202 if (sPool == null) { 203 item = new SyncQueueItem(); 204 } else { 205 item = sPool; 206 sPool = sPool.next; 207 item.next = null; 208 } 209 item.what = what; 210 item.arg1 = arg1; 211 item.arg2 = arg2; 212 item.arg3 = arg3; 213 item.arg4 = arg4; 214 item.arg5 = arg5; 215 item.data = data; 216 return item; 217 } 218 } 219 220 static SyncQueueItem obtainMessage(int what, int arg1, int arg2) { 221 return obtainMessage(what, arg1, arg2, 0, 0, 0, null); 222 } 223 224 static SyncQueueItem obtainMessage(int what, int arg1, Object data) { 225 return obtainMessage(what, arg1, 0, 0, 0, 0, data); 226 } 227 } 228 229 static class MessageQueue { 230 231 private SyncQueueItem mRoot; 232 233 synchronized SyncQueueItem next() { 234 if (mRoot == null) { 235 return null; 236 } 237 final SyncQueueItem next = mRoot; 238 mRoot = mRoot.next; 239 return next; 240 } 241 242 synchronized void sendMessageAtFrontOfQueue(SyncQueueItem item) { 243 item.next = mRoot; 244 mRoot = item; 245 } 246 247 synchronized void sendMessage(SyncQueueItem item) { 248 if (mRoot == null) { 249 mRoot = item; 250 return; 251 } 252 SyncQueueItem last = mRoot; 253 while (last.next != null) { 254 last = last.next; 255 } 256 last.next = item; 257 } 258 259 synchronized void removeMessages(int what) { 260 while (mRoot != null && mRoot.what == what) { 261 SyncQueueItem item = mRoot; 262 mRoot = mRoot.next; 263 item.recycle(); 264 } 265 if (mRoot != null) { 266 SyncQueueItem prev = mRoot; 267 SyncQueueItem item = prev.next; 268 while (item != null) { 269 SyncQueueItem next = item.next; 270 if (item.what == what) { 271 prev.next = next; 272 item.recycle(); 273 } else { 274 prev = item; 275 } 276 item = next; 277 } 278 } 279 } 280 } 281 } 282