Android how to properly finish an activity - android

I am making an android game using the SurfaceView/Thread model seen in many of the examples. I am trying to split the components into multiple activities. Right now I have a menu activity and a game activity. For some reason when I attempt to finish the game activity to return to the menu activity my app will become unresponsive. It seems to work fine on the emulator but on my device (Samsung Mesmerize) it will become unresponsive. I have a feeling this is because a thread is still busy in the game activity but I have no idea how to kill the thread. I tried breaking the game loop as well and that did not seem to help.
My SurfaceView has an inner thread class, here is the implementation of run()
public void run()
{
_isRunning = true;
Canvas c = null;
while(_isRunning)
{
try
{
c = _surfaceHolder.lockCanvas();
synchronized(_surfaceHolder)
{
if(_engine != null)
{
_engine.gameLogic();
if(c != null)
_engine.draw(c);
}
}
}
finally
{
if(c != null)
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}

I found the solution to my problem after an hour or two of debugging. I had to keep track of whether the surface was alive before calling lockCanvas(). I used this same loop in both activities and calling lockCanvas() after the surface from the game activity was destroyed was causing problems.

Related

Android UI Thread blocked in new Activity

I'm working on a small Android game that handles canvas drawing on a working thread. After the game is over this thread should start the next activity, but somehow the UI thread seems to get blocked by starting a new function.
This is my games Main-Loop:
while(true)
// Perform Ticks until the player makes a mistake
c = _surfaceHolder.lockCanvas();
if ((toDraw = rh.performTick()) == null) {
lose();
break;
}
_surfaceHolder.unlockCanvasAndPost(draw(toDraw,c));
}
The function performTick() processes the game logic and returns null when the game ends. draw()does the rendering. Both Functions work perfect - the problem is lose(), which does nothing than starting a new Activity:
protected void lose() {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
Intent intent = new Intent(_context, MainMenu.class);
_context.startActivity(intent);
}
});
}
When the Game ends, the new Activiy (MainMenu) starts normally but does not react on any touch events. There is no LogCat output and no Exception occuring. Even worse, lose()works normal when its called BEFORE the main loop starts.
I've been working on this for days now. I tried to start the Activity directly from the working Thread (without using a Handler in lose()) and searched the Internet for hours - nothing helps. It seems like the UI Thread is somehow blocked or caught in an infinite loop, and i have no idea how to fix this.
Everything is debugged on a Nexus 4 device.
Btw: MainMenu.onCreate() works normal when started another way.
Please help,
MA3o
You don't unlock the canvas when the if statement is true. Break will get you out of the while, leaving the canvas locked.
while(true)
// Perform Ticks until the player makes a mistake
c = _surfaceHolder.lockCanvas();
if ((toDraw = rh.performTick()) == null) {
lose();
break;
}
// this line won't be executed whether the if statement above is true
_surfaceHolder.unlockCanvasAndPost(draw(toDraw,c));
}
You should unlock inside the if statement also (before or after lose()).
Instead if While(true) you could have While(myVar) where myVar is a boolean that is true while game should be running and set to false when you call lose() In your case you continued to draw although you did not need to.

Memory leak at SpenInView Native Stack

