Handler.dispatchMessage hangs/crashes when used from OpenGL thread - android

I'm working on an opengl game for Android. When user looses the game should return to main menu, but this call is to be done from OpenGl ES thread to UI thread and there is some troubles. I've found this post Pass variables between renderer and another class with queueEvent() and tried to add Handler class in the following code:
public class GameActivity extends Activity {
private GLSurfaceView gameView;
private int menuViewID;
private Handler gameOverHandler;
public GameActivity () {
super();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gameOverHandler = new Handler() {
public void handleMessage (Message msg){
handleGameOver();
}
};
gameView = new GameView(this, gameOverHandler);
menuViewID = R.layout.main;
setContentView(menuViewID);
}
/** Called when the user selects the Send button */
public void startGame(View view) {
setContentView(gameView);
gameView.setVisibility(View.VISIBLE);
}
private void handleGameOver() {
/**
* TODO: switch back to main menu
*/
// setContentView(menuViewID); // after this gameView freezes
// gameView.setVisibility(View.GONE); // after this application throw an error: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
}
}
and then later in OpenGL ES code
gameOverHandler.dispatchMessage(Message.obtain(gameOverHandler));
but I'm still getting a freeze or runtime exception (see commented lines in the code above). What I'm missing here or doing wrong?
By the way, how to get the reference for the View instance that was defined in XML (see menuViewID in the code above), or why the findViewById method returns NULL?

You don't want to use the dispatchMessage(msg) method. That is apparently the same as calling the Handler directly. (The documentation is poor. It seems it's intended for system use).
See similar question here:
The difference between Handler.dispatchMessage(msg) and Handler.sendMessage(msg)
Instead, you could use this:
gameOverHandler.obtainMessage(MY_MSG_INT_ID).sendToTarget();

Do you have to handle it in the UI thread? I can handle it just fine from my Game Class using this code:
Intent myIntent = new Intent(myContext, EndGameActivity.class);
((Activity)getContext()).startActivityForResult(myIntent, 0);
I just cast the context from the UI thread activity back into an activity, and then start my new activity for a result from there. I send back a result and then in my activity on the UI thread i catch the result like so:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == 1) {
this.finish();
}
}

Related

pause an activity meanwhile another one is running

I would like to get voice recognition using just one method.
In order to do that i've created 3 classes
the main class
public class Index extends Activity {
private Button boton;
private EditText texto;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_index);
boton = (Button)findViewById(R.id.boton);
texto = (EditText) findViewById(R.id.texto);
boton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
texto.setText(IVRecognition.getInstancia().getComando(Index.this));
}
});
}
}
the intermediate
public class IVRecognition {
//*******************singleton********************
private static IVRecognition instancia;
private IVRecognition (){}
public static IVRecognition getInstancia(){
if (instancia==null) instancia = new IVRecognition();
return instancia;
}
//************************************************
public static String resultado = null;
public String getComando(Context content){
Intent intent = new Intent(content, VRecognition.class);
content.startActivity(intent);
//pause here untill VRecognition.onActivityResult is executed
return resultado;
}
}
and the recognition one
public class VRecognition extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startRecognition();
}
public void startRecognition (){
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,this.getPackageName());
startActivityForResult(intent, 1 /*VOICE_RECOGNITION_REQUEST_CODE*/);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 1 /*VOICE_RECOGNITION_REQUEST_CODE*/ && resultCode == RESULT_OK){
ArrayList<String> result = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
IVRecognition.getInstancia().resultado = result.get(0);
}
this.finish();
}
}
The problem is that when I call VRecognition activity using content.startActivity(intent); the execution of the aplication keeps on going, so the variable called resultado has null value until onActivityResult is executed, which results in a null return value.
Hope you can help me. Cheers
Ian's answer is good. But from your comment, I'd recommend using an IntentService and the BroadcastManager. That way you don't need the intermediate activity. You call the startService(intent) from any activity that wants the VR result (and implements BroadcastReceiver). Then the IntentService calls startActivityForResult(intent,1) and Broadcasts the result.
More info:
http://developer.android.com/training/run-background-service/index.html
It sounds like you want to pause execution until voice recognition is complete. You may want to rethink this; you're calling getComando() from your UI thread, so your application UI will be frozen until recognition is complete. In the (probably quite likely) event that recognition takes more than five seconds, the system will pop up an Application Not Responding dialog. (Also, since you're implementing getComando() by starting another activity within your process, blocking the UI thread in getComando() would prevent recognition from ever running.)
The right way to do this is to use a completion callback. For instance, you could create an IVRecognitionListener interface:
public interface IVRecognitionListener {
public void onRecognitionComplete(String resultado);
}
and pass an instance of that to getComando(). Then instead of just setting IVRecognition.resultado in onActivityResult(), you could call onRecognitionComplete() to notify the caller of the result.

