I am a newer to android, I read the blog, many people said we can not update the view in other thread, we have to update it in ui thread, what is the reason, why it reports exception when we do it? can anynoe give the reason
in the last weeks, I read the source code about how to add the view to the window.
when we call setContentView set view, it actually calls the window.setContentView and in the end, ActivityThread.handleResumeActivity will call the activity onResume method, the view shows. we look at handleResumeActivity method. it will call activity makeVisible method.
...
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
...
// the makeVieible will call wm.addView method
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
// this metod in the last called WindowGolbal.addView;then in the inner method;
// it will call ViewRootImpl.setView, in this method, it calls requestLayout
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
}
#Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
so we find here it reminds us we can not update the view. the system does that make the UI show smooth and updates view simple. if many threads can update it. it is a bad thing. because It has to handle the concurrency problem
This post says (in reference to the Android Doc) that any method on a view has to be called from the UI thread. However, I have not ran into any problem yet, though I set the OnClickListeners of Buttons in a non-UI-thread. Is this a situation of "You realy should not do this, even though you can." or is there a subset of methods that can actually be called from non-UI-threads?
If the latter is true, which operations are part of the subset?
EDIT
Example code:
Thread setUpActivity = new Thread(new Runnable() {
#Override
public void run() {
while (serviceConnection.getAppController() == null){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
btAddTag.setOnClickListener(onAddTag);
btGo.setOnClickListener(onGo);
runOnUiThread(new Runnable() {
#Override
public void run() {
setUpSpinner();
}
});
}
});
setUpActivity.start();
A quick answer would be:
You shouldn't do write operations on UI components from outside of the UI thread. This is because the UI components are not thread safe. And even if you might get away with a minor change on a device or emulator, you might get in trouble on other devices or in different situations.
Write operations would be:
set text, sizes, colors, etc.
I guess setting just a click listener won't get you into problems if you are not doing UI updates in the callback method(onClick..). But as a good practice I would advice not to do that(set the click listener on a non UI thread).
You can set listeners on non UI thread. Even if you really do not want it on non UI thread but on UI thread try using post method on view which will call on UI thread.
Usage :
view.post(new Runnable() {
public void run() {
// your action here on UI thread.
}
});
Here is the basic life cycle of my application. It targets SDK version 8 by now, since I am still running Android 2.3.3 on my device.
The application starts, onResume() is called
The method show() is called to display cached data.
A background service gets started which downloads and stores data. It uses AsyncTask instances to accomplish its work.
One of the tasks stores downloaded data in a SQLite database.
A broadcast intent is sent in onPostExecute() when the storing task has finished.
The MapActivity receives the intent and handles it.
The method show() is called to display cached and new data.
Within the method show() the map view gets invalidated after the overlay has been added. This works fine when show() has been called from the MapActivity itself. It raises an exception, however, when the asynchonous task is the source of the method call (indirectly).
As far as I understand, I am at the UI thread when I trigger show() in both cases. Is this true?
public class CustomMapActivity extends MapChangeActivity {
private boolean showIsActive = false;
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(IntentActions.FINISHED_STORING)) {
onFinishedStoring(intent);
}
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerReceiver(mReceiver, new IntentFilter(IntentActions.FINISHED_STORING));
}
#Override
protected void onResume() {
super.onResume();
show();
}
#Override
protected void onMapZoomPan() {
loadData();
show();
}
#Override
protected void onMapPan() {
loadData();
show();
}
#Override
protected void onMapZoom() {
loadData();
show();
}
private void onFinishedStoring(Intent intent) {
Bundle extras = intent.getExtras();
if (extras != null) {
boolean success = extras.getBoolean(BundleKeys.STORING_STATE);
if (success) {
show();
}
}
private void loadData() {
// Downloads data in a AsyncTask
// Stores data in AsyncTask
}
private void show() {
if (showIsActive) {
return;
}
showIsActive = true;
Uri uri = UriHelper.getUri();
if (uri == null) {
showIsActive = false;
return;
}
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
List<Overlay> mapOverlays = mapView.getOverlays();
CustomItemizedOverlay overlay = ItemizedOverlayFactory.getCustomizedOverlay(this, cursor);
if (overlay != null) {
mapOverlays.clear();
mapOverlays.add(overlay);
}
}
cursor.close();
mapView.invalidate(); // throws CalledFromWrongThreadException
showIsActive = false;
}
}
Here is the stack trace ...
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRoot.checkThread(ViewRoot.java:3020)
at android.view.ViewRoot.invalidateChild(ViewRoot.java:647)
at android.view.ViewRoot.invalidateChildInParent(ViewRoot.java:673)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:2511)
at android.view.View.invalidate(View.java:5332)
at info.metadude.trees.activities.CustomMapActivity.showTrees(CustomMapActivity.java:278)
at info.metadude.trees.activities.CustomMapActivity.onMapPan(CustomMapActivity.java:126)
at info.metadude.trees.activities.MapChangeActivity$MapViewChangeListener.onChange(MapChangeActivity.java:50)
at com.bricolsoftconsulting.mapchange.MyMapView$1.run(MyMapView.java:131)
at java.util.Timer$TimerImpl.run(Timer.java:284)
Note: I use the MapChange project in order to receive notifications on map events.
EDIT:
From what I now read in the documentation about AsyncTask (scroll down a bit), I am not sure if I use it the correct way. As previously mentioned I start AsyncTask instances from within a Service class. In contrary, the documentation states ...
AsyncTask allows you to perform asynchronous work on your user interface. It performs the blocking operations in a worker thread and then publishes the results on the UI thread, without requiring you to handle threads and/or handlers yourself.
... which sounds as if AsyncTask should only be used within an Activity not within a Service?!
The reason for your crash is because of the way that the MapChange library you are using is implemented. Under the hood, this library uses Timer and TimerTask implementations to delay firing the change event and reduce the number of calls your application gets to onMapChanged(). However, you can see from the docs on Timer that it runs its tasks in created threads:
Each timer has one thread on which tasks are executed sequentially. When this thread is busy running a task, runnable tasks may be subject to delays.
Since the MapChange library does nothing to ensure that callbacks are posted to your application on the main thread (a serious bug IMO, especially on Android), you have to protect the code you call as a result of this listener. You can see this in the example MyMapActivity bundled with the library, everything from that callback gets funneled through a Handler which posts the calls back to the main thread for you.
In your application, the code inside onMapPan() and subsequently showTrees() is being called on a background thread so it is not safe to manipulate the UI there. Using either a Handler or runOnUiThread() from your Activity will guarantee your code is called in the right place.
With regards to your second questions about AsyncTask, there is nothing stopping you from using it inside of any application component, not just Activity. Even though it's a "background" component, by default a Service is still running on the main thread as well, so AsyncTask is still necessary to offload long-term processing to another thread temporarily.
If it's getting called on the wrong thread, then it's likely not on the UI thread. Have you tried this:
runOnUiThread(new Runnable() {
public void run() {
mapView.invalidate();
}});
I have an AsyncTask that fetches some data and then updates the UI with this new data. It has been working fine for months, but I recently added a feature that displays a notification when there is new data. Now when my app is launched through the notification, sometimes I get this exception and onPostExecute is not called.
This is what happens when the app is launched:
1) Expand the UI and find views
2) Cancel the alarm (through AlarmManager) that checks for new data and reset the alarm. (This is so that if the user disables the alarm it is cancelled before the next time he/she reboots.)
3) Start the AsyncTask. If the app was launched from the notification, pass in a little bit of the data and then cancel the notification.
I'm stuck on what could be causing this exception. It seems that the exception is from the AsyncTask code, so I'm not sure how I can fix it.
Thanks!
Here is the exception:
I/My App( 501): doInBackground exiting
W/MessageQueue( 501): Handler{442ba140} sending message to a Handler on a dead thread
W/MessageQueue( 501): java.lang.RuntimeException: Handler{442ba140} sending message to a Handler on a dead thread
W/MessageQueue( 501): at android.os.MessageQueue.enqueueMessage(MessageQueue.java:179)
W/MessageQueue( 501): at android.os.Handler.sendMessageAtTime(Handler.java:457)
W/MessageQueue( 501): at android.os.Handler.sendMessageDelayed(Handler.java:430)
W/MessageQueue( 501): at android.os.Handler.sendMessage(Handler.java:367)
W/MessageQueue( 501): at android.os.Message.sendToTarget(Message.java:348)
W/MessageQueue( 501): at android.os.AsyncTask$3.done(AsyncTask.java:214)
W/MessageQueue( 501): at java.util.concurrent.FutureTask$Sync.innerSet(FutureTask.java:252)
W/MessageQueue( 501): at java.util.concurrent.FutureTask.set(FutureTask.java:112)
W/MessageQueue( 501): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:310)
W/MessageQueue( 501): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
W/MessageQueue( 501): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
W/MessageQueue( 501): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
W/MessageQueue( 501): at java.lang.Thread.run(Thread.java:1096)
EDIT: Here is my onCreate method in my main activity (the one opened by the notification). There are some onClickListeners that I omitted to save space. I don't think they should have any effect, since the buttons they are attached to are not being pressed.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Call the parent
setContentView(R.layout.main); // Create the UI from the XML file
// Find the UI elements
controls = (SlidingDrawer) findViewById(R.id.drawer); // Contains the
// buttons
// comic = (ImageView) findViewById(R.id.comic); // Displays the comic
subtitle = (TextView) findViewById(R.id.subtitleTxt); // Textbox for the
// subtitle
prevBtn = (Button) findViewById(R.id.prevBtn); // The previous button
nextBtn = (Button) findViewById(R.id.nextBtn); // The next button
randomBtn = (Button) findViewById(R.id.randomBtn); // The random button
fetchBtn = (Button) findViewById(R.id.comicFetchBtn); // The go to specific id button
mostRecentBtn = (Button) findViewById(R.id.mostRecentBtn); // The button to go to the most recent comic
comicNumberEdtTxt = (EditText) findViewById(R.id.comicNumberEdtTxt); // The text box to Zooming image view setup
zoomControl = new DynamicZoomControl();
zoomListener = new LongPressZoomListener(this);
zoomListener.setZoomControl(zoomControl);
zoomComic = (ImageZoomView) findViewById(R.id.zoomComic);
zoomComic.setZoomState(zoomControl.getZoomState());
zoomComic.setImage(BitmapFactory.decodeResource(getResources(), R.drawable.defaultlogo));
zoomComic.setOnTouchListener(zoomListener);
zoomControl.setAspectQuotient(zoomComic.getAspectQuotient());
resetZoomState();
// enter the new id
imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); // Used to hide the soft keyboard
Log.i(LOG_TAG, "beginning loading of first comic");
int notificationComicNumber = getIntent().getIntExtra("comic", -1);
Log.i(LOG_TAG, "comic number from intent: " + notificationComicNumber);
if (notificationComicNumber == -1) {
fetch = new MyFetcher(this, zoomComic, subtitle, controls, comicNumberEdtTxt, imm, zoomControl);
fetch.execute(MyFetcher.LAST_DISPLAYED_COMIC);
} else {
fetch = new MyFetcher(this, zoomComic, subtitle, controls, comicNumberEdtTxt, imm, zoomControl);
fetch.execute(notificationComicNumber);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).cancelAll();
}
Log.i(LOG_TAG, "ending loading of new comic");
Log.i(LOG_TAG, "first run checks beginning");
// Get SharedPreferences
prefs = getSharedPreferences("prefs", Context.MODE_PRIVATE);
// Check if this is the first run of the app for this version
if (prefs.getBoolean("firstRun-" + MAJOR_VERSION_NUMBER, true)) {
prefs.edit().putBoolean("firstRun-" + MAJOR_VERSION_NUMBER, false).commit();
firstRunVersionDialog();
}
// Check if this is the first run of the app
if (prefs.getBoolean("firstRun", true)) {
prefs.edit().putBoolean("firstRun", false).commit();
firstRunDialog();
}
Log.i(LOG_TAG, "First run checks done");
// OnClickListener s for the buttons omitted to save space
EDIT 2: I've been digging through Android source code tracking down where the exception is coming from. This is lines 456 and 457 of sendMessageAtTime in Handler:
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
And this is enqueueMessage from MessageQueue:
final boolean enqueueMessage(Message msg, long when) {
if (msg.when != 0) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
this.notify();
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
this.notify();
}
}
return true;
}
I'm a little confused about what mQuiting is, but it looks like the previous time enqueueMessage was called msg.target was null.
This is due to a bug in AsyncTask in the Android framework. AsyncTask.java has the following code:
private static final InternalHandler sHandler = new InternalHandler();
It expects this to be initialized on the main thread, but that is not guaranteed since it will be initialized on whichever thread happens to cause the class to run its static initializers. I reproduced this issue where the Handler references a worker thread.
A common pattern that causes this to happen is using the class IntentService. The C2DM sample code does this.
A simple workaround is to add the following code to the application's onCreate method:
Class.forName("android.os.AsyncTask");
This will force AsyncTask to be initialized in the main thread. I filed a bug on this in the android bug database. See http://code.google.com/p/android/issues/detail?id=20915.
To generalize Jonathan Perlow's solution to the bug he identified specifically, I use the following in any class that uses AsyncTask. The looper/handler/post is how you can run something on the UI thread anywhere in an Android app without passing down a handle to an activity or other context. Add this static initialization block inside the class:
{ // https://stackoverflow.com/questions/4280330/onpostexecute-not-being-called-in-asynctask-handler-runtime-exception
Looper looper = Looper.getMainLooper();
Handler handler = new Handler(looper);
handler.post(new Runnable() {
public void run() {
try {
Class.forName("android.os.AsyncTask");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
});
}
We had run into the problem when trying to get unit tests to run. I found a workaround for that, but hadn't specifically identified the problem. We only knew that trying to use AsyncTask<> in Android JUnit test caused onPostExecute() not to be called. Now we know why.
This post shows how to run multithreaded async code in an Android JUnit test:
Using CountDownLatch in Android AsyncTask-based JUnit tests
For use with non-UI unit tests, I created a simple subclass of android.test.InstrumentationTestCase. It has an "ok" flag and a CountDownLatch. reset() or reset(count) creates a new CountDownLatch({1,count}). good() sets ok=true, count--, and calls.countDown() on the latch. bad() sets ok=false, and counts down all the way. waitForIt(seconds) waits for timeout or the coundown latch to zero. Then it calls assertTrue(ok).
Then tests are like:
someTest() {
reset();
asyncCall(args, new someListener() {
public void success(args) { good(); }
public void fail(args) { bad(); }
});
waitForIt();
}
Because of the AsyncTask static initialization bug, we had to run our actual tests inside a Runnable passed to runTestOnUiThread(). With proper static initialization as above, this shouldn't be necessary, unless the call being tested needs to run on the UI thread.
The other idiom I now use is to test whether the current thread is the UI thread and then run the requested action on the proper thread regardless. Sometimes, it makes sense to allow the caller to request sync vs. async, overriding when necessary. For instance, network requests should always be run on a background thread. In most cases, AsyncTask thread pooling is perfect for this. Just realize that only a certain number will run at once, blocking additional requests. To test whether the current thread is the UI thread:
boolean onUiThread = Looper.getMainLooper().getThread() == Thread.currentThread();
Then use a simple subclass (just doInBackground() and onPostExecute() are needed) of AsyncTask<> to run on a non-UI thread or handler.post() or postDelayed() to run on the UI thread.
Giving the caller the option to run sync or async looks like (getting a locally valid onUiThread value not shown here; add local booleans as above):
void method(final args, sync, listener, callbakOnUi) {
Runnable run = new Runnable() { public void run() {
// method's code... using args or class members.
if (listener != null) listener(results);
// Or, if the calling code expects listener to run on the UI thread:
if (callbackOnUi && !onUiThread)
handler.post(new Runnable() { public void run() {listener()}});
else listener();
};
if (sync) run.run(); else new MyAsync().execute(run);
// Or for networking code:
if (sync && !onUiThread) run.run(); else new MyAsync().execute(run);
// Or, for something that has to be run on the UI thread:
if (sync && onUiThread) run.run() else handler.post(run);
}
Also, using AsyncTask can be made very simple and concise. Use the definition of RunAsyncTask.java below, then write code like this:
RunAsyncTask rat = new RunAsyncTask("");
rat.execute(new Runnable() { public void run() {
doSomethingInBackground();
post(new Runnable() { public void run() { somethingOnUIThread(); }});
postDelayed(new Runnable() { public void run() { somethingOnUIThreadInABit(); }}, 100);
}});
Or simply:new RunAsyncTask("").execute(new Runnable(){public void run(){ doSomethingInBackground(); }});
RunAsyncTask.java:
package st.sdw;
import android.os.AsyncTask;
import android.util.Log;
import android.os.Debug;
public class RunAsyncTask extends AsyncTask<Runnable, String, Long> {
String TAG = "RunAsyncTask";
Object context = null;
boolean isDebug = false;
public RunAsyncTask(Object context, String tag, boolean debug) {
this.context = context;
TAG = tag;
isDebug = debug;
}
protected Long doInBackground(Runnable... runs) {
Long result = 0L;
long start = System.currentTimeMillis();
for (Runnable run : runs) {
run.run();
}
return System.currentTimeMillis() - start;
}
protected void onProgressUpdate(String... values) { }
protected void onPostExecute(Long time) {
if (isDebug && time > 1) Log.d(TAG, "RunAsyncTask ran in:" + time + " ms");
v = null;
}
protected void onPreExecute() { }
/** Walk heap, reliably triggering crash on native heap corruption. Call as needed. */
public static void memoryProbe() {
System.gc();
Runtime runtime = Runtime.getRuntime();
Double allocated = new Double(Debug.getNativeHeapAllocatedSize()) / 1048576.0;
Double available = new Double(Debug.getNativeHeapSize()) / 1048576.0;
Double free = new Double(Debug.getNativeHeapFreeSize()) / 1048576.0;
long maxMemory = runtime.maxMemory();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
}
}
I had the same problem on a device with Android 4.0.4 with the IntentService and solved it as sdw said with the Class.forName("android.os.AsyncTask"). The same didn't happen on Android 4.1.2, 4.4.4 or 5.0. I wonder if this Google resolved Martin West issue from 2011.
I added this code on my Application onCreate and it worked:
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
try {
Class.forName("android.os.AsyncTask");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
It would be nice to know if the version of Android need to be changed to something else.
AsyncTask.execute() must be executed on UI thread, i.e. inside Activity.
I have the same problem, it seems to happen when the AsyncTask is running during a suspend/resume.
EDIT:
Yeah, didnt think I had but I used this http://developer.android.com/guide/appendix/faq/commontasks.html#threading
to always start the AsyncTask on the UI thread and the problem has gone.
The problem appeared after I added the licensing function, siggghhhhh
Thanks
Even though this doesn't directly answer the OP's question, I think it will be useful for people searching for the solution of the same problem when running tests.
Overall, Peter Knego's answer sums it up well.
My problem was specifically with running a test on a class outside an Activity that made use of Android's AsyncTask for an API call. The class works in the application, since it is used by an Activity, but I wanted to run a test making an actual API call from the test.
While Jonathan Perlow's answer worked, I didn't like introducing changes to my application due solely to a test.
So, in the case of a test runTestOnUiThread can be used (#UiThreadTest cannot be used, since you cannot wait for a result in a test that uses that annotation).
public void testAPICall() throws Throwable {
this.runTestOnUiThread(new Runnable() {
public void run() {
underTest.thisMethodWillMakeUseOfAnAsyncTaskSomehow();
}
});
// Wait for result here *
// Asserts here
}
Sometimes though, especially in functional tests, Jonathan Perlow's answer seems to be the only one that works.
* Take a look here to see how to pause a test waiting for a result.
An application I am currently developing is communicating with the server and the communication process runs in its own thread. There are asynchronous calls - for example login() and onLoginResponse().
login() is called in the main activity and the response is handled in main activity as well (onLoginResponse()). In onLoginResponse() method there is updateGUIState() method which modifies layout elements:
private void updateGUIState() {
Log.i(TAG, "executing updateGUIState");
arrangeLayoutElements();
txtTime.setText(mStrRecordingTime);
if (settings.isRecording()) {
//btnAction.setText("Stop");
btnAction.setImageResource(R.drawable.button_stop);
} else {
//btnAction.setText("Capture");
btnAction.setImageResource(R.drawable.button_record);
}
//set privacy level text
if (settings.getPrivacyLevel() == 0) {
txtPrivacyLevel.setText("Private");
} else if (settings.getPrivacyLevel() == 1) {
txtPrivacyLevel.setText("Public");
}
if (settings.isMute()) {
muteIcon.setIconImage(R.drawable.ic_volume_off_small);
} else {
muteIcon.setIconImage(R.drawable.ic_volume_small);
}
if (mIsUploading) {
txtUploadingText.setVisibility(View.VISIBLE);
uploadingProgressBar.setVisibility(View.VISIBLE);
} else {
txtUploadingText.setVisibility(View.INVISIBLE);
uploadingProgressBar.setVisibility(View.INVISIBLE);
}
if (mEncoderConnection != null) {
txtConnectionStatus.setText("Connected");
} else {
txtConnectionStatus.setText("Disconnected");
}
}
When the execution reaches this method (when called from onLoginResponse()) the application crashes and the log displays the following message:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Does anyone know how it is possible to modify the logic in order to switch to appropriate thread before modifying the layout and fix the problem?
Thanks!
Try Handler.
Is onLoginResponse() is a callback function?
If it is, the problem can be solved by Handler.
In onLoginResponse(),
hRefresh.sendEmptyMessage(REFRESH);
Handler hRefresh = new Handler(){
#Override
public void handleMessage(Message msg) {
switch(msg.what){
case REFRESH:
/*Refresh UI*/
updateGUIState();
break;
}
}
};
updateGUIState() needs to be run on the UI thread. A possible solution is to implement your GUI update in a Runnable, and call the runOnUiThread method with your runnable.
To add to bhatt4982's response, you can also call handler.post(onLoginThread), where onLoginThread is a Thread whose runnable will run inside the GUI thread.