Refactor app startup

- Make sure crash checking is performed as early as possible
    - Minimize calling complex native functions until after splash screen is dimissed
    - Do not attempt to setup/resume OpenGL until after splash screen is dimissed
This commit is contained in:
Aaron Culliney 2015-11-01 10:14:40 -08:00
parent cbe8a734d0
commit 521d1daccf
3 changed files with 130 additions and 113 deletions

View File

@ -15,7 +15,6 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Rect;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
@ -25,7 +24,6 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.Toast;
@ -51,9 +49,6 @@ public class Apple2Activity extends Activity {
private AtomicBoolean mPausing = new AtomicBoolean(false);
private int mWidth = 0;
private int mHeight = 0;
private float[] mXCoords = new float[MAX_FINGERS];
private float[] mYCoords = new float[MAX_FINGERS];
@ -88,17 +83,11 @@ public class Apple2Activity extends Activity {
public final static long NATIVE_TOUCH_ASCII_MASK = 0xFF00L;
public final static long NATIVE_TOUCH_SCANCODE_MASK = 0x00FFL;
private native void nativeOnCreate(String dataDir, int sampleRate, int monoBufferSize, int stereoBufferSize);
private native void nativeGraphicsInitialized(int width, int height);
private native void nativeGraphicsChanged(int width, int height);
private native void nativeOnKeyDown(int keyCode, int metaState);
private native void nativeOnKeyUp(int keyCode, int metaState);
private native void nativeEmulationResume();
public native void nativeEmulationResume();
public native void nativeEmulationPause();
@ -108,8 +97,6 @@ public class Apple2Activity extends Activity {
public native void nativeReboot();
public native void nativeRender();
public native void nativeChooseDisk(String path, boolean driveA, boolean readOnly);
public native void nativeEjectDisk(boolean driveA);
@ -135,55 +122,36 @@ public class Apple2Activity extends Activity {
Log.e(TAG, "onCreate()");
// placeholder view on initial launch
if (mView == null) {
setContentView(new View(this));
}
Apple2CrashHandler.getInstance().initializeAndSetCustomExceptionHandler(this);
if (sNativeBarfed) {
Log.e(TAG, "NATIVE BARFED...", sNativeBarfedThrowable);
View view = new View(this);
setContentView(view);
return;
}
// run first-time initializations
showSplashScreen();
Apple2CrashHandler.getInstance().checkForCrashes(Apple2Activity.this);
// first-time initializations #1
if (!Apple2Preferences.FIRST_TIME_CONFIGURED.booleanValue(this)) {
Apple2DisksMenu.firstTime(this);
Apple2Preferences.KeypadPreset.IJKM_SPACE.apply(this);
}
Apple2Preferences.FIRST_TIME_CONFIGURED.saveBoolean(this, true);
// get device audio parameters for native OpenSLES
int sampleRate = DevicePropertyCalculator.getRecommendedSampleRate(this);
int monoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/false);
int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/true);
Log.d(TAG, "Device sampleRate:" + sampleRate + " mono bufferSize:" + monoBufferSize + " stereo bufferSize:" + stereoBufferSize);
String dataDir = Apple2DisksMenu.getDataDir(this);
nativeOnCreate(dataDir, sampleRate, monoBufferSize, stereoBufferSize);
// NOTE: load preferences after nativeOnCreate ... native CPU thread should still be paused
Apple2Preferences.loadPreferences(this);
mView = new Apple2View(this);
setContentView(mView);
// Another Android Annoyance ...
// Even though we no longer use the system soft keyboard (which would definitely trigger width/height changes to our OpenGL canvas),
// we still need to listen to dimension changes, because it seems on some janky devices you have an incorrect width/height set when
// the initial OpenGL onSurfaceChanged() callback occurs. For now, include this defensive coding...
mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
Rect rect = new Rect();
mView.getWindowVisibleDisplayFrame(rect);
int h = rect.height();
int w = rect.width();
if (w < h) {
// assure landscape dimensions
final int w_ = w;
w = h;
h = w_;
}
nativeGraphicsChanged(w, h);
Intent intent = getIntent();
String path = null;
if (intent != null) {
Uri data = intent.getData();
if (data != null) {
path = data.getPath();
}
});
}
if (path != null && Apple2DisksMenu.hasDiskExtension(path)) {
handleInsertDiskIntent(path);
}
}
@Override
@ -195,7 +163,7 @@ public class Apple2Activity extends Activity {
}
Log.d(TAG, "onResume()");
mView.onResume();
showSplashScreen();
}
@Override
@ -211,7 +179,9 @@ public class Apple2Activity extends Activity {
}
Log.d(TAG, "onPause()");
mView.onPause();
if (mView != null) {
mView.onPause();
}
// Apparently not good to leave popup/dialog windows showing when backgrounding.
// Dismiss these popups to avoid android.view.WindowLeaked issues
@ -354,40 +324,6 @@ public class Apple2Activity extends Activity {
return super.onTouchEvent(event);
}
void graphicsInitialized(int w, int h) {
Log.v(TAG, "graphicsInitialized(" + w + ", " + h + ")");
if (mMainMenu == null) {
mMainMenu = new Apple2MainMenu(this, mView);
}
if (w < h) {
// assure landscape dimensions
final int w_ = w;
w = h;
h = w_;
}
mWidth = w;
mHeight = h;
nativeGraphicsInitialized(w, h);
showSplashScreen();
Intent intent = getIntent();
String path = null;
if (intent != null) {
Uri data = intent.getData();
if (data != null) {
path = data.getPath();
}
}
if (path != null && Apple2DisksMenu.hasDiskExtension(path)) {
handleInsertDiskIntent(path);
}
}
public void showMainMenu() {
if (mMainMenu != null) {
Apple2SettingsMenu settingsMenu = mMainMenu.getSettingsMenu();
@ -456,18 +392,28 @@ public class Apple2Activity extends Activity {
});
}
private synchronized void showSplashScreen() {
private void showSplashScreen() {
if (mSplashScreen != null) {
return;
}
mSplashScreen = new Apple2SplashScreen(Apple2Activity.this);
runOnUiThread(new Runnable() {
@Override
public void run() {
mSplashScreen.show();
Apple2CrashHandler.getInstance().checkForCrashes(Apple2Activity.this);
}
});
mSplashScreen.show();
}
private void setupGLView() {
boolean glViewFirstTime = false;
if (mView == null) {
glViewFirstTime = true;
mView = new Apple2View(this);
mMainMenu = new Apple2MainMenu(this, mView);
}
setContentView(mView);
if (!glViewFirstTime) {
mView.onResume();
}
}
public void registerAndShowDialog(AlertDialog dialog) {
@ -479,7 +425,7 @@ public class Apple2Activity extends Activity {
mMenuStack.add(apple2MenuView);
View menuView = apple2MenuView.getView();
nativeEmulationPause();
addContentView(menuView, new FrameLayout.LayoutParams(mWidth, mHeight));
addContentView(menuView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
}
public synchronized Apple2MenuView popApple2View() {
@ -543,6 +489,8 @@ public class Apple2Activity extends Activity {
private void _disposeApple2View(Apple2MenuView apple2MenuView) {
boolean dismissedSplashScreen = false;
// Actually remove View from view hierarchy
{
View menuView = apple2MenuView.getView();
@ -552,15 +500,19 @@ public class Apple2Activity extends Activity {
}
if (apple2MenuView == mSplashScreen) {
mSplashScreen = null;
dismissedSplashScreen = true;
}
}
// if no more views on menu stack, resume emulation
if (mMenuStack.size() == 0) {
dismissAllMenus();
if (!mPausing.get()) {
nativeEmulationResume();
}
}
if (mMenuStack.size() == 0 && !mPausing.get()) {
nativeEmulationResume();
if (!mPausing.get() && dismissedSplashScreen) {
setupGLView();
}
}
@ -570,8 +522,12 @@ public class Apple2Activity extends Activity {
}
}
public Apple2View getView() {
return mView;
public boolean isPausing() {
return mPausing.get();
}
public int menuStackSize() {
return mMenuStack.size();
}
public void maybeQuitApp() {

View File

@ -16,9 +16,11 @@
package org.deadc0de.apple2ix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.util.Log;
import android.view.ViewTreeObserver;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
@ -50,13 +52,17 @@ class Apple2View extends GLSurfaceView {
private Apple2Activity mActivity = null;
private static native void nativeOnCreate(String dataDir, int sampleRate, int monoBufferSize, int stereoBufferSize);
private static native void nativeGraphicsInitialized(int width, int height);
private static native void nativeGraphicsChanged(int width, int height);
private static native void nativeRender();
public Apple2View(Apple2Activity activity) {
super(activity.getApplication());
mActivity = activity;
setup(0, 0);
}
private void setup(int depth, int stencil) {
/* By default, GLSurfaceView() creates a RGB_565 opaque surface.
* If we want a translucent one, we should change the surface's
@ -75,14 +81,36 @@ class Apple2View extends GLSurfaceView {
* custom config chooser. See ConfigChooser class definition
* below.
*/
setEGLConfigChooser(new ConfigChooser(8, 8, 8, 8, depth, stencil));
setEGLConfigChooser(new ConfigChooser(8, 8, 8, 8, /*depth:*/0, /*stencil:*/0));
/* Set the renderer responsible for frame rendering */
setRenderer(new Renderer());
// Another Android Annoyance ...
// Even though we no longer use the system soft keyboard (which would definitely trigger width/height changes to our OpenGL canvas),
// we still need to listen to dimension changes, because it seems on some janky devices you have an incorrect width/height set when
// the initial OpenGL onSurfaceChanged() callback occurs. For now, include this defensive coding...
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
Rect rect = new Rect();
Apple2View.this.getWindowVisibleDisplayFrame(rect);
int h = rect.height();
int w = rect.width();
if (w < h) {
// assure landscape dimensions
final int w_ = w;
w = h;
h = w_;
}
nativeGraphicsChanged(w, h);
}
});
}
private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
Log.w(TAG, "creating OpenGL ES 2.0 context");
checkEglError("Before eglCreateContext", egl);
@ -295,17 +323,49 @@ class Apple2View extends GLSurfaceView {
}
private class Renderer implements GLSurfaceView.Renderer {
@Override
public void onDrawFrame(GL10 gl) {
Apple2View.this.mActivity.nativeRender();
nativeRender();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
Apple2Preferences.GL_VENDOR.saveString(mActivity, GLES20.glGetString(GLES20.GL_VENDOR));
Apple2Preferences.GL_RENDERER.saveString(mActivity, GLES20.glGetString(GLES20.GL_RENDERER));
Apple2Preferences.GL_VERSION.saveString(mActivity, GLES20.glGetString(GLES20.GL_VERSION));
Apple2View.this.mActivity.graphicsInitialized(width, height);
Log.v(TAG, "graphicsInitialized(" + width + ", " + height + ")");
if (width < height) {
// assure landscape dimensions
final int w_ = width;
width = height;
height = w_;
}
int sampleRate = DevicePropertyCalculator.getRecommendedSampleRate(Apple2View.this.mActivity);
int monoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(Apple2View.this.mActivity, /*isStereo:*/false);
int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(Apple2View.this.mActivity, /*isStereo:*/true);
Log.d(TAG, "Device sampleRate:" + sampleRate + " mono bufferSize:" + monoBufferSize + " stereo bufferSize:" + stereoBufferSize);
String dataDir = Apple2DisksMenu.getDataDir(Apple2View.this.mActivity);
nativeOnCreate(dataDir, sampleRate, monoBufferSize, stereoBufferSize);
nativeGraphicsInitialized(width, height);
// first-time initializations #2
if (!Apple2Preferences.FIRST_TIME_CONFIGURED.booleanValue(Apple2View.this.mActivity)) {
Apple2Preferences.KeypadPreset.IJKM_SPACE.apply(Apple2View.this.mActivity);
Apple2Preferences.FIRST_TIME_CONFIGURED.saveBoolean(Apple2View.this.mActivity, true);
}
Apple2Preferences.loadPreferences(Apple2View.this.mActivity);
if ((Apple2View.this.mActivity.menuStackSize() == 0) && !Apple2View.this.mActivity.isPausing()) {
Apple2View.this.mActivity.nativeEmulationResume();
}
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Do nothing.
}

View File

@ -99,7 +99,7 @@ static inline int _androidTouchEvent2InterfaceEvent(jint action) {
// ----------------------------------------------------------------------------
// JNI functions
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobject obj, jstring j_dataDir, jint sampleRate, jint monoBufferSize, jint stereoBufferSize) {
void Java_org_deadc0de_apple2ix_Apple2View_nativeOnCreate(JNIEnv *env, jclass cls, jstring j_dataDir, jint sampleRate, jint monoBufferSize, jint stereoBufferSize) {
const char *dataDir = (*env)->GetStringUTFChars(env, j_dataDir, 0);
// Android lifecycle can call onCreate() multiple times...
@ -135,14 +135,15 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobje
#endif
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeGraphicsChanged(JNIEnv *env, jobject obj, jint width, jint height) {
void Java_org_deadc0de_apple2ix_Apple2View_nativeGraphicsChanged(JNIEnv *env, jclass cls, jint width, jint height) {
// WARNING : this can happen on non-GL thread
LOG("...");
video_backend->reshape(width, height);
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeGraphicsInitialized(JNIEnv *env, jobject obj, jint width, jint height) {
void Java_org_deadc0de_apple2ix_Apple2View_nativeGraphicsInitialized(JNIEnv *env, jclass cls, jint width, jint height) {
// WANRING : this needs to happen on the GL thread only
LOG("width:%d height:%d", width, height);
video_shutdown();
video_backend->reshape(width, height);
video_backend->init((void *)0);
@ -176,7 +177,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeEmulationPause(JNIEnv *env,
#endif
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeRender(JNIEnv *env, jobject obj) {
void Java_org_deadc0de_apple2ix_Apple2View_nativeRender(JNIEnv *env, jclass cls) {
SCOPE_TRACE_VIDEO("nativeRender");
if (UNLIKELY(appState != APP_RUNNING)) {