Android - Calling an ordinary object method from an activity

First, I am an android rookie, so my solution ways can be found awkward, and i am open to suggestions.
I am trying to create a game manager object that handles all transitions between activities. And my purpose is that while in an activity, menuOut method will call the changeActivity method of GameManager object with nextActivity argument and changeActivity will start that Activity. I am getting errors consistently, and did not find a solution.
Here is my source codes:
GameManager:
public class GameManager{
public SplashScreen splash = new SplashScreen();
public MainScreen main = new MainScreen();
public LoadingScreen load = new LoadingScreen();
Context tempContext;
public GameManager(Context base) {
super();
tempContext = base;
}
public void start(){
createScreens();
Intent intent = new Intent(tempContext, splash.getClass());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
tempContext.startActivity(intent);
}
public void createScreens() {
//here is the method that i am trying to find a solution
((SplashScreen)splash.getContext()).setGameClass(this);
((MainScreen)main.getContext()).setGameClass(this);
((LoadingScreen)load.getContext()).setGameClass(this);
}
public void changeMenu(MenuType nextMenu, MenuType previousMenu){
Intent intent2;
switch(nextMenu){
case MAIN_SC:
tempContext = main.getContext();
intent2.setClass(tempContext, main.getClass());
intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
tempContext.startActivity(intent2);
case GAME_LOADING_SC:
tempContext = load.getContext();
intent2.setClass(tempContext, load.getClass());
intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
tempContext.startActivity(intent2);
default:
break;
}
}
}
And here is SplashScreen activity:
public class SplashScreen extends Activity {
public Context context = this;
public GameManager gameman;
private static final int SPLASH_DURATION = 4000;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
splash();
menuOut();
}
public Context getContext() {
return this;
}
public void splash() {
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.HORIZONTAL);
ll.setBackgroundResource(R.drawable.game_loop_splash);
setContentView(ll);
Handler handler = new Handler();
// run a thread after 2 seconds to start the home screen
handler.postDelayed(new Runnable() {
#Override
public void run() {
finish();
}
}, SPLASH_DURATION);
}
public void setGameClass(GameManager game){
gameman = game;
}
private void menuOut(){
gameman.changeMenu(MenuType.GAME_LOADING_SC, MenuType.GAME_SPLASH_SC);
this.onDestroy();
}
}
I can not return to the GameManager and call the changeMenu method.
I am very exhausted to get null pointer exceptions.
Any idea?
From what I read, you are trying to implement a singleton pattern. There are two ways I'd recommend to do that on android:
Extend the Application class, register your class in the manifest and use getApplication() in your activities to get access to it:
// In MyApplicationSubclass.java:
public final class MyApplicationSubclass extends Application {
/* ... */
public void myMethod() {
// insert some code here
}
/* ... */
}
// From your Activity:
((MyApplicationSubclass) this.getApplication()).myMethod();
Use a "normal" java singleton pattern, e.g. use a private constructor and keep one static instance within your GameManager class (this is the way the Android docs recommend, but I personally prefer the first way when having something that is logically bound to the Application).
Also, if you're only using your central class to do static stuff, you can just mark all its method as static and access them directly. Transfer Context objects as parameters to these methods, and you should be able to start activities without any static variables (which are sometimes hard to implement properly in Android, as your VM might get restarted from time to time).

