In my app I am using TTS. I have 20 different activities which are changed when the user swipe left or right. According the activity, a text is spoken. I am executing tts with separate thread and activity selection is done with main thread. But the problem is very slow, the UI feels slugish. When I swipe left or right, once tts is finished speaking the text, the activity changes which shouldn't happen because I am using separate thread for tts.
Here is the codE:
TTS class:
public class textToSpeech {
TextToSpeech tts=null;
public textToSpeech(Context con)
{
tts = new TextToSpeech(con,new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if(status != TextToSpeech.ERROR) // initialization me error to nae ha
{
tts.setPitch(1.1f); // saw from internet
tts.setSpeechRate(0.4f); // f denotes float, it actually type casts 0.5 to float
tts.setLanguage(Locale.US);
}
}
});
}
public void SpeakText (String text)
{
tts.speak(text, TextToSpeech.QUEUE_FLUSH, null); // TextToSpeech.QUEUE_FLUSH forces the app to stop all the sounds that are currently playing before speaking this text
}
public void stopSpeak()
{
tts.stop();
}
Gesture Reader Class: (separate class)
public void decideAlphabet()
{
tts.stopSpeak();
threadForTTS.start();
switch (i)
{
case 0:
activities=null;
activities = new Intent(contxt,A.class);
contxt.startActivity(activities);
break;
case 1:
activities=null;
activities = new Intent(contxt,B.class);
contxt.startActivity(activities);
break;
....... 20 more case statements for selecting activities
}
decideActivity() method is called when it is checked, which swipe was made, swipe to right or left.
NOTE:
Before adding tts in this app, the UI was performing properly without lag or slowness. After I added TTS, the app became slow. How can I solve this problem
Regards
I had the same problem and was about to comment on seeing the following logcat error ...skipped x many frames. The application may be doing too much work on its main thread.
Of course I was sure TTS was being called from another thread which I checked using Thread.currentThread().getName() But it turns out however that OnInit was indeed still running on the main thread and it looks like setting the language is an expensive operation. A quick change to run contents of onInit in a new thread and the UI freezing/choreographer complaining stopped:
#Override
public void onInit(int status) {
new Thread(new Runnable() {
public void run() {
if(status != TextToSpeech.ERROR) // initialization me error to nae ha
{
tts.setPitch(1.1f); // saw from internet
tts.setSpeechRate(0.4f); // f denotes float, it actually type casts 0.5 to float
tts.setLanguage(Locale.US);
}
}
}
}).start()
Related
I have a Timer in my App that infinitely runs an Animation. like this:
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
//Running Animation Code
}
});
}
}, 1000, 1000);
Now I realized that this code runs even if user click Back Button of android. if fact it runs in the background and it seems uses a lot of memory.
I need this code run ONLY if user in the app. In fact when user click on Back Button, this Timer goes to end and if user clicks on Home Button, after a while that user doesn't use the App, terminates this Timer.
What I need is to prevent using memory. Because i realized if this codes runs a while, App freezes! I need a normal behavior.
If your Activity is the last element in the BackStack, then it will be put in the background as if you pressed the Home button.
As such, the onPause() method is triggered.
You can thus cancel your animation there.
#Override protected void onPause() {
this.timer.cancel();
}
You should as well start your animation in the onResume() method.
Note that onResume() is also called right after onCreate(); so it's even suitable to start the animation from a cold app start.
#Override protected void onResume() {
this.timer.scheduleAtFixedRate(...);
}
onPause() will be also called if you start another Application from your app (e.g: a Ringtone Picker). In the same way, when you head back to your app, onResume() will be triggered.
There is no need to add the same line of code in onBackPressed().
Also, what's the point in stopping the animation in onStop() or onDestroy()?
Do it in onPause() already. When your are app goes into the background, the animation will already be canceled and won't be using as much memory.
Don't know why I see such complicated answers.
You can do it like this, in onBackPressed() or onDestroy(), whatever suits you.
if (t != null) {
t.cancel();
}
If you need, you can start timer in onResume() and cancel it in onStop(), it entirely depend on you requirement.
If a caller wants to terminate a timer's task execution thread
rapidly, the caller should invoke the timer's cancel method. - Android Timer documentation
You should also see purge and
How to stop the Timer in android?
Disclaimer: This might not be the 100% best way to do this and it might be considered bad practice by some.
I have used the below code in a production app and it works. I have however edited it (removed app specific references and code) into a basic sample that should give you a very good start.
The static mIsAppVisible variable can be called anywhere (via your App class) in your app to check if code should run based on the condition that the app needs to be in focus/visible.
You can also check mIsAppInBackground in your activities that extend ParentActivity to see if the app is actually interactive, etc.
public class App extends Application {
public static boolean mIsAppVisible = false;
...
}
Create a "Parent" activity class, that all your other activities extend.
public class ParentActivity extends Activity {
public static boolean mIsBackPressed = false;
public static boolean mIsAppInBackground = false;
private static boolean mIsWindowFocused = false;
public boolean mFailed = false;
private boolean mWasScreenOn = true;
#Override
protected void onStart() {
applicationWillEnterForeground();
super.onStart();
}
#Override
protected void onStop() {
super.onStop();
applicationDidEnterBackground();
}
#Override
public void finish() {
super.finish();
// If something calls "finish()" it needs to behave similarly to
// pressing the back button to "close" an activity.
mIsBackPressed = true;
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
mIsWindowFocused = hasFocus;
if (mIsBackPressed && !hasFocus) {
mIsBackPressed = false;
mIsWindowFocused = true;
}
if (!mIsWindowFocused && mFailed)
applicationDidEnterBackground();
if (isScreenOn() && App.mIsAppVisible && hasFocus) {
// App is back in focus. Do something here...
// this can occur when the notification shade is
// pulled down and hidden again, for example.
}
super.onWindowFocusChanged(hasFocus);
}
#Override
public void onResume() {
super.onResume();
if (!mWasScreenOn && mIsWindowFocused)
onWindowFocusChanged(true);
}
#Override
public void onBackPressed() {
// this is for any "sub" activities that you might have
if (!(this instanceof MainActivity))
mIsBackPressed = true;
if (isTaskRoot()) {
// If we are "closing" the app
App.mIsAppVisible = false;
super.onBackPressed();
} else
super.onBackPressed();
}
private void applicationWillEnterForeground() {
if (mIsAppInBackground) {
mIsAppInBackground = false;
App.mIsAppVisible = true;
// App is back in foreground. Do something here...
// this happens when the app was backgrounded and is
// now returning
} else
mFailed = false;
}
private void applicationDidEnterBackground() {
if (!mIsWindowFocused || !isScreenOn()) {
mIsAppInBackground = true;
App.mIsAppVisible = false;
mFailed = false;
// App is not in focus. Do something here...
} else if (!mFailed)
mFailed = true;
}
private boolean isScreenOn() {
boolean screenState = false;
try {
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
screenState = powerManager.isInteractive();
} catch (Exception e) {
Log.e(TAG, "isScreenOn", e);
}
mWasScreenOn = screenState;
return screenState;
}
}
For your use you might want to create a method in your activity (code snippet assumes MainActivity) that handles the animation to call the t.cancel(); method that penguin suggested. You could then in the ParentActivity.applicationDidEnterBackground() method add the following:
if (this instanceof MainActivity) {
((MainActivity) this).cancelTimer();
}
Or you could add the timer to the ParentActivity class and then not need the instanceof check or the extra method.
So I am creating an app where I get the string from my AsyncTask which I have created as a subclass in my MainActivity class in order to get the variables I receive from the internet. And according to variables I get I change the images accordingly after every 5 seconds. Now the task is successful but on activity refresh I keep getting the default layout I created in activity_main.xml and again it changes to the one I want.
Posting my code below.
Thread thread = new Thread() { //thread I am running every 5 secs
#Override
public void run() {
try {
synchronized (this) {
wait(5000);
syncapp sa = new syncapp(); //AsyncTask to get String from Internet
sa.execute();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Intent mainActivity = new Intent(getApplicationContext(), MainActivity.class); //Creating intent to restart the activity (Need a workaround if possible)
startActivity(mainActivity);
}
;
};
thread.start();
}
public void setImage(String str) //Function I will call to change Image
{
vacancy =0;
b = a.toCharArray();
for (int i = 0; i < 6; i++)
if (b[i] == '0'){
iv[i].setImageResource(R.drawable.img2);
vacancy++;}
else if (b[i] == '1') {
iv[i].setImageResource(R.drawable.img1);
}
Log.i("abc ", a);
tv.setText("InBackGround" + str);
}
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/* for(int i =0;i<6;i++) {
outState.putChar("imv"+(i+1), b[i]);
}*/
outState.putString("a",a); //Getting a from the internet (php file)
Log.i("Saved State", "Activity A - Saving instance state");
}
Now what I want is if you have a better method to do this thing. Eg. In a Stock market application the prices keep on changing. The same way I want my image to change according to the data I get.
If this is the only method then how do I save changes I make
(eg. like in the code above setImageResource to img2) permanently.
If I can use something else ImageView.
I have already used onSaveInstanceState But as I am taking values from the internet I don't know I am not able to use them.
So first of all.. when working with UI elements such as Views, Widgets, you would want to avoid spawning your own threads, as View can be touched only from the thread it was created from.
Second.. you would also want to avoid sleeping inside your worker thread - basically just use the Handler class (from android.os) and post a delayed Runnable, like so: https://stackoverflow.com/a/20784353/2102748. Just be sure to stop all work on that particular handler when your Activity stops, like so: https://stackoverflow.com/a/3627399/2102748
Third - you should perhaps load the photo immediately (on the same AsyncTask or thread) if that is the only thing you need, but be sure to post the "set bitmap to view" work to the handler you created.
Hope this helps.
I'm working on an app that downloads information from my server after an NFC card has been detected.
When a card is detected, I start
- an Asynctask to download some data from my server
- an animation of a popup appearing on the screen
After both the asynctask and the animation are done, I want to start a method that displays the downloaded data in the popup.
What is the correct way to trigger this new method? It can only start when both conditions are met.
In the async task you use to download data add the onPostExecute method to remove the animation popup and show the downloaded data as well like this :
protected void onPostExecute(Long result) {
//put code to disable animation popup
//code for displaying downloaded data popup
}
For more info check out http://developer.android.com/reference/android/os/AsyncTask.html
you Animation object has the method setAnimationLister. It takes as parameter a Class Object that implements the interface Animation.AnimationListener . This interface requires three methods to be implemented:
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
The onAnimationEnd is triggered when the animation ends. If I have not misunderstood you, this is what you need
EDIT:
you could have two booleans value, boolean animationFinished = false, downloadFinished = false; When onPostExecute is called put downloadFinished to true and call yourMethod.
When onAnimationEnd is triggered animationFinished = true and call yourMethod. yourMethod should start like:
if (!animationFinished || !downloadFinished)
return;
Lets say I have multiple running Activities; A, B, and C. Each share a similar optionsmenu with some differences in their execution ("start" option might be slightly different in Activity A than B). I also have a static class called "values" that is tied to activity A. It also has the context of the current running activity.
At times values may call an item in the optionsmenu of the current running activity. My code is messy(see below) so I would like to organize it to a more readable form.
My goal is to set up values so that it can call just a function of the current running activity instead of an optionmenu item of that activity. Inside the activity an item of the optionsmenu would just call a function instead of code inside of it (for organization reasons).
Here a sample of values.class calling the item of an optionsmenu of the current running activity.
public void startExample() {
Runnable startRun = new Runnable() {
#Override
public void run() {
handler.post(new Runnable() { // This thread runs in the
// UI
#Override
public void run() {
((Activity) getCurrentContext()).openOptionsMenu();
((Activity) getCurrentContext()).closeOptionsMenu();
((Activity) getCurrentContext()).onOptionsItemSelected(theMenu.findItem(R.id.start));
}
});
}
};
new Thread(startRun).start();
}
As you can see values.startExample() calles the start item of the options menu of the current running activity. I would like to change this so that it calls a function based off of the current running activity instead. So I was thinking that I could do something like this instead.
In values.class
ActivityB b = new ActivityB
public void startExample() {
Runnable startRun = new Runnable() {
#Override
public void run() {
handler.post(new Runnable() { // This thread runs in the
// UI
#Override
public void run() {
if( ((Activity) getCurrentContext()).getClass().getName().equals("package.ActivityB") ) {
b.start();
}
}
});
}
};
new Thread(startRun).start();
}
And Activity B might look like.
public class ActivityB extends Activity {
//class code here
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case start:
this.start();
break;
}
}
public void start() {
//code here
}
}
Is this possible? I know that this question might sound confusing so please ask questions and I might be able to simplify it again.
No no no no no. This is like pulling a crowbar to Android and just smashing it to pieces. Activities are not ever meant to be instantiated via new.
If all you want is for there to be some shared behavior between the methods that the different Activities call in their onOptionsItemSelected(), then either create a super activity and call the super before you add whatever behavior you want in the particular subclasses or else create a function accessible to all activities that contains what doesn't change and then add what does change inside of the subclass. Either way, Activities are meant to be ran via providing a reference to the class in startActivity, etc. You can't treat an Activity like an ordinary java object (even though it ultimately is) because it has a lifecycle that is carefully managed by the system.
I programmed game, which is supposed to use .startActivity on activity which should display score, the problem is, that instead of properly displaying the activity, i'm getting "Deprecated Thread methods are not supported". I googled this error and i removed all Thread.stop() in my application, but it did not helped. Could there be any other reason why i'm getting this type of error?
public class GameFrame extends Activity implements OnTouchListener, OnLossListener {
Panel game;
#Override
public void onCreate(Bundle savedInstanceState) {
Display d=getWindowManager().getDefaultDisplay();
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
Point p=new Point(d.getWidth(),d.getHeight());
game=new Panel(this,p);
setContentView(game);
game.setOnTouchListener(this);
game.setOnLossListener(this);
}
public boolean onKeyDown (int kc,KeyEvent ke){
if (kc==23){
game.shoot();
}
return true;
}
public void onPause(){
super.onPause();
game.thread.setState(false);
game.mech.setState(false);
this.finish();
}
#Override
public boolean onTouch(View v, MotionEvent me) {
if (v.equals(game)){
game.setPosOfPlane((int)me.getX());
}
return true;
}
#Override
public void lost(int score) {
Intent i=new Intent (this,EndGame.class);
i.putExtra("score",score+"");
startActivity (i);
}
}
Use Thread.currentThread().interrupt(); instead stop();
Hope it helps cya!
I can't tell you what exactly is the problem, but I can give you some hints.
First of all you usually create a thread when starting your application and let it run until you want to exit your game (user pressed BACK). Other than that you only pause your game thread, e.g. when you're displaying another activity or your app is temporarily suspended (user pressed HOME).
If you want to end your game thread, just let it die naturally. Usually such thread runs in some sort of a loop, so you just exit this loop and the thread will terminate on its own. You usually call join() on the game thread from the main thread to block it until the game thread dies.
You should never manipulate thread's state directly.