How to change content view from a GLSurfaceView? - android

I'm working on a small game and I'm having problems changing my Activity's content view when a GLSurfaceView is set, let me describe how the app works:
The app has only one Activity, it starts with a static view: setContentView(R.layout.main);, when the user sends a certain input using menus a GLSurfaceView is instantiated and set via setContentView(gameSession); (where gameSession is the class extending GLSurfaceView). The GLSurfaceView class then sets the Renderer on which the real application logic runs.
What happens now is that the game logic (ran inside the Renderer) is the one responsible for knowing when the game is over and the view should change back to R.layout.main, the Renderer then calls a synchronized method on the GLSurfaceView, which notifies the Activity to be changed (again with setContentView(R.layout.main);).
And here comes the problem, as soon as setContentView(R.layout.main); is called everything hangs, the GLSurfaceView is still there (just not getting updated anymore). I fear that I'm experiencing a deadlock, with the Activity waiting for the Renderer to be done before removing it.
I've been thinking on a few solutions but all of them bring other issues:
Having the GLSurfaceView or the Activity see if the game is over by polling a flag on the Renderer, but there is no loop on those classes, (and the onDraw on the GLSurfaceView is never called)
Using the queueEvent() method, but the Renderer class doesn't have it (and using GLSurfaceView.queueEvent() doesn't work because the Runnable remains of the Renderer thread)
Implementing some kind of message thread only to check for game end, but that sounds like a waste of resources.
Has any one of you experienced a similar issue? What's the best way to change your Activity's content view from a GLSurfaceView to something else if the logic for when to swap is on the Renderer?

You shouldn't be calling setContentView() multiple times.
You could instead use multiple activities, calling finish() on your game activity when you're done with it to return to the static view Activity.

Related

Multiple SurfaceView Threads, Crashing in Android Fragments

I wrote this dynamic keyboard app to track frequencies in an online PowerBall-type game. I used a SurfaceView because I can do stuff in real-time that I can't do with Buttons. The app works great with one thread: onPause destroys the surface/thread and onResume creates the surface/thread again. I thought that it would be cool to add another keyboard in a Tabbed View fragment so the user can track the keno numbers as well. For example
Fragment1:
return new PowerBallPanel(getActivity(),15,3);
Fragment2:
return new PowerBallPanel(getActivity(),75,5);
I thought Fragments were very similar to individual apps but every time I add Fragment2 I get a Surface Locked Exception, even though it's not running yet.
I am confused.
Thanks.
Chris
The answer is to include the Thread class as a sub-class of the surfaceview class. Hope this helps someone else.

Invalidate is failing to call onDraw when returning to an activity that has a custom view

I have inherited some code hence I don't have true freedom to change it. :(
I have a main activity, from which other activities (I will refer to these as sub activities from now on) are called. Whenever one of these completes, it calls finish and returns data to the main activity.
Each activity (including the main one) has a bar on the top that displays a custom view. The custom view contains a canvas which has a drawing that is dependant upon the state of the network.. i.e. wifi/mobile etc...
Since that 'state' data never changes, it's held within a singleton and the view gets data from the singleton to define what it draws. That is working with no issues, i.e. the data is always as I expect it.
When I first launch the MainActivity, as the network changes, the data changes and each call to 'invalidate' the view receives a system call to 'onDraw' as I would expect.
In each of the sub activities the same is again true.
Upon finishing a sub activity and returning to the mainActivity, calls to invalidate no longer cause a call to onDraw to occur.
I have looked at this for quite a while now and just cannot figure out what is going wrong.
In my constructor I have:
setWillNotDraw(false);
Whenever the data changes the following methods are called:
invalidate();
requestLayout();
Now, there's one more thing... upon returning to the activity at that immediate point, I refresh and this DOES draw correctly, i.e. invalidate does trigger an onDraw call... any subsequent network changes (which are propogated) fail to result in the onDraw call.
I'm wondering if this is to do with the view somehow being detached. I can see that 'onDetachedFromWindow' is called, however the trigger for this is the destruction of the subactivity, hence I don't see why that should affect the MainActivity but it's the only thing I can think of.
I'm hoping I've provided enough information for someone to help me...
Well, in the end my answer has very little to do with the question and I guess this is an example of how an issue can be solved by going back to absolute basics and checking for the obvious.
My activities all inherit from an abstract activity. Within that activity there is an instance of the view. The views in which I was having trouble were using that declaration as opposed to having their own instance, hence behaviour from one activity was then affecting another inadvertently.
So, if I'd been able to post up all the code, I'm sure someone else would have spotted this but, unfortunately I couldn't in this instance.
Still, whilst this posting doesn't provide a resolution that will help others, maybe it does say... step back and check the obvious first!

