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.
Related
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.
While writing an Android activity that submits input queries to a web server, I was thinking instead of having an anonymous inner class to define the networking thread, why can't we use a separate class that extends Thread.
While this works as expected, I would like to know whether this belongs any good or bad practice.
public class GreetActivity extends Activity{
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_greet_activity);
}
public void onClickBtn(View v){
Thread t = new WorkerThread("http://10.0.2.2:8080",this);
t.start();
}
}
class WorkerThread extends Thread{
private String targetURL;
private Activity activity;
public WorkerThread(String url, Activity act){
this.activity = act;
this.targetURL = url;
}
public void run(){
TextView tv = (TextView) activity.findViewById(R.id.textview1);
. . . . . .
}
}
Passing an Activity reference to a thread has some caveats. Activity lifecycle is separate from thread lifecycle. Activities can be destroyed and recreated e.g. by orientation change events. If the activity reference is hold in a thread, the resources held by the activity (lots of bitmap assets for example, taking a lot of memory) are not garbage collectible.
An non-static inner class also has the same problem since the reference to the parent is implicit.
A working solution is to clear the activity reference when the activity is destroyed, and supply a new activity reference when the activity is recreated.
You can only touch your UI widgets in the UI thread as mentioned by blackbelt.
For what it's worth, an AsyncTask is easier to work with than a bare-bones Thread.
In your case, no it is not, since only the UI Thread can touch the UI, your code will make your application crashes with
android.view.ViewRoot$CalledFromWrongThreadException
I'm not quite sure how to debug the phenomenon I'm currently seeing in my Android application.
I have an Activity which is just doing some networking stuff (which needs to be done in background).
This activity is launched from a PreferencesFragment using an Intent.
When the user selects the preference item, the Intent is fired and the Activity is started (then it does the networking stuff and quits using finish()).
I created an AsyncTask to perform the networking actions in the background.
(I thought that onCreate will most probably run in the UI thread...)
But then, an exception occurred:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
Did onCreate() already run in the background???
To test that, I moved the networking functions directly into onCreate().
This was working well...
... at least several times.
Suddenly, an exception was thrown:
java.lang.RuntimeException: Unable to start activity ComponentInfo{...}: android.os.NetworkOnMainThreadException
Moving the code back to the AsyncTask helped... for some time.
Does anyone know why this phenomenon might occur?
Are there scenarios when onCreate() runs in the UI thread and others when onCreate() runs in background?
My class is as simple as this:
public class ReregisterInDb extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
new ReregisterAsyncTask().execute(""); // solution 1
// solution 2
//GCMFunctions gcmFunctions = new GCMFunctions(getApplicationContext());
//gcmFunctions.registerInDb();
super.onCreate(savedInstanceState);
finish();
}
class ReregisterAsyncTask extends AsyncTask<String, Void, Void> {
#Override
protected Void doInBackground(String... params) {
GCMFunctions gcmFunctions = new GCMFunctions(getApplicationContext());
gcmFunctions.registerInDb();
return null;
}
}
}
try to move the call of the method finish() of the activity in the method onPostExecute of async task
You can't do anything before calling super.onCreate(...) put that right at the beginning as I've shown below. EDIT: Also, your use of getApplicationContext in the AsyncTask is likely causing an issue, try creating a global Context variable and initializing that in onCreate and see if that works.
Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
new ReregisterAsyncTask().execute(""); // solution 1
finish();
}
class ReregisterAsyncTask extends AsyncTask<String, Void, Void> {
#Override
protected Void doInBackground(String... params) {
GCMFunctions gcmFunctions = new GCMFunctions(mContext);
gcmFunctions.registerInDb();
return null;
}
}
I finally found out the reason for this strange behavior.
I did not post the contents of the registerInDb() method.
In that method, there is a Toast:
Toast.makeText(context,
"Not currently registered with GCM. [...]",
Toast.LENGTH_LONG).show();
This message is causing the exceptions...
The solution is:
call the function in the UI thread so that the Toast messages work and
enter code heremove the AsyncTask to only cover the actual network code.
Sorry for not giving all the details. I did not think that the Toast message was the root cause.
I learned that you cannot have Toasts in AsyncTasks. The always have to run on the UI thread.
I have two Activities A and B. Activity A will start Activity B. Am doing some UI initialization, db query and local storage read operation in onCreate of Activity B. My problem is Activity B takes long time to come visible. Is that because of such operations in onCreate? I want at least my UI changes to visible first. Please guide me
In your onCreate perform heavy tasks in different Thread/handler/AsyncTask. For Example
public void onCreate(Bundle b){
super.onCreate(b);
setContentView(R.layout.yourlayout);
// do other gui initialization
Handler h = new Handler();
h.post(new Runnable{
public void run(){
// do your heave tasks
}
});
}
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