I have a screen that overlays the SpenSurfaceView with Android layout components. When the user switches the screen to load different components, the old components leak into this class:
com.samsung.android.sdk.pen.engine.SpenInView
from the native stack according to MAT (Memory Analyzer T)
The overlayed components are custom controllers to take in user input in the form of strokes. They have a reference to the SurfaceView, but it is nulled before the components are deleted.
The problem remains even if I completely exit the application.
I am using example PenSample5_6_TextRecognition as reference.
This is using Samsung Mobile SDK (http://developer.samsung.com/samsung-mobile-sdk)
What strategy should I employ to continue to chase this memory leak ?
The NDK side of the SDK is likely closed source.
Does the SpenObjectBase retains references to the SpenSurfaceView ?
Can somebody with more reputation than I have create the "spen sdk" tag.
Please check onDestroy() from sample application. Do you close all resources?
#Override
protected void onDestroy() {
super.onDestroy();
if (mTextRecognition != null) {
mSpenTextRecognitionManager.destroyRecognition(mTextRecognition);
mSpenTextRecognitionManager.close();
}
if (mSpenSurfaceView != null) {
mSpenSurfaceView.closeControl();
mSpenSurfaceView.close();
mSpenSurfaceView = null;
}
if(mSpenNoteDoc != null) {
try {
mSpenNoteDoc.close();
} catch (Exception e) {
e.printStackTrace();
}
mSpenNoteDoc = null;
}
}
Samples are closing resources in onDestroy. That may happen well after user leaves activity (even never if device has enough memory). Consider releasing resources in onPause and re-creating them in onResume instead.
Finally, in my code I am removing registred callbacks
private void removeListeners() {
spenPageDocContainingNoteDoc.setObjectListener(null);
try {
spensBasicShapeConverter.setResultListener(null);
} catch (Exception e) {
..
}
When investingating heap dumps, I saw my Activity (callback handler) held inside some data structure in Spen API. Removing listeners removed that memory leak.

Android SurfaceView/Thread for update games states

I'm developing a small game on Android and I have a question. I want my main game loop to be the thread that is called from the SurfaceView but every time I want to call a method of another class from this thread I get the "stopped unexpectedly" message.
To be more clear:
#Override
public void run() { //Game Loop
Canvas canvas = null;
while (mRun) {
canvas = mHolder.lockCanvas();
if (canvas != null) {
grub.updateGrubber(); //here I want to update state.
mPanel.doDraw(canvas);
mHolder.unlockCanvasAndPost(canvas);
}
}
}
the line grub.updateGrubber(); show the error that I mention before. I tried different methods from that class and all of them gives me the same error.
Any help will be appreciated.
If my guess is correct, you are trying to update the UI thread directly. You can't call UI methods from threads other than the main thread. Try:
Runnable action = new Runnable() {
#Override
public void run() {
grub.updateGrubber();
} };
this.post(action);
instead of your `grub.updateGrubber();` line.
I'm actually tested what you suggested and is not a bad idea, but the problem in my code was that I never really created a Grubber object (I miss the = new Grubber(); part).
Probably a mistake related with the enormous amount of code writing and testing that I've done in the past few weeks, sorry about that. Anyway I'm going to use as main game loop the thread that is created to update the canvas in the surfaceview according to what i've read here.

Surface view drawing thread - busy loop?

All examples of the use of a SurfaceView seems to use a run method that performs a busy loop. Is that a valid way to do this? All the code I can see follows this paradigm from the lunar lander sample. However, creating a busy while loop seems to be a strange way to code multi threaded apps. Shouldnt the drawing code wait on a queue of drawing commands, or something similar. I would have implemented it that way, but the amount of code that I see that does is like below makes me ask the question... What is the best semantics for a thread drawing on a SurfaceView.
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
// DO DRAWING HERE
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
I don't know what is best practice in this case, but I have successfully used a slightly modified version of that example in my apps. Since I respond to touch input (rather than continuously updating the canvas) I added a flag to test if drawing even needs to be done. I also added a sleep after each refresh to limit system load. This is my code inside of the try block:
if(mPanel.needsRefresh()) {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
mPanel.onDraw(c);
}
} else {
SystemClock.sleep(10);
}

Unexpected NullPointerException in SurfaceView-Thread on API-level 15

I have programmed an App which I wanted to test on higher API-Levels for checking the compatibility. For API 10 (2.3.3) there were no problems, but as soon as I ran my app on API 15 (4.0.3) I got an NullPointerException in one of my SurfaceViews when I was quitting the Activity.
I have to say that i solved the problem, but i can't figure out why the Exception occured actually. So maybe you could tell me.
Here is the code that worked for me on API 10:
It's the common structure of the run()-method.
public void run() {
while (mThreadActive) {
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if(mState == 1) {
updateValues();
updateAnimation();
}
doDraw(c);
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
On API 15 when quitting the activity:
The Exception accoured when the doDraw()-method tried to write on "c". I checked c and found out it was null, so no surprise I got an Exception. I also checked mThreadActive and found out that although i set it to false, the while-loop still triggers.
Here is the Code sample:
public void run() {
while (mThreadActive) {
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if(mState == 1) {
updateValues();
updateAnimation();
}
if(!mThreadActive) // so it really is!
Log.d("Thread", "mThreadActive is false!");
if(c == null) // so it is too!
Log.d("Thread", "c is null!");
doDraw(c); // error
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
I can imagine why mThreadActive becomes false AFTER being checked by the while-statement, but I can't figure out why "c" is null after mSurfaceHolder.lockCanvas(null). It seems that the code is not running sequential.
Well, the solution would be checking c != null before drawing on it:
public void run() {
while (mThreadActive) {
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if(mState == 1) {
updateValues();
updateAnimation();
}
if(c != null) // prevent drawing on c if c doesnt exist.
doDraw(c);
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
So why do i get an Exception on API 15 whereas it works fine on API 10?
Another funny thing is, that i have other SurfaceViews with the same structure, but in comparison to this one, they work all fine!
Why is the code not running sequential? Is it because I am testing on an emulator (which is pretty laggy)?
Thank You.
You mention that your while() loop seems to proceed despite mThreadActive being false. Is mThreadActive marked volatile? It may need to be.
Also, lockCanvas(null) smells. Since we can't see the rest of your code, it's not exactly clear what you're trying to do. What does the API say about passing a null in to lockCanvas? (And are we talking about a raw instance of SurfaceHolder or a subclass?)
Note that the API spec for SurfaceHolder.lockCanvas() indicates it can return null:
A null is returned if the surface has not been created or otherwise cannot be edited. You will usually need to implement Callback.surfaceCreated to find out when the Surface is available for use.
From reading the API, it looks like you should be implementing the SurfaceHolder.Callback interface and responding to the surfaceCreated() event, which is really the event that tells you you're ready to go in writing to the canvas.
I also get a very similar issue when calling:
public void surfaceDestroyed(SurfaceHolder holder) {
isAttached = false;
this.drawthread = null;
}
When exiting my application to stop the drawing thread - the while boolean I use isAttached is false, but the drawing code within that while loop still executes - giving a nullexception on exit. Not a show stopper but something I really want to fix. Was considering a if (canvas != null) sort of solution too but I thought there must be a better way.
Now here's the weird thing with this error - it only happens some of the time - and happens less on faster devices. The problem the way I see it is that holder.lockCanvas() is returning null - meaning the holder is being destroyed before the boolean is set to false and the while has a chance to stop executing the Thread. A thread race problem?
Found a possible solution that used Thread.join() before making the while boolean null, but with holder.lockCanvas() returning null, the holder is being destroyed before the drawing is finished, so it's kind of pointless and still caused a nullexception.
Only other solution I can think of is to override the back button method and force the while bool to null before destroying the surfaceview(if that's poss), but I think if (canvas != null) is probably cleaner. Any other ideas out there keen to hear!
edit: haven't tested elsewhere, but I get the error using API 17

Categories

Resources