Merge "Fix crash due to texture view callback threading invariants."

This commit is contained in:
Jeff Brown
2014-05-07 00:47:08 +00:00
committed by Android (Google) Code Review
4 changed files with 72 additions and 68 deletions

View File

@@ -10734,6 +10734,7 @@ package android.graphics {
method public void releaseTexImage();
method public void setDefaultBufferSize(int, int);
method public void setOnFrameAvailableListener(android.graphics.SurfaceTexture.OnFrameAvailableListener);
method public void setOnFrameAvailableListener(android.graphics.SurfaceTexture.OnFrameAvailableListener, android.os.Handler);
method public void updateTexImage();
}

View File

@@ -23,7 +23,6 @@ import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
@@ -119,8 +118,6 @@ public class TextureView extends View {
private boolean mUpdateLayer;
private boolean mUpdateSurface;
private SurfaceTexture.OnFrameAvailableListener mUpdateListener;
private Canvas mCanvas;
private int mSaveCount;
@@ -370,21 +367,7 @@ public class TextureView extends View {
mSurface.setDefaultBufferSize(getWidth(), getHeight());
nCreateNativeWindow(mSurface);
mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
// Per SurfaceTexture's documentation, the callback may be invoked
// from an arbitrary thread
updateLayer();
if (Looper.myLooper() == Looper.getMainLooper()) {
invalidate();
} else {
postInvalidate();
}
}
};
mSurface.setOnFrameAvailableListener(mUpdateListener);
mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
if (mListener != null && !mUpdateSurface) {
mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
@@ -422,7 +405,7 @@ public class TextureView extends View {
// To cancel updates, the easiest thing to do is simply to remove the
// updates listener
if (visibility == VISIBLE) {
mSurface.setOnFrameAvailableListener(mUpdateListener);
mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
updateLayerAndInvalidate();
} else {
mSurface.setOnFrameAvailableListener(null);
@@ -767,6 +750,15 @@ public class TextureView extends View {
mListener = listener;
}
private final SurfaceTexture.OnFrameAvailableListener mUpdateListener =
new SurfaceTexture.OnFrameAvailableListener() {
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
updateLayer();
invalidate();
}
};
/**
* This listener can be used to be notified when the surface texture
* associated with this texture view is available.

View File

@@ -221,7 +221,7 @@ static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz)
}
fields.postEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;)V");
"(Ljava/lang/ref/WeakReference;)V");
if (fields.postEvent == NULL) {
ALOGE("can't find android/graphics/SurfaceTexture.postEventFromNative");
}
@@ -338,7 +338,7 @@ static void SurfaceTexture_release(JNIEnv* env, jobject thiz)
static JNINativeMethod gSurfaceTextureMethods[] = {
{"nativeClassInit", "()V", (void*)SurfaceTexture_classInit },
{"nativeInit", "(IZLjava/lang/Object;)V", (void*)SurfaceTexture_init },
{"nativeInit", "(IZLjava/lang/ref/WeakReference;)V", (void*)SurfaceTexture_init },
{"nativeFinalize", "()V", (void*)SurfaceTexture_finalize },
{"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize },
{"nativeUpdateTexImage", "()V", (void*)SurfaceTexture_updateTexImage },

View File

@@ -62,9 +62,8 @@ import android.view.Surface;
* #updateTexImage} should not be called directly from the callback.
*/
public class SurfaceTexture {
private EventHandler mEventHandler;
private OnFrameAvailableListener mOnFrameAvailableListener;
private final Looper mCreatorLooper;
private Handler mOnFrameAvailableHandler;
/**
* These fields are used by native code, do not access or modify.
@@ -83,7 +82,8 @@ public class SurfaceTexture {
/**
* Exception thrown when a SurfaceTexture couldn't be created or resized.
*
* @deprecated No longer thrown. {@link Surface.OutOfResourcesException} is used instead.
* @deprecated No longer thrown. {@link android.view.Surface.OutOfResourcesException}
* is used instead.
*/
@SuppressWarnings("serial")
@Deprecated
@@ -100,10 +100,10 @@ public class SurfaceTexture {
*
* @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
*
* @throws OutOfResourcesException If the SurfaceTexture cannot be created.
* @throws Surface.OutOfResourcesException If the SurfaceTexture cannot be created.
*/
public SurfaceTexture(int texName) {
init(texName, false);
this(texName, false);
}
/**
@@ -121,20 +121,58 @@ public class SurfaceTexture {
* @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
* @param singleBufferMode whether the SurfaceTexture will be in single buffered mode.
*
* @throws throws OutOfResourcesException If the SurfaceTexture cannot be created.
* @throws Surface.OutOfResourcesException If the SurfaceTexture cannot be created.
*/
public SurfaceTexture(int texName, boolean singleBufferMode) {
init(texName, singleBufferMode);
mCreatorLooper = Looper.myLooper();
nativeInit(texName, singleBufferMode, new WeakReference<SurfaceTexture>(this));
}
/**
* Register a callback to be invoked when a new image frame becomes available to the
* SurfaceTexture. Note that this callback may be called on an arbitrary thread, so it is not
* SurfaceTexture.
* <p>
* This callback may be called on an arbitrary thread, so it is not
* safe to call {@link #updateTexImage} without first binding the OpenGL ES context to the
* thread invoking the callback.
* </p>
*
* @param listener The listener to set.
*/
public void setOnFrameAvailableListener(OnFrameAvailableListener l) {
mOnFrameAvailableListener = l;
public void setOnFrameAvailableListener(OnFrameAvailableListener listener) {
setOnFrameAvailableListener(listener, null);
}
/**
* Register a callback to be invoked when a new image frame becomes available to the
* SurfaceTexture.
* <p>
* If no handler is specified, then this callback may be called on an arbitrary thread,
* so it is not safe to call {@link #updateTexImage} without first binding the OpenGL ES
* context to the thread invoking the callback.
* </p>
*
* @param listener The listener to set.
* @param handler The handler on which the listener should be invoked, or null
* to use an arbitrary thread.
*/
public void setOnFrameAvailableListener(final OnFrameAvailableListener listener,
Handler handler) {
if (listener != null) {
// Although we claim the thread is arbitrary, earlier implementation would
// prefer to send the callback on the creating looper or the main looper
// so we preserve this behavior here.
Looper looper = handler != null ? handler.getLooper() :
mCreatorLooper != null ? mCreatorLooper : Looper.getMainLooper();
mOnFrameAvailableHandler = new Handler(looper, null, true /*async*/) {
@Override
public void handleMessage(Message msg) {
listener.onFrameAvailable(SurfaceTexture.this);
}
};
} else {
mOnFrameAvailableHandler = null;
}
}
/**
@@ -285,49 +323,22 @@ public class SurfaceTexture {
}
}
private class EventHandler extends Handler {
public EventHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
if (mOnFrameAvailableListener != null) {
mOnFrameAvailableListener.onFrameAvailable(SurfaceTexture.this);
}
}
}
/**
* This method is invoked from native code only.
*/
@SuppressWarnings({"UnusedDeclaration"})
private static void postEventFromNative(Object selfRef) {
WeakReference weakSelf = (WeakReference)selfRef;
SurfaceTexture st = (SurfaceTexture)weakSelf.get();
if (st == null) {
return;
}
if (st.mEventHandler != null) {
Message m = st.mEventHandler.obtainMessage();
st.mEventHandler.sendMessage(m);
private static void postEventFromNative(WeakReference<SurfaceTexture> weakSelf) {
SurfaceTexture st = weakSelf.get();
if (st != null) {
Handler handler = st.mOnFrameAvailableHandler;
if (handler != null) {
handler.sendEmptyMessage(0);
}
}
}
private void init(int texName, boolean singleBufferMode) throws Surface.OutOfResourcesException {
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(looper);
} else {
mEventHandler = null;
}
nativeInit(texName, singleBufferMode, new WeakReference<SurfaceTexture>(this));
}
private native void nativeInit(int texName, boolean singleBufferMode, Object weakSelf)
private native void nativeInit(int texName, boolean singleBufferMode,
WeakReference<SurfaceTexture> weakSelf)
throws Surface.OutOfResourcesException;
private native void nativeFinalize();
private native void nativeGetTransformMatrix(float[] mtx);