Android Game Leaking memory because of GLThread reference

Right now I'm developing a game in Android (OpenGL ES 1.1) and I'm seeing that whenever I create a new SurfaceView (GLView) its thread is created. That's ok. The problem comes when I want to finish the Activity that holds the SurfaceView (and go back to the menu). It seems that the activities are not released because each GLThread is referencing it. This may finish with an OOM error.
Some MAT pictures:
The first picture: the MarkitActivity represents each single instance of the Activity that deals with SurfaceView.
The second picture: The list of all the activities in memory.
The Third Picture: What is holding the Activities from GC.
If any code is needed I will post it. Nevertheless I have already tried the following things:
->Weak reference of the Activity Context to the renderer and to the surfaceview.
->Application Context instead of Activity Context (in normal and weak mode).
->Trying to stop (in a hard way) the thread (interrupt) and waiting for join (Which the program does it, but the thread does not care, it is still there...)
->Trying without debugging, just in case in debugger mode the values changes (the MAT pictures are without debugger).
->Trying the Activity as singleInstance mode. Weird results and errors everywhere.
->onPause and onResume are correctly controlled for the view.
Any hint, idea, question or help will be really appreciated. Thanks in advance!
Carlos.
I had a similar problem with threads (but not using OpenGL), and end up solving it using a simple trick.
Before exting activity (in onPause() or onStop(), try nulling the thread like this:
myThread = null;
It seems that it makes the Thread GC collectable, and therefore you activity becomes also collectable.
It worked for me, as well as for some people with similar problems to whom I gave the same suggestion.
Regards.

System.gc() causing slowdown from the second start of Activity

I am experiencing a very strange phenomenon (test device: HTC Desire HD, Android 2.3.5). I know that System.gc() is needless and discouraged, and I don't try to suggest otherwise, but the point is that it shouldn't cause issues either (i.e. it should be useless at most).
I have an application which contains a GLSurfaceView in its view hierarchy. The GLSurfaceView is instantiated and added in the Activity.onCreate(). Normally, the application works like this:
User starts the app and goes to mainmenu
User chooses a mainmenu item which sets the GLSurfaceView to View.VISIBLE
User plays with the in-built game on GLSurfaceView
User goes to mainmenu and exits the activity (=> Activity.finish() is called)
My Activity.onPause() looks like this:
mGameThread.pause(); // gameThread is my custom thread class for the in-built game
mGLView.onPause(); // pause the renderer thread
So far so good, everything works fine. However, issues appear after I add the following code to onPause() (for the case when the user exits the game from the mainmenu):
mGameThread.pause(); // gameThread is my custom thread class for the in-built game
mGLView.onPause(); // pause the renderer thread
if (isFinishing()) {
System.gc();
}
In details: if the Activity is started for the first time (= i.e. the app process didn't exist before), everything works fine. However, starting from the 2nd start of the activity (= after the first exit from the mainmenu, i.e. after the first Activity.finish()), the framerate of GLSurfaceView is reduced by 40-50%, the in-built game becomes slow.
If I remove the System.gc() call, the problem disappears. Moreover, if I do the following, it also gets rid of the problem:
mGameThread.pause(); // gameThread is my custom thread class for the in-built game
mGLView.onPause(); // pause the renderer thread
if (isFinishing()) {
// 1. get layout root of View hierarchy
// 2. recursively remove (detach) all Views
// 3. call GC
System.gc();
}
I didn't add concrete code because it's complex, so I used comments. If I just detach the GLSurfaceView via removeView(), it is not enough. The entire view hierarchy needs to be cleared.
Note that I couldn't find any memory leaks (no Activity leak via drawables/statics etc.). Moreover, of course, the gameThread properly exits when the app is closed (I just didn't include its source code).
Any ideas, guesses? Apparently, System.gc() seems to cause some issues for the Activity/layout destroying mechanism of Android. Again, as I said, if I remove System.gc(), the problem disappears.
I have experience of Android Game Programming. I used to clear all the view in hierarchy because when running threads if you call System.gc() sometimes it happens that your thread has a reference to some of your view, even if you call system.gc() this view won't get removed and if you keep playing again and again this game you will notice that your heap memory is started growing.
It depends upon the memory leak, if you are leaking some KB memory it will take more time to crash your game. The best way it to use Eclipse Memory Anlyser (Eclipse MAT) and compare your stacks.
Step1:
take memory snap shot when you start your game for first time
Step2:
take memory snap shot when you start your game second time
Step3:
Now compare your both stacks of snapshots it will tell you the difference.
It is a very useful tool. I was having huge memory issues in my game Apache Attack. I fixed them using this awesome tool.
Follow this ECLIPSE MAT TUTORIAL

How to extend an existing background thread solution?

I am using Eclipse to develop an Android application that plots Bluetooth data.
I am using open source code, which has an existing solution that I want to extend and not replace to solve my development problem as stated above.
The open source code has a very nice and solid background thread that among other things continually logs BluetoothData to logcat even when I switch to a new activity.
Currently I have a solution which I am concerned about: I simply leverage a background thread method that writes to logcat to call a static plotData() method in my Plotting Activity. The result seems good. I get a nice plot. It clips along in real-time. Looks like an oscilloscope.
But I have received negative feedback about using the existing background thread coupled with a static method to plot the BluetoothDate. It has been suggested that I use a new thread, or add a handler, or use Async Task, or AIDL to solve my problem.
I have looked at all these solutions without success. Nothing seems to work like my static plotData() method. That is to say the existing background thread calls my static plotData() method which results in a real-time plot that looks great.
But I am still concerned about the negative feedback. I simply want to extend my existing background thread solution which I have done by having it call a static method to plot the data.
What are the problems I might face with this approach? Thread safety? Deadlock? I don't know.
Why do people keep suggesting that I create a new thread, handler, Async Task, or Service to solve my problem when extending my existing thread to call a static method seems to work just fine?
Any suggestions? What are the problems with extending the existing thread to use a static method to plot the data in real-time?
Anyone who says that you should use AIDL for this is a loon who should not be listened to. :) Also someone saying you need a Service if you don't want to have your background thread running when the user is not viewing your activity.
I'm not sure what you mean by "writes to logcat to call a static plotData()." You should write to logcat only for testing. Writing to logcat doesn't cause a call to any Java method.
If you are calling a static plotData() method on your Activity, you need to be extremely careful with this: first because it is difficult to figure out what activity instance should be called from there (it may go away at any time from the user finishing it, or be recreated as a new instance when the configuration changes, etc); and second because you can't touch your app's UI/view hierarchy from a background thread without risking that you corrupt its state (since the view hierarchy is single threaded).
The general model one does for this kind of thing is to have a background thread doing some work, generating the next data to display. Once it is done with the work you send a message to the main thread to have it display the new data. AsyncTask can be a simple way to do this, it takes care of the underlying message sending and threading. You can also implement this yourself, at some point having a Handler that you post a Runnable on or send a Message to that once executed on the UI thread will update your view state.
(Of course if you are using a SurfaceView, the whole point of that is to allow drawing to it outside of the main UI loop, so your background thread could just draw directly on to it as needed. Basically that is like writing a game.)

Categories

Resources