How to solve Can not perform this action after onSaveInstanceState in android

Hi I am developing small android application in which I am using Sherlock actionbar. My application contains following things. one main activity which contains 2 fragments and one sample activity. So what i want to do on button click inside fragment one start new sample activity. I am starting sample activity for result. on activity result what I want to do swap tab to Tab2 that is another tab. I did this in following way
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(getSherlockActivity(), "click on button", Toast.LENGTH_SHORT).show();
//getActivity().getActionBar().setSelectedNavigationItem(1);
Intent intent = new Intent(getActivity(), SampleActivity.class);
startActivityForResult(intent, 7);
}
and on activity result what I want to do
#Override #SuppressLint("NewApi")
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 7)
{
getSherlockActivity().getSupportActionBar().setSelectedNavigationItem(1);
}
}
So when I tried to do getSherlockActivity().getSupportActionBar().setSelectedNavigationItem(1);
it shows me following error :
08-08 16:05:21.596: E/AndroidRuntime(6885): FATAL EXCEPTION: main
08-08 16:05:21.596: E/AndroidRuntime(6885): java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=65543, result=0, data=null} to activity {com.example.sampletabapp/com.example.sampletabapp.MainActivity}: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
Any one having solution for this.Need help thank you.
Here is sample project regarding this issue https://www.dropbox.com/s/0d61i9ccol189tv/SampleTabAppZIP.tar.gz. you can check what is actual problem. Any one having solution for this please help. thank you.
To avoid conflicts with immediate execution on the UI thread during lifecycle methods, you can use the various post() methods or implement your own Handler.
I prefer the Handler pattern.
I recommend your Handler be declared as a private static inner class, and have it hold a WeakRefence to whatever it will be manipulating, most likely the fragment. The weak reference will help avoid memory leaks if the fragment is repeatedly run through it's life cycle methods.
private static MyHandler extends Handler {
private final WeakReference<MyFragment> ref;
public MyHandler(MyFragment fragment) {
ref = new WeakReference<MyFragment>(fragment);
}
#Override
public void handleMessage(Message message) {
MyFragment fragment = ref.get();
if (fragment != null) {
//do whatever you want to do.
//For example:
fragment.someCallbackMethod();
}
}
}
Then in your fragment, all you have to do is send a message to your handler inside the onClick() method
#Override
public void OnClick(View v) {
handler.sendMessage(new Message());
}

Can I use a static TextToSpeech in Android?

