I have an Activity with a TextView, and I set the label and color of the TextView each time a background thread invokes a method on the Activity. It works properly until I leave the Activity and re-enter it. In that case, the TextView is not updated because the Runnable that is posted for execution on the UI thread is not invoked. Perhaps I need to implement something in onResume(), but I don't know what that would be.
Here is how the TextView is assigned when the Activity is created:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage_nameserver);
statusView = (TextView) findViewById(R.id.statusNameserverButton);
...
}
And here's the method called by the background thread, which updates the TextView:
public void running(boolean running) {
final int color;
final String text;
if (running) {
color = Color.GREEN;
text = "Running";
} else {
color = Color.RED;
text = "Stopped";
}
statusView.post(new Runnable() {
#Override
public void run() {
statusView.setTextColor(color);
statusView.setText(text);
}
});
}
In the debugger I see that when running() is invoked after I've re-entered the Activity, the Runnable passed to statusView.post() is never invoked. Inspection of the statusView object properties in the debugger shows no difference between the success and failure cases. I don't know what's different after resuming the Activity that would cause the Runnable to not be invoked. I tried re-assigning the TextView object in onResume(), with the same code used to assign it in onCreate(), but that didn't help.
First check to see if the Activity after resume is the same one as the original Activity, as the original Activity may have been destroyed by Android. Also, check to see if statusView.post(...) returns true.
Related
I'm developing an app where I will be getting a GCM notification and I'm sending a broadcast to my main activity so that I could update the UI. UI is updating if I have just opened the app and my Main activity is visible. But when I navigate to any other screen and come back to main activity, this time when i recieve the broadcast, the UI is not updating. I debugged and the code is executing fine and when I check the mText value for textview, its still showing the old value. I have also tried invalidate() and postInvalidate() methods. I dont now what I' doing wrong. Could someone please help me.
Note: I'm using setSelected(true) to apply marquee for my textviews.
Intent newi = new Intent("rating-changed");
newi.putExtra("message", "notify");
LocalBroadcastManager.getInstance(context).sendBroadcast(newi);
private void updateUI(final String pSong, final String pArt, final String cSong, final String cArt, final String nSong, final String nArt){
runOnUiThread(new Runnable() {
public void run() {
ImageView ivPrevSong = (ImageView)findViewById(R.id.ivPrevSong);
ImageView ivCurSong = (ImageView)findViewById(R.id.ivCurSong);
ImageView ivNextSong = (ImageView)findViewById(R.id.ivNextSong);
TextView tvPrevSongName = (TextView)findViewById(R.id.tvPrevSongName);
tvPrevSongName.setSelected(true);
TextView tvCurrSongName = (TextView)findViewById(R.id.tvCurrSongName);
tvCurrSongName.setSelected(true);
TextView tvNextSongName = (TextView)findViewById(R.id.tvNextSongName);
tvNextSongName.setSelected(true);
tvPrevSongName.setText(pSong);
tvCurrSongName.setText(cSong);
tvNextSongName.setText(nSong);
tvPrevSongName.refreshDrawableState();
tvCurrSongName.refreshDrawableState();
tvNextSongName.refreshDrawableState();
}
});
}
I have a problem
I am using handler and runnable to update timer inside my app, inside my Runnable I am updating textview, after 1minut I want to show some content, everything works fine until I rotate the screen, every textview is now null, and I couldnt figure out why.
My code:
Runnable mTimer = new Runnable() {
#Override
public void run() {
textView.setText(DateFormat.format("mm:ss", timers - System.currentTimeMillis()));
test();
mHandler.postDelayed(this, TIME);
}
};
Any ideas why this might happen?
Handler probably delivers a Runnable to an Activity that was recycled. Proper use of Handler is like
private Handler mHandler;
private TextView mTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
setContentView(R.layout.yourView);
mTextView = findViewById(R.id.text);
}
#Override
protected void onStart() {
super.onStart();
//start updating every time Activity is started
handler.postDelayed(mTimer, oneMinuteDelay);
}
#Override
protected void onStop() {
super.onStop();
//make sure to remove all messages
handler.removeCallbacksAndMessages(null);
}
In theory, this (null views) should not happen.
When you change the screen orientation, the activity leaves the screen and becomes useless, but it still exists and references the views. Your runnable references the instance of activity that has created it, so the activity cannot die while the runnable is still there. At least, so it was. Which Android version do you use?
It seems I understand what you mean. You mean null contents in the views. You have to create a static variable, say, lastInstance:
class MyActivity extends Activity {
static MyActivity lastInstance;
void onCreate(...) {
...
lastInstance = this;
}
// no need to reference an instance of any Activity, so static
static class MyRunnable implements Runnable {
#Override
public void run() {
lastInstance.textView.setText(DateFormat.format("mm:ss", timers - System.currentTimeMillis()));
lastInstance.test();
mHandler.postDelayed(this, TIME);
}
}
static Runnable mTimer = new MyRunnable();
}
I do not recommend android:configChanges="screenSize|keyboardHidden|orientation" because this is not the only case when Android recreates an Activity, so this way you will not fix any bugs, you will just make them more difficult to reproduce.
For this thing you have to specify in your manifest with the specified line in your activity tag then your issue will be fixed.
i.e,
<activity android:name="your activity"
android:configChanges="screenSize|keyboardHidden|orientation">
</activity>
Then it will work for you on rotating the screen also.
Edited Answer
Better check that textview If it is null create a reference and then add the data it may fix your issue. or meanwhile you can pass your old data from onSavedInstance();
and you can get the data from onCreate(SavedInstance savedinstance)
here it will returns that prevoius data what you are setted in onsavedInstance Method.
try this for data exchange it will work
After rotate your activity recreates, so textView is null.
Please remove the handler code from the runnable. Also first create object of handler then write the handlers post delayed method where you want. Main use of handler is to update UI from thread.
If the Activity doesn't crash when you turn round the device, it means that the textView is there. If you see nulls on the screen it is the content of the textView that is being set as null.
In the text, the only variable I see is timers.
Where is this variable defined and where is it being set?
First check that you properly initialize the handler as below :
handler = new Handler();
The null pointer error may come if you not initialize the handler.
This question has to do with an AsyncTask that needs to kill a ProgressDialog. While the task is running, the activity gets destroyed and recreated (phone rotation, for example). A new dialog gets made with the help of onSaveInstanceState() but the AsyncTask, spawned by the previously destroyed activity, can't see it.
Picture, if you will... (quick mockup code for example's sake).
public class Bob extends Activity {
private ProgressDialog m_d;
public void onCreate(Bundle b) {
m_d = new ProgressDialog(this);
// ...
if (b != null) {
if (b.getBoolean("dialog") == true)
m_d.show();
}
// ...
}
public void onSaveInstanceState(Bundle b) {
super.onSaveInstanceState(b);
b.putBoolean("dialog", (m_d.isShowing()));
}
public void onDestroy() {
if (m_d.isShowing()) {
m_d.dismiss();
m_d = null;
}
//...
}
}
The AsyncTask onPreExecute() does m_d.show() and onPostExecute() does m_d.hide().
The problem is when my activity gets recreated (say, on phone rotation), the AsyncTask seems to have the old m_d. It is null, because that got killed in onDestroy().
It doesn't have the new m_d, created when the activity got recreated. So now I have a ProgressDialog and the guy who was supposed to kill it in onPostExecute() can't see it.
Now what? I need the old AsyncTask to somehow signal the new ProgressDialog to go away.
private OnItemSelectedListener onSpinnerSelection = new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
// ...
new Switcharoo().execute();
}
}
private class Switcharoo extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
m_d.show();
// ...
}
protected void onPostExecute() {
if (m_d != null) m_d.hide();
// ...
}
protected Void doInBackground(Void... arg0) {
// ...
}
}
If Android doesn't kill my activity while the task is running, it's fine. The ProgressDialog pops up and goes away like I expect.
If Android decides to restart my activity for any reason while the task is running, m_d will be null in onPostExecute() even though I recreated it in onCreate() - it has the old m_d, not the new one.
There are two problems: the first is that the AsyncTask is bound to the activity that has created it. I mean, the instance of the activity.
If you want it to survive between activities rotations you have to store it somewhere else (check this for example). In any case, you need to have a reference to it from the new activity (another thing you could do is to use a model fragment, i.e. a fragment with no UI that you set as setRetainInstance(true)).
Once you have the reference to the async task from the newly created activity, nothing prevents you to have m_d local to the async task and a setter method that updates it with the new dialog.
Note also that it would be a good practice to have weak references pointing to the activity and the dialog itself in order to allow garbage collection. Otherwise the dialog (and probably the activity itself) would not be freed until the execution of the task itself ended.
I have a parent activity, and a child activity that extends the parent activity. When the parent starts the child activity,
Which onCreate gets executed first? The child's or parent's?
There is a particular variable I am setting in the Child activity's onCreate method, and right now, it looks like it takes a while to get to the Child activity's onCreate, and so the methods in the Parent are reporting an empty variable. Whereas when I make the Parent sleep for a while, it reports the correct variable.
Thanks
Chris
Parent Activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
goButton = (ImageButton) this.findViewById(R.id.goButton);
goButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
Intent childIntent = new Intent("com.example.Child");
String newValue = "Child Value";
Bundle bun = new Bundle();
bun.putString("value", newValue); // add two parameters: a string and a boolean
childIntent.putExtras(bun);
startActivity(childIntent);
}
});
this.doTheWork("Parent Value");
}
private void doTheWork(String value) {
new MyNewThread(value).start();
}
public String getTheValue(String value) {
return "My Value is: " + value;
}
private class MyNewThread extends Thread {
String value;
public LoadThread(String v) {
this.value = v;
}
#Override
public void run() {
String str = getTheValue(this.value);
}
}
Child Activity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bun = getIntent().getExtras();
childValue = bun.getString("value");
}
public String getTheValue(String value) {
return "My Value is: " + value;
}
So, basically, even after the Parent starts the Child, it still returns "Parent Value", but when I have the thread sleep, it return "Child Value".
Your extended class's method (the child class) is called first, but your super class's method is called immediately after since the first line in your child class's method is
Child Activity
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
This is an explicit call to your super class's onCreate Method.
First answer about the sequence. First is call parent and next one is child. This information you can read from your source code.
Next information is about why you have different result when you are using sleep.
First of all the code of Child Activiry onCreate is not completed because is not show how you are using field (or local variable) childValue. But I expect that you are creating new thread in child object too. Please remember that thread are not run in the moment when you are creating the thread but in the moment when the runtime find time for this. The method sleep inform runtime that you can run the thread because your main thread is sleeping and this is the reason of different result. Important is that you are creating two thread one in child and one in parent.
I've been working with AsyncTasks in Android and I am dealing with an issue.
Take a simple example, an Activity with one AsyncTask. The task on the background does not do anything spectacular, it just sleeps for 8 seconds.
At the end of the AsyncTask in the onPostExecute() method I am just setting a button visibility status to View.VISIBLE, only to verify my results.
Now, this works great until the user decides to change his phones orientation while the AsyncTask is working (within the 8 second sleep window).
I understand the Android activity life cycle and I know the activity gets destroyed and recreated.
This is where the problem comes in. The AsyncTask is referring to a button and apparently holds a reference to the context that started the AsyncTask in the first place.
I would expect, that this old context (since the user caused an orientation change) to either become null and the AsyncTask to throw an NPE for the reference to the button it is trying to make visible.
Instead, no NPE is thrown, the AsyncTask thinks that the button reference is not null, sets it to visible. The result? Nothing is happening on the screen!
Update: I have tackled this by keeping a WeakReference to the activity and switching when a configuration change happens. This is cumbersome.
Here's the code:
public class Main extends Activity {
private Button mButton = null;
private Button mTestButton = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mButton = (Button) findViewById(R.id.btnStart);
mButton.setOnClickListener(new OnClickListener () {
#Override
public void onClick(View v) {
new taskDoSomething().execute(0l);
}
});
mTestButton = (Button) findViewById(R.id.btnTest);
}
private class TaskDoSomething extends AsyncTask<Long, Integer, Integer>
{
#Override
protected Integer doInBackground(Long... params) {
Log.i("LOGGER", "Starting...");
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 0;
}
#Override
protected void onPostExecute(Integer result) {
Log.i("LOGGER", "...Done");
mTestButton.setVisibility(View.VISIBLE);
}
}
}
Try executing it and while the AsyncTask is working change your phones orientation.
AsyncTask is not designed to be reused once an Activity has been torn down and restarted. The internal Handler object becomes stale, just like you stated. In the Shelves example by Romain Guy, he simple cancels any currently running AsyncTask's and then restarts new ones post-orientation change.
It is possible to hand off your Thread to the new Activity, but it adds a lot of plumbing. There is no generally agreed on way to do this, but you can read about my method here : http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html
If you only need a context and won't use it for ui stuff you can simply pass the ApplicationContext to your AsyncTask.You often need the context for system resources, for example.
Don't try to update the UI from an AsyncTask and try to avoid handling configuration changes yourself as it can get messy. In order to update the UI you could register a Broadcast receiver and send a Broadcast.
You should also have the AsyncTask as a separate public class from the activity as mentioned above, it makes testing a lot easier. Unfortunately Android programming often reinforces bad practices and the official examples are not helping.
This is the type of thing that leads me to always prevent my Activity from being destroyed/recreated on orientation change.
To do so add this to your <Activity> tag in your manifest file:
android:configChanges="orientation|keyboardHidden"
And override onConfigurationChanged in your Activity class:
#Override
public void onConfigurationChanged(final Configuration newConfig)
{
// Ignore orientation change to keep activity from restarting
super.onConfigurationChanged(newConfig);
}
To avoid this you can use the answer givin here: https://stackoverflow.com/a/2124731/327011
But if you need to destroy the activity (different layouts for portrait and landscape) you can make the AsyncTask a public class (Read here why it shouldn't be private Android: AsyncTask recommendations: private class or public class?) and then create a method setActivity to set the reference to the current activity whenever it is destroyed/created.
You can see an example here: Android AsyncTask in external class