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);
}
}
Related
I have a main application with a button defined in layout.xml.
When I click on the button, I want to call a method located in another class, and when I try to create a TextView in that class, I must provide an argument to the new TextView(???) command, and I don't know what to do.
I reckon that this is a 2 seconds question for you folks, and for me, newbiiie as it is, it is a tough one.
Just in case, here are the relevant sections of code:
The section of the main class that is applicable:
public class MainActivity extends Activity {
public DateAndTime cur_datetime = new DateAndTime();
public LongLat cur_longlat = new LongLat();
public int current_location_number = 0;
public ArrayList<LocationInfo> locations = null;
Button doSunButton;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addListenersForButtons();
dosomething();
}
public void addListenersForButtons()
{
doSunButton = (Button) findViewById(R.id.dosun_button_id);
doSunButton.setOnClickListener( new OnClickListener()
{
#Override
public void onClick(View arg0)
{
DoSun myDoSun = new DoSun();
Log.v("button", "Am I really calling from the button function...");
myDoSun.doSun2(locations, current_location_number);
} // end of dosun on click on dosun_id button
); // end of define listener
} // end of addListenersForButtons(0) method
}
The class whose method is called:
package com.example.sunandmoon;
import java.util.ArrayList;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
public class DoSun extends Activity{
public void doSun2(ArrayList<LocationInfo> locations, int current_location_number)
{
//Log.v("doSun", "Am I really there!");
TextView textViewsunrise = new TextView(??????);
textViewsunrise = (TextView) findViewById(R.id.sunrise_id);
((TextView)textViewsunrise).setText("From DoSun2! " + locations.get(current_location_number).getnameGiven());
} // end of doSun(0) method
}
And by the way, I also wonder how I could avoid passing the two parameters current_location_numberv and ArrayList locations to the doSun2 method, since they "should be" globals (you can see that I come from C...).
Thank you for your help.
And to you flamers of all kinds, yes, I have tried to find an answer to this...
There are multiple problems in your code.
Do not use classes extended from Activity for anything other than, er, being Activities! An Activity represents a single task focused object with which the user interacts. Loosely, think of each screen in an app as being an Activity.
"should be globals". No they should not. There are occasional situations in Android in which globals make sense. This is not one of them. You should not avoid passing the arguments to doSun2() since it behaves according to those arguments. It would be completely anti-pattern to use globals.
The TextView you want to create rightly belongs to MainActivity. It should be responsible for creating and managing it. To do this, have the method in DoSun take arguments (locations, currentLocationNumber) and have it return a structure of some sort with all of the values you need to create the TextView back in MainActivity. You could create a helper method in MainActivity which takes the structure returned by doSun2() as an argument and returns a new TextView ready for adding to the Activity Layout.
In general, only Activities should create and manage any UI elements.
If DoSun really should be an Activity, then do not attempt to create an instance of it via it's constructors. Instead, create an Intent and use startActivity to create it.
All this said, you should get into the habit of describing what you want to achieve, since your approach (which I have responded to) may not be the right one.
Good luck!
I have tried almost all the solutions from SO but no success :(.
I have a simple myJavaClass.java with a couple of functions.
One of the functions in myJavaClass : startActivity() starts MyCustomActivity
public startActivity(Context context)
{
Intent intent = new Intent(context, MyCustomActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent);
}
This launches MyCustomActivity() as expected.
Now I have another function in myJavaClass.java to close/finish MyCustomActivity but it is not able to do so!
I have tried
Making MyCustomActivity SingleTop in manifest and creating the activity via an intent as above
Passing an activity instance to "this" in onCreate() of MyCustomActivity and calling MyCustomActivity.activity.finish() from myJava.class but that doesnt work as well
Please help me. I have been stuck here for hours now. I know the solution is very simple and conceptual but I am a newbie. Just building Java/Android concepts!
EDIT
MyCustomActivity
public Activity activity;
OnCreate()
{
...
this = activity;
}
MyJavaClass
public closeActivity(Context context)
{
Activity customActivity = MyCustomActivity.activity;
customActivity.finish();
}
I think that what you are trying to do is fundamentally bad. For a start, outside of the Activity code, there are no guarantees that the activity still exists - the memory manager may have cleaned it up, the user may have pressed Back etc. Think of Activities as independent entities - you can start them, and you can optionally get a result back when they finish what they're doing, but that's it.
Think about whether you really have to programmatically close the activity from outside it - I'd say this is an unusual design, but there are circumstances where it may be appropriate.
If so, what I think you want is a publish/subscribe system whereby MyCustomActivity can register a listener with MyJavaClass, and then receive a callback whereupon it can 'finish' itself.
public Activity activity implements FinishListener
{
public void onCreate(...)
{
//where does MyJavaClass come from? see in a minute
MyJavaClass myjava = getMyJavaclass();
myJava.addFinishListener( this );
}
public void onFinishCallback()
{
this.finish();
}
}
and
public class MyJavaClass
{
private List<FinishListener> finishListeners = ...;
public void addFinishListener( FinishListener fl )
{
this.finishListeners.add(fl);
}
public closeActivity(Context context)
{
for ( FinishListener fl : finishListeners )
{
fl.onFinishCallback();
}
}
}
and
public interface FinishListener
{
void onFinishCallback();
}
Now the only remaining issue is how to get MyJavaClass from the Activity. That's up to you - you may already know how, you may be able to put it in your Application implementation, it could be a singleton (bad), the listeners could be static (bad) or various other options.
Oh, and don't forget to remove the listener again in the Activity's onDestroy() method!
Just try this....
public closeActivity(Activity _activity)
{
_activity.finish();
}
you can't finish activity from other class until you have the reference of instance of Activity in that class, give the reference in that class and call finish() method to stop the activity.
activity.finish();
I hope it exists.
I would like to store the time when the application loses focus and then check if it has lost focus for more than n-minutes to bring up a lock.
Seeing how an application is composed of activities, I think there will not be a direct equivalent. How would I be able to achieve similar results?
EDIT
I tried to extend the Application class to registerActivityLifecycleCallbacks() and realized I will not be able to use this approach because it is only available in API Level 14+
Allow me to share how I made a backwards compatible solution.
I had already implemented the locking of my app on launch if there was a passcode associated with the account. To be complete, I needed to handle situations where other applications (including the home activity) take over for n-minutes.
I ended up making a BaseActivity that all my Activities extend.
// DataOperations is a singleton class I have been using for other purposes.
/* It is exists the entire run time of the app
and knows which activity was last displayed on screen.
This base class will set triggeredOnPause to true if the activity before
"pausing" because of actions triggered within my activity. Then when the
activity is paused and triggeredOnPause is false, I know the application
is losing focus.
There are situations where an activity will start a different application
with an intent. In these situations (very few of them) I went into those
activities and hard-coded these lines right before leaving my application
DataOperations datao = DataOperations.sharedDataOperations();
datao.lostFocusDate = new Date();
*/
import java.util.Date;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
public class BaseActivity extends Activity {
public boolean triggeredOnPause;
#Override
public void onResume(){
super.onResume();
DataOperations datao = DataOperations.sharedDataOperations();
if (datao.lostFocusDate != null) {
Date now = new Date();
long now_ms = now.getTime();
long lost_focus_ms = datao.lostFocusDate.getTime();
int minutesPassed = (int) (now_ms-lost_focus_ms)/(60000);
if (minutesPassed >= 1) {
datao.displayLock();
}
datao.lostFocusDate = null;
}
triggeredOnPause = false;
}
#Override
public void onPause(){
if (triggeredOnPause == false){
DataOperations datao = DataOperations.sharedDataOperations();
datao.lostFocusDate = new Date();
}
super.onPause();
}
#Override
public void startActivity(Intent intent)
{
triggeredOnPause = true;
super.startActivity(intent);
}
#Override
public void startActivityForResult(Intent intent, int requestCode) {
triggeredOnPause = true;
super.startActivityForResult(intent, requestCode);
}
}
If you are going to use this solution and have trouble implementing the equivalent of my DataOperations class, please comment and I can post the necessary code.
I have a question regarding an Android application. I want to, later on, create a game and i am currently trying out classes and functions that I need to understand.
At the moment im trying to get a grip of how to use threads in a good way, but my application is "force closing" when i touch the button.
For this test application, all have on the screen is one TextView and one button.
The button is calling threadStart() when pressed. (onClick in xml)
And what i want it to do is to create a thread which increases the variable value by 1 and then report to the UI thread which then update the textview with the new value.
Can someone see what i am doing wrong with this small pice of code?
package com.weldeborn.tc;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.TextView;
public class ThreadCounter extends Activity {
TextView txtCounter1;
int value=0;
final Handler mHandler = new Handler();
final Runnable mUpdateResults = new Runnable() {
public void run() {
updateResult();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
txtCounter1 = (TextView) findViewById(R.id.counter1);
}
protected void threadStart() {
Thread t = new Thread() {
public void run() {
doSomething();
mHandler.post(mUpdateResults);
}
};
t.start();
}
private void doSomething() {
value = value+1;
}
private void updateResult() {
txtCounter1.setText(value);
}
}
My code is based on an example from Android Developer: The Common Tasks and how to do them section under the "Handling Expensive Operations in the UI Thread" heading.
I am thankful for any help.
setText doesn't work correctly when you pass an integer, directly. Try converting it to String before:
txtCounter1.setText(String.valueOf(value));
Also, check this answer about the usage of threads that need to update the UI.
if threadStart is your onClick the signature needs to be
public void threadStart(View v)
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);
}
}