In my main activity, I would like to have it set up, so that I first get met by a contentView just showing a background and some text. After X seconds, I want to change to my other view (GLSurfaceView).
This is obviously something I am doing completely wrong.
This is how I've imagined it could've been done (it's all in the onCreate method):
setContentView(R.layout.main);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
}
viewer = new Viewer(this);
setContentView(viewer);
Where layout Main is what I want to show at the beginning and Viewer is my GLSurfaceView class.
What happens is that it just goes black for 10 seconds and then it starts loading the objects I've got that is shown through OpenGLES.
There's nothing wrong with the layout Main, since it works if I just erase the lines under where the Thread.sleep takes action. Though, nothing happens before the Thread.sleep is over...
With that said, my questions are following:
Why is the contentView not changing until after Thread.sleep is done?
What would be an appropiate solution to what I want to achieve?
I'm assuming this in your onCreate() and thats why you are seeing nothing.
The way I would implement this is to start a thread using AsyncTask sleep in the doInBackground and in the onPostExecute set up the new view.
Don't make sleep the main thread(UI thread).Use a threads,AsynkTask or TimerTask for that type of works instead.
You're not sleeping the UI thread in the way you think you are.
The simplest thing for what you're looking to achieve is to separate the views into separate activities and let Android handle the transition between the views. It adds another file to your codebase, but it's fairly straightforward. Let's say your initial, plain view (R.layout.main) is for a SplashActivity activity, and your post-splash view goes into PostSplashActivity. Then you could do something like this:
public class SplashActivity extends Activity {
private static long DELAY = 10000; //milliseconds;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new Handler().postDelayed(
new Runnable() {
#Override
public void run() {
Intent postSplash = new Intent(SplashActivity.this, PostSplashActivity.class);
SplashActivity.this.startActivity(postSplash);
SplashActivity.this.finish();
}
}, DELAY);
}
}
This will draw your R.layout.main layout, and then puts a startActivity call for your PostSplashActivity on the message queue and tells the queue to wait DELAY milliseconds to execute it.
It seems like you are making the main thread sleep. This may be why the code is running tell after.
It sounds like you want something like a splash screen. I like to think of these as separate to the following screen, so always use a separate activity rather than calling setContentView twice. You'd still need to sleep in a thread.
Just personal preference though...
Related
From my main thread, I start an AsyncTask which will go through a list of images and for each image, it will do some processing on it. So basically, there's a for loop and inside it, another AsyncTask is called. I use an instance of a class which holds the boolean value for checking if each image is done with its processing, its called a dummyStructure.
Code of the main thread:
new BatchProcessor().execute()
the doInBackground of the BatchProcessor:
protected Void doInBackground(Void... params){
while(dummyStructure.isWorking())
{
//Try loop
thread.sleep(1000);
}
dummyStructure.setIsWorking(true); //basically sets the flag to true
for(String s: pictureList)
{
RunTheProcessingLoop().execute();
}
The Problem:
I tried debugging, and here's what the problem is imo, if I remove the line just outside the while loop dummyStrucutre.setIsWorking(true) then there are multiple asyncTasks called even before it finishes, and basically everything gets screwed up. However, if I don't remove that line, then the BatchProcessor AsyncTask gets caught in the while loop, while as the RunTheProcessingLoop AsyncTask never executes beyond its onPreExecute()(debugged to know that, I used Log.e() in every method of that asyncTask).
Definitely I'm missing something, any help? Thanks a lot! :)
What you are encountering is asynctasks getting piled up because you are starting one from another and not exiting the first. This is because the asynctasks are handled serially by a single thread by default. If you want to use multiple threads in parallel, you'd need to use your own thread executor. See the AsyncTask documentation for more details.
So after 2 days of posting this question and finding out more about what people posted, I figured this:
My main thread called for an AsyncTask and I wanted to wait for that AsyncTask to finish. So I used a boolean flag which the AsyncTask sets to false once it is done and I can queue another task. Here's the code:
class mExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}}
Now, all you need to do is, whatever task/method/etc you want to run Asynchronously, simply create a thread and push it in that class, example:
Thread t = new Thread(new Runnable() {
public void run() {
new someshit().execute();
}
});
new mExecutor().execute(t);
and Tada! Now they both won't be queued/synchronized but would run in parallel.
If I am wrong, please correct me! Thanks! :)
I'm developing my first app for Android and its supposed to be a game. Everything is fine but there's one thing I just can't wrap my head around.
In my game's "main" activity (not the first activity which starts when the app starts) I want to have a method which starts a thread which changes a buttons background color/image (go with color because I haven't made any images just yet) for one second then turns it back. I wan't the method to also have an integer parameter which makes it perform this n times. I want to be able to call like changeButtons(5); and it turns button x background blue for 1 second then waits 1 second five times.
So practically I'm trying to make a "main" thread which runs during the game and inside that thread I'm going to run this method whenever certain conditions are true (a thread which calls a thread).
So I have 2 questions. The first one is "Is this possible? " and If so can someone show me how to do it (not all of it of course but help me get started at least)? Especially I want to know if I can change a buttons background color in a thread and if so can someone show me how to write/get me started that thread?
The second question is a follow-up, if you can do this, can you have a like a boolean b which turns to true if someone presses a button and the thread can "notice" that change? For example, if the thread is running and Obama presses button x and b turns "true" in the method OnClick(View v) can I, inside my main thead have an if(b == true){Obama.moon();} and Obama will moon?
Sure you can.
In android you can use the Handler class (example available) to post actions to the event queue. You can do something like this:
final Handler handler = new Handler();
final Runnable updateRunner = new Runnable() {
public void run() {
// we are now in the event handling so to speak,
// so go ahead and update gui, set color of button for instance
}
};
new Thread(new Runnable() {
public void run() {
while (condition) {
SystemClock.sleep(1000);
handler.post(updateRunner);
}
}
}).start();
This will trigger the run in updateRunner each second.
Regarding your follow up, it can be done as well (of course :) ). You can for instance implement an observable pattern to the class that handles the button x. When pressed, notify the observers with something like observers.updateChange(b)
where you previously had a thatClassOverThere.registerObserver(this) in your main thread.
I have no idea why my app isnt liking the following and would be grateful for any help.
I have a main activity that sets the following onCreate
setContentView(new Splash(this));
Splash being a surfaceview with the following in its constructor
this.setBackgroundDrawable(getResources().getDrawable(R.drawable.splash));
Then I have a thread in Splash that waits 3seconds or 3000 miliseconds.
Then calls the second surfaceview in the main activity
setContentView(new GameCanvas(this));
Everything works fine until it calls the seoncds setContentView, the screen doesnt change, it sticks on the splash screen.
any idea why?
Here is my thread
new Thread(){
public void run(){
try{
Log.e("here", "sleeping");
sleep(3000);
//main being the main activity class
main.killSplash();
//Log.e("here", "KILL SPLASH");
}catch(Exception e){
//
}
}
}.start();
p.s i have a feeling it is because I'm calling the switch canvas method from inside a thread
Activities are designed to be different "screens" in your application, and thus you should separate your splash screen's activity from your main game activity. Once an activity has drawn, I don't believe changing the contentView will trigger a redraw. I believe you are only supposed to call setContentView once- from the onCreate method.
Also note that each activity is automatically run in a new thread.
As an aside, you might want to follow some of the Android Developer tutorials. setContentView is primarily for establishing the layout and contents of your activity, usually defined in an XML resource, whereas you seem to be treating it as a means for switching view instances.
when I add a ViewFlipper, the UI thread seems to wait for the onCreate() method in the activity to be finished. Then it shows the second view. Why does it happen?
My current code is:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_menu);
viewFlipper = (ViewFlipper) findViewById(R.id.ScreenSwitch);
viewFlipper.setInAnimation(AnimationUtils.makeInAnimation(this, true));
viewFlipper.setOutAnimation(AnimationUtils.makeOutAnimation(this, true));
//do the necessary loading, when the splash screen persists
doSomeLoading();
viewFlipper.showNext();
}
Actually, the doSomeLoading consists of a for loop counting to ten millions and doing nothing. Now it just waits for loop to be done and shows the second view.
I would really appreciate a solution without having to create a separate Thread, because it seems to be pointless, invalidate() doesn't help there.
Maybe with viewFlipper.showPrevious();?
EDIT
I don't have your full code but here is the idea :
When you do long loading or slow actions, most of the time, you can think AsynTask or background thread.
So you need to create a new inner class. Lets say AsyncLoader. You will implement the method doInBackground() of this class and put your doSomeLoading() in it.
Now, implement onPostExecute() and put your viewFlipper.showNext(); in it.
Then, in your onCreate() method, replace the doSomeLoading() by new AsyncLoader.execute();
This should be nice. I might have forgotten some stuff as it's not real code. Check this for more explanations.
I have a semi-complicated problem and hoping that someone here will be able to help me.
On a click event I create a thread and start a long-running operation based on this method. After the long-running task is completed, it does a callback to another method, which does a post to the handler:
#Override
public void contentSearchModelChanged(Model_ContentSearch csm, ArrayList<Class_Reminder> newRemindersList) {
remindersList = newRemindersList;
mHandler.post(mUpdateDisplayRunnable);
}
Which calls a Runnable:
// post this to the Handler when the background thread completes
private final Runnable mUpdateDisplayRunnable = new Runnable() {
public void run() {
updateDisplay();
}
};
Finally, here is what my updateDisplay() method is doing:
private void updateDisplay() {
if (csModel.getState() != Model_ContentSearch.State.RUNNING) {
if(remindersList != null && remindersList.size() > 0){
r_adapter = new ReminderAdapater(Activity_ContentSearch.this, remindersList, thisListView);
thisListView.setAdapter(r_adapter);
r_adapter.notifyDataSetChanged();
}
}
}
This works beautifully when I do this normally. However, if I change the orientation while the long-running operation is running, it doesn't work. It does make the callback properly, and the remindersList does have items in it. But when it gets to this line:
r_adapter.notifyDataSetChanged();
Nothing happens. The odd thing is, if I do another submit and have it run the whole process again (without changing orientation), it actually updates the view twice, once for the previous submit and again for the next. So the view updates once with the results of the first submit, then again with the results of the second submit a second later. So the adapater DID get the data, it just isn't refreshing the view.
I know this has something to do with the orientation change, but I can't for the life of me figure out why. Can anyone help? Or, can anyone suggest an alternative method of handling threads with orientation changes?
Bara
The problem is that when you change orientations a new activity is spun up from the beginning (onCreate). Your long running process has a handle to the old (no longer visible) activity. You are properly updating the old activity but since it isn't on screen anymore, you don't see it.
This is not an easy problem to fix. There is a library out there that may help you though. It is called DroidFu. Here is a blog post that (much more accurately than I) describes the root cause of what you are seeing and how the DroidFu library combats it: http://brainflush.wordpress.com/2009/11/16/introducing-droid-fu-for-android-betteractivity-betterservice-and-betterasynctask/
Edit: (Adding code for tracking active activity)
In your application class add this:
private Activity _activeActivity;
public void setActiveActivity(Activity activity) {
_activeActivity = activity;
}
public Activity getActiveActivity() {
return _activeActivity;
}
In your Activities, add this:
#Override
public void onResume() {
super.onResume();
((MyApplicationClassName)getApplication()).setActiveActivity(this);
}
Now you can get the active activity by calling MyApplicationClassName.getActiveActivity();
This is not how DroidFu does it. DroidFu sets the active activity in onCreate but I don't feel that is very robust.
I had a similar problem, having a time consuming thread sendEmptyMessage to a handler, which in turn called notifyDataSetChanged on a ListAdapter. It worked fine until I changed orientation.
I solved it by declaring a second handler in the UI thread and make the first handler sendEmptyMessage to this handler, which in turn called notifyDataSetChanged on the ListAdapter. And having the ListAdapter declared as static.
I'm a newbie so I don't know if it is an ugly solution, but it worked for me...
From Jere.Jones description I would assume this works as: The long running process sendEmptyMessage to the handle from the old Activity, which in turn sendEmptyMessage to the handle in the new Activity.