You've already forked android_translation_layer
mirror of
https://gitlab.com/android_translation_layer/android_translation_layer.git
synced 2025-10-27 11:48:10 -07:00
make Looper, Handler, and MessageQueue work properly
this for example makes Unity apps not steal the main thread, hanging Gtk.
This commit is contained in:
@@ -59,6 +59,7 @@ libandroid_so = shared_library('android', [
|
|||||||
libtranslationlayer_so = shared_library('translation_layer_main', [
|
libtranslationlayer_so = shared_library('translation_layer_main', [
|
||||||
'src/api-impl-jni/egl/com_google_android_gles_jni_EGLImpl.c',
|
'src/api-impl-jni/egl/com_google_android_gles_jni_EGLImpl.c',
|
||||||
'src/api-impl-jni/android_os_Environment.c',
|
'src/api-impl-jni/android_os_Environment.c',
|
||||||
|
'src/api-impl-jni/android_os_MessageQueue.c',
|
||||||
'src/api-impl-jni/android_os_SystemClock.c',
|
'src/api-impl-jni/android_os_SystemClock.c',
|
||||||
'src/api-impl-jni/android_view_Window.c',
|
'src/api-impl-jni/android_view_Window.c',
|
||||||
'src/api-impl-jni/util.c',
|
'src/api-impl-jni/util.c',
|
||||||
|
|||||||
62
src/api-impl-jni/android_os_MessageQueue.c
Normal file
62
src/api-impl-jni/android_os_MessageQueue.c
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#include "defines.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* TODO put these in a header */
|
||||||
|
typedef void ALooper;
|
||||||
|
ALooper * ALooper_prepare(void);
|
||||||
|
void ALooper_wake(ALooper *looper);
|
||||||
|
bool ALooper_isPolling(ALooper *looper);
|
||||||
|
int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
|
||||||
|
|
||||||
|
struct native_message_queue {
|
||||||
|
ALooper *looper;
|
||||||
|
bool in_callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
JNIEXPORT jlong JNICALL Java_android_os_MessageQueue_nativeInit(JNIEnv *env, jclass this)
|
||||||
|
{
|
||||||
|
struct native_message_queue *message_queue = malloc(sizeof(struct native_message_queue));
|
||||||
|
|
||||||
|
message_queue->in_callback = false;
|
||||||
|
message_queue->looper = ALooper_prepare();
|
||||||
|
|
||||||
|
return _INTPTR(message_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativeDestroy(JNIEnv *env, jclass this, jlong ptr)
|
||||||
|
{
|
||||||
|
struct native_message_queue *message_queue = _PTR(ptr);
|
||||||
|
|
||||||
|
free(message_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativePollOnce(JNIEnv *env, jclass this, jlong ptr, jint timeout_millis)
|
||||||
|
{
|
||||||
|
struct native_message_queue *message_queue = _PTR(ptr);
|
||||||
|
|
||||||
|
// printf("Java_android_os_MessageQueue_nativePollOnce: entry (timeout: %d)\n", timeout_millis);
|
||||||
|
message_queue->in_callback = true;
|
||||||
|
ALooper_pollOnce(timeout_millis, NULL, NULL, NULL);
|
||||||
|
message_queue->in_callback = false;
|
||||||
|
// printf("Java_android_os_MessageQueue_nativePollOnce: exit\n");
|
||||||
|
|
||||||
|
/* TODO: what's with the exception stuff */
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativeWake(JNIEnv *env, jclass this, jlong ptr)
|
||||||
|
{
|
||||||
|
struct native_message_queue *message_queue = _PTR(ptr);
|
||||||
|
|
||||||
|
ALooper_wake(message_queue->looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL Java_android_os_MessageQueue_nativeIsIdling(JNIEnv *env, jclass this, jlong ptr)
|
||||||
|
{
|
||||||
|
struct native_message_queue *message_queue = _PTR(ptr);
|
||||||
|
|
||||||
|
return ALooper_isPolling(message_queue->looper);
|
||||||
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#include <time.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "generated_headers/android_os_SystemClock.h"
|
#include "generated_headers/android_os_SystemClock.h"
|
||||||
|
|
||||||
JNIEXPORT jlong JNICALL Java_android_os_SystemClock_elapsedRealtime(JNIEnv *env, jclass this)
|
JNIEXPORT jlong JNICALL Java_android_os_SystemClock_elapsedRealtime(JNIEnv *env, jclass this)
|
||||||
@@ -5,3 +8,10 @@ JNIEXPORT jlong JNICALL Java_android_os_SystemClock_elapsedRealtime(JNIEnv *env,
|
|||||||
printf("FIXME: Java_android_os_SystemClock_elapsedRealtime: returning 0\n");
|
printf("FIXME: Java_android_os_SystemClock_elapsedRealtime: returning 0\n");
|
||||||
return 0; // FIXME
|
return 0; // FIXME
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jlong JNICALL Java_android_os_SystemClock_uptimeMillis(JNIEnv *env, jclass this)
|
||||||
|
{
|
||||||
|
struct timespec t;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &t);
|
||||||
|
return t.tv_sec * 1000 + lround(t.tv_nsec / 1e6);
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,42 +10,42 @@ extern "C" {
|
|||||||
/*
|
/*
|
||||||
* Class: android_os_MessageQueue
|
* Class: android_os_MessageQueue
|
||||||
* Method: nativeInit
|
* Method: nativeInit
|
||||||
* Signature: ()I
|
* Signature: ()J
|
||||||
*/
|
*/
|
||||||
JNIEXPORT jint JNICALL Java_android_os_MessageQueue_nativeInit
|
JNIEXPORT jlong JNICALL Java_android_os_MessageQueue_nativeInit
|
||||||
(JNIEnv *, jclass);
|
(JNIEnv *, jclass);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: android_os_MessageQueue
|
* Class: android_os_MessageQueue
|
||||||
* Method: nativeDestroy
|
* Method: nativeDestroy
|
||||||
* Signature: (I)V
|
* Signature: (J)V
|
||||||
*/
|
*/
|
||||||
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativeDestroy
|
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativeDestroy
|
||||||
(JNIEnv *, jclass, jint);
|
(JNIEnv *, jclass, jlong);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: android_os_MessageQueue
|
* Class: android_os_MessageQueue
|
||||||
* Method: nativePollOnce
|
* Method: nativePollOnce
|
||||||
* Signature: (II)V
|
* Signature: (JI)V
|
||||||
*/
|
*/
|
||||||
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativePollOnce
|
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativePollOnce
|
||||||
(JNIEnv *, jclass, jint, jint);
|
(JNIEnv *, jclass, jlong, jint);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: android_os_MessageQueue
|
* Class: android_os_MessageQueue
|
||||||
* Method: nativeWake
|
* Method: nativeWake
|
||||||
* Signature: (I)V
|
* Signature: (J)V
|
||||||
*/
|
*/
|
||||||
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativeWake
|
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativeWake
|
||||||
(JNIEnv *, jclass, jint);
|
(JNIEnv *, jclass, jlong);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: android_os_MessageQueue
|
* Class: android_os_MessageQueue
|
||||||
* Method: nativeIsIdling
|
* Method: nativeIsIdling
|
||||||
* Signature: (I)Z
|
* Signature: (J)Z
|
||||||
*/
|
*/
|
||||||
JNIEXPORT jboolean JNICALL Java_android_os_MessageQueue_nativeIsIdling
|
JNIEXPORT jboolean JNICALL Java_android_os_MessageQueue_nativeIsIdling
|
||||||
(JNIEnv *, jclass, jint);
|
(JNIEnv *, jclass, jlong);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ extern "C" {
|
|||||||
JNIEXPORT jboolean JNICALL Java_android_os_SystemClock_setCurrentTimeMillis
|
JNIEXPORT jboolean JNICALL Java_android_os_SystemClock_setCurrentTimeMillis
|
||||||
(JNIEnv *, jclass, jlong);
|
(JNIEnv *, jclass, jlong);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: android_os_SystemClock
|
||||||
|
* Method: uptimeMillis
|
||||||
|
* Signature: ()J
|
||||||
|
*/
|
||||||
|
JNIEXPORT jlong JNICALL Java_android_os_SystemClock_uptimeMillis
|
||||||
|
(JNIEnv *, jclass);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: android_os_SystemClock
|
* Class: android_os_SystemClock
|
||||||
* Method: elapsedRealtime
|
* Method: elapsedRealtime
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import android.content.Intent;
|
|||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.XmlResourceParser;
|
import android.content.res.XmlResourceParser;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -102,6 +104,12 @@ public class Activity extends Context {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
System.out.println("- onCreate - yay!");
|
System.out.println("- onCreate - yay!");
|
||||||
|
|
||||||
|
/* TODO: this probably belongs elsewhere, but this is our entry point for better or worse */
|
||||||
|
Looper looper = Looper.myLooper();
|
||||||
|
if(looper == null) {
|
||||||
|
Looper.prepareMainLooper();
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +220,11 @@ public class Activity extends Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final void runOnUiThread(Runnable action) {
|
public final void runOnUiThread(Runnable action) {
|
||||||
action.run(); // FIXME: running synchronously for now
|
if(Looper.myLooper() == Looper.getMainLooper()) {
|
||||||
|
action.run();
|
||||||
|
} else {
|
||||||
|
new Handler(Looper.getMainLooper()).post(action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {}
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import android.view.WindowManager;
|
|||||||
import android.view.WindowManagerImpl;
|
import android.view.WindowManagerImpl;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
|
||||||
public class Context extends Object {
|
public class Context extends Object {
|
||||||
@@ -138,8 +139,14 @@ public class Context extends Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Looper getMainLooper() {
|
public Looper getMainLooper() {
|
||||||
System.out.println("returning the main Looper, most definitely doing just that!");
|
/* TODO: this is not what AOSP does, which could be a problem */
|
||||||
return new Looper();
|
Looper looper = Looper.myLooper();
|
||||||
|
if(looper == null) {
|
||||||
|
Looper.prepare();
|
||||||
|
looper = Looper.myLooper();
|
||||||
|
}
|
||||||
|
|
||||||
|
return looper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPackageName() {
|
public String getPackageName() {
|
||||||
@@ -251,9 +258,12 @@ public class Context extends Object {
|
|||||||
return new ComponentName("", "");
|
return new ComponentName("", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME - it should be *trivial* to do actually implement this
|
// TODO: do these both work? make them look more alike
|
||||||
public FileInputStream openFileInput(String name) {
|
public FileInputStream openFileInput(String name) throws FileNotFoundException {
|
||||||
return null;
|
System.out.println("openFileInput called for: '" + name + "'");
|
||||||
|
File file = new File(getFilesDir(), name);
|
||||||
|
|
||||||
|
return new FileInputStream(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileOutputStream openFileOutput(String name, int mode) throws java.io.FileNotFoundException {
|
public FileOutputStream openFileOutput(String name, int mode) throws java.io.FileNotFoundException {
|
||||||
|
|||||||
@@ -738,6 +738,7 @@ public final class Bundle implements Cloneable {
|
|||||||
*/
|
*/
|
||||||
public boolean getBoolean(String key, boolean defaultValue) {
|
public boolean getBoolean(String key, boolean defaultValue) {
|
||||||
Object o = mMap.get(key);
|
Object o = mMap.get(key);
|
||||||
|
System.out.println("bundle.getBoolean(" + key + ", " + defaultValue + ") called");
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ public class Handler {
|
|||||||
*/
|
*/
|
||||||
public Handler(Looper looper, Callback callback, boolean async) {
|
public Handler(Looper looper, Callback callback, boolean async) {
|
||||||
mLooper = looper;
|
mLooper = looper;
|
||||||
mQueue = null /*looper.mQueue*/;
|
mQueue = looper.mQueue;
|
||||||
mCallback = callback;
|
mCallback = callback;
|
||||||
mAsynchronous = async;
|
mAsynchronous = async;
|
||||||
}
|
}
|
||||||
@@ -316,9 +316,7 @@ public class Handler {
|
|||||||
* looper processing the message queue is exiting.
|
* looper processing the message queue is exiting.
|
||||||
*/
|
*/
|
||||||
public final boolean post(Runnable r) {
|
public final boolean post(Runnable r) {
|
||||||
// return sendMessageDelayed(getPostMessage(r), 0);
|
return sendMessageDelayed(getPostMessage(r), 0);
|
||||||
r.run();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -571,29 +569,14 @@ public class Handler {
|
|||||||
* occurs then the message will be dropped.
|
* occurs then the message will be dropped.
|
||||||
*/
|
*/
|
||||||
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
|
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
|
||||||
/* MessageQueue queue = mQueue;
|
MessageQueue queue = mQueue;
|
||||||
if (queue == null) {
|
if (queue == null) {
|
||||||
RuntimeException e = new RuntimeException(
|
RuntimeException e = new RuntimeException(
|
||||||
this + " sendMessageAtTime() called with no mQueue");
|
this + " sendMessageAtTime() called with no mQueue");
|
||||||
Log.w("Looper", e.getMessage(), e);
|
Log.w("Looper", e.getMessage(), e);
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
return enqueueMessage(queue, msg, uptimeMillis);*/
|
|
||||||
if (mCallback != null) {
|
|
||||||
// System.out.println("Handler.sendMessageAtTime: directly calling mCallback.handleMessage)");
|
|
||||||
if (msg.callback != null) {
|
|
||||||
msg.callback.run();
|
|
||||||
}
|
|
||||||
return mCallback.handleMessage(msg);
|
|
||||||
} else {
|
|
||||||
// System.out.println("Handler.sendMessageAtTime: not directly calling mCallback.handleMessage - mCallback is null)");
|
|
||||||
/* do this in this case as well?
|
|
||||||
if(msg.callback != null) {
|
|
||||||
msg.callback.run();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return true; // false?
|
|
||||||
}
|
}
|
||||||
|
return enqueueMessage(queue, msg, uptimeMillis);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -609,15 +592,14 @@ public class Handler {
|
|||||||
* looper processing the message queue is exiting.
|
* looper processing the message queue is exiting.
|
||||||
*/
|
*/
|
||||||
public final boolean sendMessageAtFrontOfQueue(Message msg) {
|
public final boolean sendMessageAtFrontOfQueue(Message msg) {
|
||||||
/*MessageQueue queue = mQueue;
|
MessageQueue queue = mQueue;
|
||||||
if (queue == null) {
|
if (queue == null) {
|
||||||
RuntimeException e = new RuntimeException(
|
RuntimeException e = new RuntimeException(
|
||||||
this + " sendMessageAtTime() called with no mQueue");
|
this + " sendMessageAtTime() called with no mQueue");
|
||||||
Log.w("Looper", e.getMessage(), e);
|
Log.w("Looper", e.getMessage(), e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return enqueueMessage(queue, msg, 0);*/
|
return enqueueMessage(queue, msg, 0);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
|
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
|
||||||
|
|||||||
@@ -1,19 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2006 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.os;
|
package android.os;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handy class for starting a new thread that has a looper. The looper can then be
|
||||||
|
* used to create handler classes. Note that start() must still be called.
|
||||||
|
*/
|
||||||
public class HandlerThread extends Thread {
|
public class HandlerThread extends Thread {
|
||||||
String name;
|
int mPriority;
|
||||||
|
int mTid = -1;
|
||||||
public HandlerThread() {}
|
Looper mLooper;
|
||||||
|
|
||||||
public HandlerThread(String name) {
|
public HandlerThread(String name) {
|
||||||
this.name = name;
|
super(name);
|
||||||
|
mPriority = Process.THREAD_PRIORITY_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
/**
|
||||||
// if(name.equals("FlurryAgent")) { return; }
|
* Constructs a HandlerThread.
|
||||||
|
* @param name
|
||||||
|
* @param priority The priority to run the thread at. The value supplied must be from
|
||||||
|
* {@link android.os.Process} and not from java.lang.Thread.
|
||||||
|
*/
|
||||||
|
public HandlerThread(String name, int priority) {
|
||||||
|
super(name);
|
||||||
|
mPriority = priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call back method that can be explicitly overridden if needed to execute some
|
||||||
|
* setup before Looper loops.
|
||||||
|
*/
|
||||||
|
protected void onLooperPrepared() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mTid = Process.myTid();
|
||||||
|
Looper.prepare();
|
||||||
|
synchronized (this) {
|
||||||
|
mLooper = Looper.myLooper();
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
Process.setThreadPriority(mPriority);
|
||||||
|
onLooperPrepared();
|
||||||
|
Looper.loop();
|
||||||
|
mTid = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the Looper associated with this thread. If this thread not been started
|
||||||
|
* or for any reason is isAlive() returns false, this method will return null. If this thread
|
||||||
|
* has been started, this method will block until the looper has been initialized.
|
||||||
|
* @return The looper.
|
||||||
|
*/
|
||||||
public Looper getLooper() {
|
public Looper getLooper() {
|
||||||
return new Looper();
|
if (!isAlive()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the thread has been started, wait until the looper has been created.
|
||||||
|
synchronized (this) {
|
||||||
|
while (isAlive() && mLooper == null) {
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mLooper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quits the handler thread's looper.
|
||||||
|
* <p>
|
||||||
|
* Causes the handler thread's looper to terminate without processing any
|
||||||
|
* more messages in the message queue.
|
||||||
|
* </p><p>
|
||||||
|
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
|
||||||
|
* For example, the {@link Handler#sendMessage(Message)} method will return false.
|
||||||
|
* </p><p class="note">
|
||||||
|
* Using this method may be unsafe because some messages may not be delivered
|
||||||
|
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
|
||||||
|
* that all pending work is completed in an orderly manner.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return True if the looper looper has been asked to quit or false if the
|
||||||
|
* thread had not yet started running.
|
||||||
|
*
|
||||||
|
* @see #quitSafely
|
||||||
|
*/
|
||||||
|
public boolean quit() {
|
||||||
|
Looper looper = getLooper();
|
||||||
|
if (looper != null) {
|
||||||
|
looper.quit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quits the handler thread's looper safely.
|
||||||
|
* <p>
|
||||||
|
* Causes the handler thread's looper to terminate as soon as all remaining messages
|
||||||
|
* in the message queue that are already due to be delivered have been handled.
|
||||||
|
* Pending delayed messages with due times in the future will not be delivered.
|
||||||
|
* </p><p>
|
||||||
|
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
|
||||||
|
* For example, the {@link Handler#sendMessage(Message)} method will return false.
|
||||||
|
* </p><p>
|
||||||
|
* If the thread has not been started or has finished (that is if
|
||||||
|
* {@link #getLooper} returns null), then false is returned.
|
||||||
|
* Otherwise the looper is asked to quit and true is returned.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return True if the looper looper has been asked to quit or false if the
|
||||||
|
* thread had not yet started running.
|
||||||
|
*/
|
||||||
|
public boolean quitSafely() {
|
||||||
|
Looper looper = getLooper();
|
||||||
|
if (looper != null) {
|
||||||
|
looper.quitSafely();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the identifier of this thread. See Process.myTid().
|
||||||
|
*/
|
||||||
|
public int getThreadId() {
|
||||||
|
return mTid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,13 +53,6 @@ import android.util.Printer;
|
|||||||
public final class Looper {
|
public final class Looper {
|
||||||
private static final String TAG = "Looper";
|
private static final String TAG = "Looper";
|
||||||
|
|
||||||
// FIXME
|
|
||||||
public Looper() {
|
|
||||||
mQueue = null;
|
|
||||||
mThread = Thread.currentThread();
|
|
||||||
System.out.println("making a fake Looper object, let's hope noone tries to actually use it");
|
|
||||||
}
|
|
||||||
|
|
||||||
// sThreadLocal.get() will return null unless you've called prepare().
|
// sThreadLocal.get() will return null unless you've called prepare().
|
||||||
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
|
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
|
||||||
private static Looper sMainLooper; // guarded by Looper.class
|
private static Looper sMainLooper; // guarded by Looper.class
|
||||||
@@ -84,7 +77,7 @@ public final class Looper {
|
|||||||
if (sThreadLocal.get() != null) {
|
if (sThreadLocal.get() != null) {
|
||||||
throw new RuntimeException("Only one Looper may be created per thread");
|
throw new RuntimeException("Only one Looper may be created per thread");
|
||||||
}
|
}
|
||||||
sThreadLocal.set(new Looper(/*quitAllowed*/));
|
sThreadLocal.set(new Looper(quitAllowed));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,10 +100,9 @@ public final class Looper {
|
|||||||
* Returns the application's main looper, which lives in the main thread of the application.
|
* Returns the application's main looper, which lives in the main thread of the application.
|
||||||
*/
|
*/
|
||||||
public static Looper getMainLooper() {
|
public static Looper getMainLooper() {
|
||||||
return new Looper();
|
synchronized (Looper.class) {
|
||||||
/* synchronized (Looper.class) {
|
return sMainLooper;
|
||||||
return sMainLooper;
|
}
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,51 +110,46 @@ public final class Looper {
|
|||||||
* {@link #quit()} to end the loop.
|
* {@link #quit()} to end the loop.
|
||||||
*/
|
*/
|
||||||
public static void loop() {
|
public static void loop() {
|
||||||
System.out.println("oops, Looper.loop called... and we don't implement that");
|
final Looper me = myLooper();
|
||||||
/*final Looper me = myLooper();
|
|
||||||
if (me == null) {
|
if (me == null) {
|
||||||
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
|
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
|
||||||
}
|
}
|
||||||
final MessageQueue queue = me.mQueue;
|
final MessageQueue queue = me.mQueue;
|
||||||
|
|
||||||
// Make sure the identity of this thread is that of the local process,
|
// Make sure the identity of this thread is that of the local process,
|
||||||
// and keep track of what that identity token actually is.
|
// and keep track of what that identity token actually is.
|
||||||
// Binder.clearCallingIdentity();
|
// Binder.clearCallingIdentity();
|
||||||
// final long ident = Binder.clearCallingIdentity();
|
// final long ident = Binder.clearCallingIdentity();
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
Message msg = queue.next(); // might block
|
Message msg = queue.next(); // might block
|
||||||
if (msg == null) {
|
if (msg == null) {
|
||||||
// No message indicates that the message queue is quitting.
|
// No message indicates that the message queue is quitting.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This must be in a local variable, in case a UI event sets the logger
|
// This must be in a local variable, in case a UI event sets the logger
|
||||||
Printer logging = me.mLogging;
|
Printer logging = me.mLogging;
|
||||||
if (logging != null) {
|
if (logging != null) {
|
||||||
logging.println(">>>>> Dispatching to " + msg.target + " " +
|
logging.println(">>>>> Dispatching to " + msg.target + " " +
|
||||||
msg.callback + ": " + msg.what);
|
msg.callback + ": " + msg.what);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.target.dispatchMessage(msg);
|
msg.target.dispatchMessage(msg);
|
||||||
|
|
||||||
if (logging != null) {
|
if (logging != null) {
|
||||||
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
|
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that during the course of dispatching the
|
// Make sure that during the course of dispatching the
|
||||||
// identity of the thread wasn't corrupted.
|
// identity of the thread wasn't corrupted.
|
||||||
// final long newIdent = Binder.clearCallingIdentity();
|
/* final long newIdent = Binder.clearCallingIdentity();
|
||||||
/* if (ident != newIdent) {
|
if (ident != newIdent) {
|
||||||
Log.wtf(TAG, "Thread identity changed from 0x"
|
Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what);
|
||||||
+ Long.toHexString(ident) + " to 0x"
|
}
|
||||||
+ Long.toHexString(newIdent) + " while dispatching to "
|
*/
|
||||||
+ msg.target.getClass().getName() + " "
|
msg.recycle();
|
||||||
+ msg.callback + " what=" + msg.what);
|
}
|
||||||
}* /
|
|
||||||
|
|
||||||
msg.recycle();
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -170,11 +157,7 @@ public final class Looper {
|
|||||||
* null if the calling thread is not associated with a Looper.
|
* null if the calling thread is not associated with a Looper.
|
||||||
*/
|
*/
|
||||||
public static Looper myLooper() {
|
public static Looper myLooper() {
|
||||||
return new Looper();
|
return sThreadLocal.get();
|
||||||
/* if(sThreadLocal.get() == null) {
|
|
||||||
prepare(false);
|
|
||||||
}
|
|
||||||
return sThreadLocal.get();*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -293,8 +276,7 @@ public final class Looper {
|
|||||||
* Return the Thread associated with this Looper.
|
* Return the Thread associated with this Looper.
|
||||||
*/
|
*/
|
||||||
public Thread getThread() {
|
public Thread getThread() {
|
||||||
return Thread.currentThread(); // ugly hack
|
return mThread;
|
||||||
// return mThread;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public final class MessageQueue {
|
|||||||
private final boolean mQuitAllowed;
|
private final boolean mQuitAllowed;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private int mPtr; // used by native code
|
private long mPtr; // used by native code
|
||||||
|
|
||||||
Message mMessages;
|
Message mMessages;
|
||||||
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
|
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
|
||||||
@@ -48,11 +48,11 @@ public final class MessageQueue {
|
|||||||
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
|
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
|
||||||
private int mNextBarrierToken;
|
private int mNextBarrierToken;
|
||||||
|
|
||||||
private native static int nativeInit();
|
private native static long nativeInit();
|
||||||
private native static void nativeDestroy(int ptr);
|
private native static void nativeDestroy(long ptr);
|
||||||
private native static void nativePollOnce(int ptr, int timeoutMillis);
|
private native static void nativePollOnce(long ptr, int timeoutMillis);
|
||||||
private native static void nativeWake(int ptr);
|
private native static void nativeWake(long ptr);
|
||||||
private native static boolean nativeIsIdling(int ptr);
|
private native static boolean nativeIsIdling(long ptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback interface for discovering when a thread is going to block
|
* Callback interface for discovering when a thread is going to block
|
||||||
|
|||||||
@@ -105,28 +105,25 @@ public final class SystemClock {
|
|||||||
* @param ms to sleep before returning, in milliseconds of uptime.
|
* @param ms to sleep before returning, in milliseconds of uptime.
|
||||||
*/
|
*/
|
||||||
public static void sleep(long ms) {
|
public static void sleep(long ms) {
|
||||||
System.out.println("!!! sleep(...) doesn't work, need to implement uptimeMillis");
|
long start = uptimeMillis();
|
||||||
/*
|
long duration = ms;
|
||||||
long start = uptimeMillis();
|
boolean interrupted = false;
|
||||||
long duration = ms;
|
do {
|
||||||
boolean interrupted = false;
|
try {
|
||||||
do {
|
Thread.sleep(duration);
|
||||||
try {
|
} catch (InterruptedException e) {
|
||||||
Thread.sleep(duration);
|
interrupted = true;
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) {
|
duration = start + ms - uptimeMillis();
|
||||||
interrupted = true;
|
} while (duration > 0);
|
||||||
}
|
|
||||||
duration = start + ms - uptimeMillis();
|
if (interrupted) {
|
||||||
} while (duration > 0);
|
// Important: we don't want to quietly eat an interrupt() event,
|
||||||
|
// so we make sure to re-interrupt the thread so that the next
|
||||||
if (interrupted) {
|
// call to Thread.sleep() or Object.wait() will be interrupted.
|
||||||
// Important: we don't want to quietly eat an interrupt() event,
|
Thread.currentThread().interrupt();
|
||||||
// so we make sure to re-interrupt the thread so that the next
|
}
|
||||||
// call to Thread.sleep() or Object.wait() will be interrupted.
|
}
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
*/}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current wall time, in milliseconds. Requires the calling
|
* Sets the current wall time, in milliseconds. Requires the calling
|
||||||
@@ -141,9 +138,7 @@ public final class SystemClock {
|
|||||||
*
|
*
|
||||||
* @return milliseconds of non-sleep uptime since boot.
|
* @return milliseconds of non-sleep uptime since boot.
|
||||||
*/
|
*/
|
||||||
/*native */ public static long uptimeMillis() {
|
native public static long uptimeMillis();
|
||||||
return 654000; // FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns milliseconds since boot, including time spent in sleep.
|
* Returns milliseconds since boot, including time spent in sleep.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ void ALooper_release(ALooper* looper) {
|
|||||||
_ZNK7android7RefBase9decStrongEPKv(looper, (void*)ALooper_acquire);
|
_ZNK7android7RefBase9decStrongEPKv(looper, (void*)ALooper_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _ZN7android6Looper7pollAllEiPiS1_PPv(ALooper *this, int timeoutMillis, int* outFd, int* outEvents, void** outData);
|
int _ZN7android6Looper7pollAllEiPiS1_PPv(ALooper *this, int timeoutMillis, int *outFd, int *outEvents, void **outData);
|
||||||
int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData)
|
int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData)
|
||||||
{
|
{
|
||||||
ALooper *looper = ALooper_forThread();
|
ALooper *looper = ALooper_forThread();
|
||||||
@@ -49,12 +50,34 @@ int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outDat
|
|||||||
return _ZN7android6Looper7pollAllEiPiS1_PPv(looper, timeoutMillis, outFd, outEvents, outData);
|
return _ZN7android6Looper7pollAllEiPiS1_PPv(looper, timeoutMillis, outFd, outEvents, outData);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _ZN7android6Looper5addFdEiiiPFiiiPvES1_(ALooper *this, int fd, int ident, int events, Looper_callbackFunc callback, void* data);
|
int _ZN7android6Looper8pollOnceEiPiS1_PPv(ALooper *this, int timeoutMillis, int *outFd, int *outEvents, void **outData);
|
||||||
|
int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)
|
||||||
|
{
|
||||||
|
ALooper *looper = ALooper_forThread();
|
||||||
|
if(!looper) {
|
||||||
|
fprintf(stderr, "ALooper_pollAll: ALooper_forThread returned NULL\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _ZN7android6Looper8pollOnceEiPiS1_PPv(looper, timeoutMillis, outFd, outEvents, outData);
|
||||||
|
}
|
||||||
|
|
||||||
|
int _ZN7android6Looper5addFdEiiiPFiiiPvES1_(ALooper *this, int fd, int ident, int events, Looper_callbackFunc callback, void *data);
|
||||||
int ALooper_addFd(ALooper* looper, int fd, int ident, int events, Looper_callbackFunc callback, void* data)
|
int ALooper_addFd(ALooper* looper, int fd, int ident, int events, Looper_callbackFunc callback, void* data)
|
||||||
{
|
{
|
||||||
return _ZN7android6Looper5addFdEiiiPFiiiPvES1_(looper, fd, ident, events, callback, data);
|
return _ZN7android6Looper5addFdEiiiPFiiiPvES1_(looper, fd, ident, events, callback, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _ZN7android6Looper4wakeEv(ALooper *this);
|
void _ZN7android6Looper4wakeEv(ALooper *this);
|
||||||
void ALooper_wake(ALooper* looper) {
|
void ALooper_wake(ALooper *looper)
|
||||||
_ZN7android6Looper4wakeEv(looper);
|
{
|
||||||
|
_ZN7android6Looper4wakeEv(looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is not part of the android API, but we use it internally */
|
||||||
|
|
||||||
|
bool _ZNK7android6Looper9isPollingEv(ALooper *this);
|
||||||
|
bool ALooper_isPolling(ALooper *looper)
|
||||||
|
{
|
||||||
|
return _ZNK7android6Looper9isPollingEv(looper);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user