Introduction
Provide a multiple-async-waiting operation management pattern for Android.
Background
Commonly, when we have some IO FileDescriptor
(File, Pipe, Socket, etc.) to do the async-operation, we have to create a dedicated thread to handle the async waiting. If some modules/components require the async waiting simultaneously, we may have to create multiple threads, these will cause some resource wasting.
To reduce the resource consumption and simplify the multiple-async-waiting management, we will create a pattern/method to handle the async waiting in a single dedicated thread.
Using the Code
This article provides native(C++) implementation & Java implementation.
Native(C++) Implementation
Prepare the looper for Native Implementation
void *looperThreadCallback(void *data) {
*(ALooper **) data = ALooper_prepare(0);
ALooper_pollAll(-1, 0, NULL, NULL); return NULL;
}
ALooper *createLooper(const char *threadName) {
ALooper *looper = NULL;
pthread_t t;
pthread_create(&t, NULL, looperThreadCallback, &looper);
pthread_setname_np(t, threadName);
pthread_detach(t); while (looper == NULL) { usleep(0); } return looper;
}
void createDefaultLooper() {
g_defaultWaitIOLooper = createLooper("DefaultNativeIO");
}
Native main Function
void *registerWaitForSingleFD(int fd, int events, ALooper_callbackFunc func,
void *data, bool isLongRunListener) {
LOGD("registerWaitForSingleFD: fd=%d events=%d", fd, events);
ALooper *looper = NULL;
if (isLongRunListener) {
looper = createLooper("LongRunNativeIO");
} else {
pthread_once(&g_defaultWaitIOLooper_once_t, createDefaultLooper);
looper = g_defaultWaitIOLooper;
}
ALooper_addFd(looper, fd, 0, events, func, data);
return looper;
}
void unregisterWaitForSingleFD(void *hWait, int fd) {
LOGD("unregisterWaitForSingleFD: fd=%d", fd);
ALooper_removeFd((ALooper *) hWait, fd);
if (hWait != g_defaultWaitIOLooper) { ALooper_wake((ALooper *) hWait); }
}
Java Implementation
Prepare the looper for Java Implementation
private static final HandlerThread mHandlerThread = new HandlerThread("DefaultJavaIO") {{
start();
}};
Java main Function
public static WaitIOThreadPool registerWaitForSingleFD(FileDescriptor fd, int events,
MessageQueue.OnFileDescriptorEventListener listener, boolean isLongRunListener) {
final Looper looper;
if (isLongRunListener) {
looper = new HandlerThread("LongRunJavaIO") {{
start();
}}.getLooper();
} else {
looper = mHandlerThread.getLooper();
}
looper.getQueue().addOnFileDescriptorEventListener(fd, events, listener);
return new WaitIOThreadPool(looper, isLongRunListener);
}
public void unregisterWaitForSingleFD(FileDescriptor fd) {
mLooper.getQueue().removeOnFileDescriptorEventListener(fd);
if (mIsLongRunListener) {
mLooper.quitSafely();
}
}
In this way, we can handle the multiple-async-waiting in a single dedicated thread for multiple modules/components.
Points of Interest
- Android has provided the threadpool implementation in Java side, so we can execute the async callback(
MessageQueue.OnFileDescriptorEventListener
) in threadpool instead of inline execution (using by the demo code). Because the inline executation may cause some latency on IO data handling. - You even can utilize the main looper(in the UI thread) as the waitor, then no additional dedicated thread require
History
- 8th January, 2018: Initial post