The TextToSpeech constructor looks like it's designed to be 'owned' by an Activity. I'm producing an app with multiple different Activities, and I don't want to have to initialise a new TextToSpeech instance for each - I want the speech to carry on smoothly even if the Activity is changing.
My idea is to have a static TextToSpeech object accessed by all activities, initialised by the first one.
Does anyone know if the TextToSpeech implementation is thread-safe? I'm guessing not, but someone out there might know.
If I initialise it with the Context of my default Activity, will the TextToSpeech instance stop working when the Activity is destroyed?
I have never tried that, but I think you can pass an Application context as the parameter in the constructor, not necessarily an Activity.
But paying attention to the documentation, I see that the TTS engine has its own queing system, so you can call speak several times without worrying about the thread timing.
Regardind to your questions, I'm not sure about the number two, but as I wrote first, I would try passing an Application context, rather than Activity context.
About number one, well, there is one instance per engine at a time, I guess. And you normally have just one engine, but again, if the engine controls queries queuing, don't worry about the threads.
Thanks to those that told me to pass the ApplicationContext. Turned out that was the easy bit... The hard bit was whether the TextToSpeech object is guaranteed thread-safe.
Thanks for answers telling me how to make something thread-safe / assuming that it is, but the question was about whether the object already is. I probably should have said, I'm fine with implementing thread-safety, but I wanted to know whether I need to bother. And I don't want to assume thread-safety without being certain.
I ran the following and it seemed to work. So I assume the Android SDK TTS is thread-safe, but can't find any documentation saying that it's safe to assume this across all devices, so I'll be wrapping my TTS instance for the time being!
package com.example.testproject;
import java.util.Random;
import android.os.Bundle;
import android.app.Activity;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
public class TestActivity extends Activity implements OnInitListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tts = new TextToSpeech(getApplicationContext(), this);
}
TextToSpeech tts = null;
#Override
public void onInit(int arg0) {
for (int i = 0; i < 100; ++i) {
class Irritate implements Runnable {
Irritate(int iIn) {
i = iIn;
}
#Override
public void run() {
Random r = new Random();
try {
Thread.sleep(r.nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
tts.speak(Integer.toString(i), TextToSpeech.QUEUE_ADD, null);
}
int i;
}
Thread t = new Thread(new Irritate(i));
t.start();
}
}
}
I've always used TTS as an Activity that I startedForResult.
I just fire an intent to it and then wait for it to come back.
If I remember correctly, if returns an array of answers sorted by confidence.
So you if you don't have a Context, then I don't believe there is another way to call it (at least using this model). Not sure if there is an object reference that you can get for it.
However, if there is, to use your idea. Then you can just extend Application and hold the static reference to your TTS in there. That way it's visible to all your Activities. I think this is answer you are looking for.
The above was helpful in helping me resolve this issue. In my case, I had also had a fragment and so, I did the following:
From a fragment (from a fragment, you want to say "getActivity().getApplicationContext()" instead of just "getApplicationContext()") :
public void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode == MY_DATA_CHECK_CODE){
if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
tts = new TextToSpeech(getActivity().getApplicationContext(), new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if(status == TextToSpeech.SUCCESS){
result = tts.setLanguage(Locale.UK);
}
}
});
} else {
// missing data, install it
Intent installIntent = new Intent();
// The ACTION_INSTALL_TTS_DATA intent will take the user to Android Market, and will let the user initiate the download
installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installIntent);
}
}
}
TextToSpeech is not thread-safe with respect to the GUI, because a TextToSpeech listener method is called from a non-GUI thread.
If your listener methods interact with the GUI, you will have to include code to put the GUI changes into the Looper for the GUI thread.
There are plenty of examples of how to wrap a GUI command in a Handler and post it on the GUI thread's looper. Here is a sketch of what you'd do:
public class SpeechUtteranceListener extends UtteranceProgressListener {
#Override
public void onDone(String utteranceId) {
Runnable guiCommand = new Runnable() {
#Override
public void run() {
someButton.setEnabled(true);
}
}
};
runOnUiThread(asrStartCommand);
}
private void runOnUiThread(Runnable command){
Looper.getMainLooper().post(command);
}
}

How to force main Acivity to wait for subactivity in Android?

