blob: 49efcf9e60e377d8479986c4282d0f5ca9d33909 [file] [log] [blame]
/*
* Copyright (C) 2015 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.support.v7.util;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.content.ParallelExecutorCompat;
import android.util.Log;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
class MessageThreadUtil<T> implements ThreadUtil<T> {
public MainThreadCallback<T> getMainThreadProxy(final MainThreadCallback<T> callback) {
return new MainThreadCallback<T>() {
final private MessageQueue mQueue = new MessageQueue();
final private Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private static final int UPDATE_ITEM_COUNT = 1;
private static final int ADD_TILE = 2;
private static final int REMOVE_TILE = 3;
@Override
public void updateItemCount(int generation, int itemCount) {
sendMessage(SyncQueueItem.obtainMessage(UPDATE_ITEM_COUNT, generation, itemCount));
}
@Override
public void addTile(int generation, TileList.Tile<T> tile) {
sendMessage(SyncQueueItem.obtainMessage(ADD_TILE, generation, tile));
}
@Override
public void removeTile(int generation, int position) {
sendMessage(SyncQueueItem.obtainMessage(REMOVE_TILE, generation, position));
}
private void sendMessage(SyncQueueItem msg) {
mQueue.sendMessage(msg);
mMainThreadHandler.post(mMainThreadRunnable);
}
private Runnable mMainThreadRunnable = new Runnable() {
@Override
public void run() {
SyncQueueItem msg = mQueue.next();
while (msg != null) {
switch (msg.what) {
case UPDATE_ITEM_COUNT:
callback.updateItemCount(msg.arg1, msg.arg2);
break;
case ADD_TILE:
//noinspection unchecked
callback.addTile(msg.arg1, (TileList.Tile<T>) msg.data);
break;
case REMOVE_TILE:
callback.removeTile(msg.arg1, msg.arg2);
break;
default:
Log.e("ThreadUtil", "Unsupported message, what=" + msg.what);
}
msg = mQueue.next();
}
}
};
};
}
public BackgroundCallback<T> getBackgroundProxy(final BackgroundCallback<T> callback) {
return new BackgroundCallback<T>() {
final private MessageQueue mQueue = new MessageQueue();
final private Executor mExecutor = ParallelExecutorCompat.getParallelExecutor();
AtomicBoolean mBackgroundRunning = new AtomicBoolean(false);
private static final int REFRESH = 1;
private static final int UPDATE_RANGE = 2;
private static final int LOAD_TILE = 3;
private static final int RECYCLE_TILE = 4;
@Override
public void refresh(int generation) {
sendMessageAtFrontOfQueue(SyncQueueItem.obtainMessage(REFRESH, generation, null));
}
@Override
public void updateRange(int rangeStart, int rangeEnd,
int extRangeStart, int extRangeEnd, int scrollHint) {
sendMessageAtFrontOfQueue(SyncQueueItem.obtainMessage(UPDATE_RANGE,
rangeStart, rangeEnd, extRangeStart, extRangeEnd, scrollHint, null));
}
@Override
public void loadTile(int position, int scrollHint) {
sendMessage(SyncQueueItem.obtainMessage(LOAD_TILE, position, scrollHint));
}
@Override
public void recycleTile(TileList.Tile<T> tile) {
sendMessage(SyncQueueItem.obtainMessage(RECYCLE_TILE, 0, tile));
}
private void sendMessage(SyncQueueItem msg) {
mQueue.sendMessage(msg);
maybeExecuteBackgroundRunnable();
}
private void sendMessageAtFrontOfQueue(SyncQueueItem msg) {
mQueue.sendMessageAtFrontOfQueue(msg);
maybeExecuteBackgroundRunnable();
}
private void maybeExecuteBackgroundRunnable() {
if (mBackgroundRunning.compareAndSet(false, true)) {
mExecutor.execute(mBackgroundRunnable);
}
}
private Runnable mBackgroundRunnable = new Runnable() {
@Override
public void run() {
while (true) {
SyncQueueItem msg = mQueue.next();
if (msg == null) {
break;
}
switch (msg.what) {
case REFRESH:
mQueue.removeMessages(REFRESH);
callback.refresh(msg.arg1);
break;
case UPDATE_RANGE:
mQueue.removeMessages(UPDATE_RANGE);
mQueue.removeMessages(LOAD_TILE);
callback.updateRange(
msg.arg1, msg.arg2, msg.arg3, msg.arg4, msg.arg5);
break;
case LOAD_TILE:
callback.loadTile(msg.arg1, msg.arg2);
break;
case RECYCLE_TILE:
//noinspection unchecked
callback.recycleTile((TileList.Tile<T>) msg.data);
break;
default:
Log.e("ThreadUtil", "Unsupported message, what=" + msg.what);
}
}
mBackgroundRunning.set(false);
}
};
};
}
/**
* Replica of android.os.Message. Unfortunately, cannot use it without a Handler and don't want
* to create a thread just for this component.
*/
static class SyncQueueItem {
private static SyncQueueItem sPool;
private static final Object sPoolLock = new Object();
private SyncQueueItem next;
public int what;
public int arg1;
public int arg2;
public int arg3;
public int arg4;
public int arg5;
public Object data;
void recycle() {
next = null;
what = arg1 = arg2 = arg3 = arg4 = arg5 = 0;
data = null;
synchronized (sPoolLock) {
if (sPool != null) {
next = sPool;
}
sPool = this;
}
}
static SyncQueueItem obtainMessage(int what, int arg1, int arg2, int arg3, int arg4,
int arg5, Object data) {
synchronized (sPoolLock) {
final SyncQueueItem item;
if (sPool == null) {
item = new SyncQueueItem();
} else {
item = sPool;
sPool = sPool.next;
item.next = null;
}
item.what = what;
item.arg1 = arg1;
item.arg2 = arg2;
item.arg3 = arg3;
item.arg4 = arg4;
item.arg5 = arg5;
item.data = data;
return item;
}
}
static SyncQueueItem obtainMessage(int what, int arg1, int arg2) {
return obtainMessage(what, arg1, arg2, 0, 0, 0, null);
}
static SyncQueueItem obtainMessage(int what, int arg1, Object data) {
return obtainMessage(what, arg1, 0, 0, 0, 0, data);
}
}
static class MessageQueue {
private SyncQueueItem mRoot;
synchronized SyncQueueItem next() {
if (mRoot == null) {
return null;
}
final SyncQueueItem next = mRoot;
mRoot = mRoot.next;
return next;
}
synchronized void sendMessageAtFrontOfQueue(SyncQueueItem item) {
item.next = mRoot;
mRoot = item;
}
synchronized void sendMessage(SyncQueueItem item) {
if (mRoot == null) {
mRoot = item;
return;
}
SyncQueueItem last = mRoot;
while (last.next != null) {
last = last.next;
}
last.next = item;
}
synchronized void removeMessages(int what) {
while (mRoot != null && mRoot.what == what) {
SyncQueueItem item = mRoot;
mRoot = mRoot.next;
item.recycle();
}
if (mRoot != null) {
SyncQueueItem prev = mRoot;
SyncQueueItem item = prev.next;
while (item != null) {
SyncQueueItem next = item.next;
if (item.what == what) {
prev.next = next;
item.recycle();
} else {
prev = item;
}
item = next;
}
}
}
}
}