I am calling a subactivity from main activity. This subactivity should take few numbers from user (i'm using Edit text control to achieve this), save them to static variable in another class and terminate. I want main activity to wait for subactivity but both are just running simultaneously. Even doing sth like that doesn't help:
Thread t = new Thread(new Runnable(){
public void run(){
Log.v("==================", "run "+new Date());
startActivityForResult(new Intent(ctx,myCustomSubactivity.class),1);
} });
Log.v("==================", "calling run "+new Date());
t.start();
try {
t.join();
} catch (InterruptedException e) {Log.v("==================", "can't join");}
Log.v("==================", "back from activity "+new Date());
do you know how to force main activity to wait? Thread.wait() method is not supported in Android(program throws error).
May be I'm missing something but why don't just use startActivityForResult and onActivityResult mechanism? You could get result from you subactivity from intent it was resulted with.
Edit: BTW as far as I understand, if you will run Object.wait() from Activity code if will hold UI tread whitch can result in Application not responding error.
I agree with Nikolay this is definitely the android way to do this.
Start the subactivity with startActivityForResult in the sub activity use setResult to add an result code and an intent with all the numbers you need in the data bundle.
In your first activity overwrite onActivityResult and retrieve the numbers from the Intent.
If you use the static variable this seems easier in the first moment but it is very insecure and there are some cases this may not work. If your program is send to the background your activities will be saved but if the phone runs low on memory the system will close your program and after the user resumes it everything looks like the moment the user left it but the static variables will be recreated to their initialization value.
Try to get used to the way the android activity lifecycle works. Using this approach will result in fewer used memory and a much better user experience.
Check out the Notepad example, it covers exactly this situation. And as others have said, the Android way is to have your first activity start up your second activity (not sub-activity!) and asynchronously listen for a response (not pause or wait, no need for joining, etc.).
Well... you can do it like this (btw, there's not straight forward way):
Have a singleton class, let's call it Monitor:
public class Singleton
{
private Singleton() { }
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
public class ParentActivity extends Activity
{
private void startAndWait()
{
Intent i = new Intent();
// initialize i
startActivityForResult(i);
Singleton si = Singleton.getInstance();
synchronized(si)
{
si.wait();
}
//do remaining work
}
}
public class ChildActivity extends Activity
{
protected void onCreate(Bundle savedInstance)
{
//do all the work
Singleton si = Singleton.getInstance();
synchronized(si)
{
si.notify();
}
}
}
I'm not here to judge if it's a good pattern or not but if you really need an activity to wait for a sub-activity, you can try this approach:
define an object (lock) over which the two activities get synchronized; this can (should) also work as the object to exchange data between those two activities and thus should be defined as static
in parent activity, start an async task (as the UI main thread cannot be in waiting state)
in the async task, start your sub-activity
the async task waits on the lock till it gets notified
the sub-activity does whatever it needs and notifies the waiting thread when it finishes
I did a similar thing in my app and IMHO had a good reason for this (not to bother a user with login screen upon app start or resume, the app tries to re-use credentials stored in a secured place and only in case it fails, it shows this login screen. So yes, basically any activity in my app can get "paused" and waits till the user provides correct credentials in the login activity upon which the login screen finishes and the app continues exactly where it got paused (in the parent activity).
In the code it would be something like this:
ParentActivity:
public class ParentActivity extends Activity {
private static final String TAG = ParentActivity.class.getSimpleName();
public static class Lock {
private boolean condition;
public boolean conditionMet() {
return condition;
}
public void setCondition(boolean condition) {
this.condition = condition;
}
}
public static final Lock LOCK = new Lock();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.parent_layout);
// do whatever logic you need and anytime you need to stat sub-activity
new ParentAsyncTask().execute(false);
}
private class ParentAsyncTask extends AsyncTask<Boolean, Void, Boolean> {
#Override
protected Boolean doInBackground(Boolean... params) {
// do what you need and if you decide to stop this activity and wait for the sub-activity, do this
Intent i = new Intent(ParentActivity.this, ChildActivity.class);
startActivity(i);
synchronized (LOCK) {
while (!LOCK.conditionMet()) {
try {
LOCK.wait();
} catch (InterruptedException e) {
Log.e(TAG, "Exception when waiting for condition", e);
return false;
}
}
}
return true;
}
}
}
ChildActivity:
public class ChildActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.child_layout);
// do whatever you need in child activity, but once you want to finish, do this and continue in parent activity
synchronized (ParentActivity.LOCK) {
ParentActivity.LOCK.setCondition(true);
ParentActivity.LOCK.notifyAll();
}
finish();
// if you need the stuff to run in background, use AsyncTask again, just please note that you need to
// start the async task using executeOnExecutor method as you need more executors (one is already occupied), like this:
// new ChildAsyncTask().executeOnExecutor(ChildAsyncTask.THREAD_POOL_EXECUTOR, false);
}
}

Categories

Resources