Show multiple TextView changes during OnClick - android

I am currently working on an app which uses Bluetooth, GPS and uploads data to a remote server. I have a simple button which launches a series of events and threads in order to let everything work together.
I am now adding TextView components on the screen, which show the user a more detailed process of what is happening. Is the GPS running? Is my Bluetooth device connected? Etc. This process can take up to 10 seconds, this is why I am adding some more information on what is happening on the background.
However, when I click my button, only the last change will be visible. I suppose the TextView components are rendered AFTER the Onclick?
An example:
#Override
public void onClick(View view) {
textView.setText(R.string.launching_text);
// start a thread
textView.setText(R.string.start_new_thread);
// start another thread
textView.setText(R.string.almost_there);
// start last thread
textView.setText(R.string.done);
}
Imagine this process taking about 10 seconds. It will look like the app "freezes", but the changes are not visible till after the OnClick finishes.
How can I show my information realtime, during the OnClick event? Is there perhaps a better practice? Is it possible to do some sort of way to asynchronously push TextView changes?

I’d suggest you first check this Android Performance Patterns video, to see some of the options at your disposal. I’d also advise to not perform multithreading in a lifecycle environment (e.g. Activities, Fragments) as this is just asking for trouble.
In your onClick example, R.string.done could (and most likely will) be displayed before the first thread has done its work. I’m assuming that’s not really what you want.
I have no knowledge of the problem you’re tackling, tools you’re using or the architecture you’re following, so here’s one slightly generic way to make it work. Each Thread in your onClick implementation comes with a status of sorts. You could represent this in code with a simple abstraction:
class Holder {
#StringRes int status;
Runnable runnable;
Holder(#StringRes int status, #NonNull Runnable runnable) {
this.status = status;
this.runnable = runnable;
}
}
Notice Runnable is used instead of Thread.
You’re also executing things in sequence. You could represent this in code with a simple List or a Queue, providing a fluid, expressive API, for example:
class StatusRunnableBuilder {
private final WeakReference<TextView> viewRef;
private final Queue<Holder> queue;
#StringRes private int finalStatus;
StatusRunnableBuilder(#NonNull TextView view) {
viewRef = new WeakReference<>(view);
queue = new ArrayDeque<>();
}
StatusRunnableBuilder addStep(#StringRes int status,
#NonNull Runnable runnable) {
queue.add(new Holder(status, runnable));
return this;
}
StatusRunnableBuilder withFinalStatus(#StringRes int status) {
finalStatus = status;
return this;
}
Runnable build() {
return new Runnable() {
#Override
public void run() {
for (Holder item: queue) {
updateStatus(item.status);
item.runnable.run();
}
if (finalStatus != 0) {
updateStatus(finalStatus);
}
}
};
}
private void updateStatus(#StringRes final int status) {
final TextView view = viewRef.get();
if (view != null) {
view.post(new Runnable() {
#Override
public void run() {
// As this has been posted to a queue,
// it could have been processed with some delay,
// so there is no guarantee the view is still present.
// Let's check again.
final TextView v = viewRef.get();
if (v != null) {
v.setText(status);
}
}
});
}
}
}
Then your onClick becomes something like:
#Override
public void onClick(View v) {
final Runnable runnable = new StatusRunnableBuilder(view)
.addStep(R.string.launching_text, launchingRunnable)
.addStep(R.string.almost_done, almostDoneRunnable)
.withFinalStatus(R.string.finally_done)
.build();
service.execute(runnable);
}
where service is an ExecutorService which allows you to create/shutdown on any lifecycle event, e.g.:
#Override
protected void onStart() {
super.onStart();
service = Executors.newSingleThreadExecutor();
}
#Override
protected void onStop() {
super.onStop();
service.shutdownNow();
}

You can use a Runnable with Handler. Handler will post updates to Runnable after certain intervals.
For Bluetooth connectivity you can go for Broadcast receivers as well.

You can try using an AsyncTask. It's pretty simple and it handles all the background threading as well as inter-thread communication for you. There are several considerations with it, to avoid memory leaks you can use EventBus or similar mechanics. Here's an article I found very useful:
http://simonvt.net/2014/04/17/asynctask-is-bad-and-you-should-feel-bad/

Related

runOnUiThread() no executing when using Thread.sleep()

I'm using code that looks like this :
_thread = new Thread(){
#Override
public void run() {
try {
while (true) {
operate();
Thread.sleep(DELAY);
}
} catch (InterruptedException e) {
// Doesn't matters...
}
}
};
operate function looks like this :
// does things....
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
// adds an ImageView to the screen
}
});
// does other things...
At the bottom line, what i wanted to achieve is an operation that happens once in a while, without interrupting the main thread and the UI, something like a game-loop.
In the first 2 times that operate() runs, it adds the ImageView and everything is alright, but after 2 or 3 times it stops adding the ImageViews, but the UI is still running as usual. When i debugged the problem, i found out that after 3 times the run() method of the Runnable isn't called anymore, even thought the operate function was called.
The wired thing (for me) was that when i removed the Thread.sleep, everything worked fine (much faster of course...). I tried to replace it with a very long for loop (just for checking) and it worked, but of course it is not an appropriate solution to the problem.
I read about the problem, most of the people that asked this question did a thread.sleep or an infinite loop on the main thread, but, as i see it, i didn't do such thing. Many people wrote that you should replace the Thread.sleep with Handler.postDelayed. I tried to do it but it didn't work, maybe I did it wrong. I even tried replacing the runOnUiThread with other options I found on the internet, but all of them gave me the same exact results. I tried to replace the method that I'm adding the view to the activity, but all of them, again, gave the same result.
The waiting is crucial for this application. I got to find a way to wait sometime and then execute a function on the UI thread, cause this pattern returns at least a couple of times in my application.
It sounds like you want a post delay so that you can do the code on the UI thread after some delay. Handler Post Delay.
private static final int DELAY = 500;
private Handler mHandler;
private Runnable mRunnable;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start();
}
private void start()
{
mHandler = new Handler();
mRunnable = new MyRunnable(this);
mHandler.postDelayed(mRunnable, DELAY);
}
private void stop()
{
mHandler.removeCallbacks(mRunnable);
}
private void doSomething()
{
// Do your stuff here.
// Reschedule.
mHandler.postDelayed(mRunnable, DELAY);
}
Recommended way of creating a Runnable.
private static class MyRunnable implements Runnable
{
private WeakReference<MainActivity> mRef;
// In here you can pass any object that you need.
MyRunnable(MainActivity activity)
{
mRef = new WeakReference<MainActivity>(activity);
}
#Override
public void run()
{
// Safety check to avoid leaking.
MainActivity activity = mRef.get();
if(activity == null)
{
return;
}
// Do something here.
activity.doSomething();
}
}
There could be several reasons why the UI Runnable isn't being executed. Probably the activity variable has something messed up with it or it's referencing the context incorrectly, or as you said the Thread.sleep() could be causing an issue. At this point more parts of the code needs to viewed to better solve the problem.
A better way of implementing your logic is to use a scheduled Timer instead of using an infinite loop with a Thread.sleep() in it. It will execute the code within a background thread. And then use a Handler to update the UI instead of activity.runOnUiThread(). Here's an example:
// Global variable within the activity
private Handler handler;
// Activity's onCreate()
#Override
protected void onCreate(Bundle savedInstanceState) {
handler = new Handler(getMainLooper());
Timer timer = new Timer("ScheduledTask");
// Timer must be started on the Main UI thread as such.
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
operate();
}
}, 0L, DELAY);
}
private void operate() {
// does things in background....
handler.post(new Runnable() {
#Override
public void run() {
// adds an ImageView to the screen from within the Main UI thread
}
});
// does other things in the background...
}

Leak : Timer and TextWatcher

I'm working on an editText and a recyclerView.
My recyclerView is updated when I write letters in my EditText.
I put a Timer inside my textWatcher in order to avoid sending requests each time user write a letter.
searchDestinationEt.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
//There is nothing to do here
}
#Override
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
if (timer != null) {
timer.cancel();
}
}
#Override
public void afterTextChanged(final Editable s) {
timer = new Timer();
//we schedule this in order to avoid sending useless request.
//We wait the user is finishing writing before sending requests
timer.schedule(new TimerTask() {
#Override
public void run() {
((Activity) context).runOnUiThread(new Runnable() {
#Override
public void run() {
actionsListener.onDestinationSearch(s.toString());
}
});
}
}, DELAY_SEND_REQUEST);
}
});
It works well but leakcanary says that I have a leak in this part of code.
Any idea ?
Sorry for being late with the response, but did you try separating textwatcher like this ?:
TextWatcher for more than one EditText
Why do you use Timer and TimerTask for delayed, not recurring action? The easiest and the common way is to use just a regular Handler with postDelayed():
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//do somthing here
}
}, DELAY_SEND_REQUEST);
The leak occurs because you're starting a thread which has a reference to your context (fragment or activity). And until your thread is done - it won't be garbage collected.
That means, for example, if a user types something and you're waiting for a time to start requesting and meanwhile the user turns the phone and orientation change occurs - your activity/fragment will be recreated - but the old one (which started a thread and should be used when thread is done) is not gone and still present in memory.
Why are doing a request on the UI Thread? It blocks the UI, you know that right? I assume an AsyncTask may fit better.
What should you do?
Replace Timer with Handler and do the requests in a worker thread. Regarding the leak you have 2 options:
a) do nothing since the time for which your activity/fragment will be preserved is very small and it will be GCed after the request is done. (not recommended)
b) Utilize the AsyncTask and in the constructor of the AsyncTask pass the context (your listener) and store it as a weak reference object, like this:
private static class SomeWorkTask extends AsyncTask<Void,Void,Void>{
private WeakReference<ActionsListenerWithContext> weakListener;
public SomeWorkTask(ActionsListenerWithContext listener){
this.weakListener = new WeakReference<>(listener);
}
#Override
protected Void doInBackground(Void... voids) {
//do some work here
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
if(weakListener.get() != null){
weakListener.get().callYourCallbacks();
}
}
}
and then you call it
new SomeWorkTask(listener).execute();
Utilizing WeakReference wrapper is a common and recommended practice.

Communication between Android Services and Activities

I want to develop an Android App with three activities and two services.
The first Service, named WebClientService, calls a REST API every 30 seconds, using an Handler, and has to notify the active Activity with the result.
It also has to notify a second Service, named DatabaseService, in order to update a local DB.
The Database Service will be called just once onCreate of the activity (in case of app crash and restart) and just once at onRestart (in this way we have data to show in case there were connectivity issues). The activities will then keep themselves updated thanks to the WebClientService that notifies the "alive" activity every 30 seconds.
Questions are:
What's the best way to notify for an update both the active activity and the background DatabaseService?
My idea is to use sendBroadcast() within WebClientService and a BroadcastReceiver in every activity and within the DatabaseService, is it the right approach?
Should I use the same approach for the communication between AllMeetingRoomActivity and DatabaseService or should I use a Bound Service?
Thanks
UPDATE:
DatabaseService won't be a background service anymore but just a shared instance of the db layer between WebClientService and the activities.
So question now is: is it a good approach to just write my 30 seconds updates to the local db and allow the activities to update themselves every few seconds simply reading from the local db?
Would that affect the performance too much?
Context:
Follows what I've implemented so far but using SettableFutures and thus needs to be re-implemented using Services and Broadcasts once I've clear how to make them communicate effectively:
public class MainActivity extends AppCompatActivity {
private TextView meetingsTextView;
private EditText mEdit, editSubject;
private final ConnectorInitializer clientInitializer = new ConnectorInitializer();
private AppConnector genericClient; // can use OutlookClient or a test client to talk with a mock server
#Override
protected void onCreate(Bundle savedInstanceState) {
// initializes client based on the settings in "config.json"
genericClient = clientInitializer.create(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
meetingsTextView = (TextView) findViewById(R.id.NowMeeting);
mEdit = (EditText)findViewById(R.id.editText);
editSubject = (EditText)findViewById(R.id.editSubject);
Futures.addCallback(genericClient.logon(this, scopes), new FutureCallback<Boolean>() {
#Override
public void onSuccess(Boolean result) {
Log.d("APP", "-- Logged in. --");
databaseConnector.synchronouslyGetBackupFromLocalDatabase() // FUTURE
// callback here
// onSuccess, onFailure
}
#Override
public void onFailure(#NonNull Throwable t) {
Log.e("\n ~~~~>> logon \n", t.getMessage());
meetingsTextView.setText(R.string.Login_Failed);
}
});
}
/** At the moment the UI is not updated automatically every 30 seconds
* but manually using a refresh button
*/
public void getBookings(#SuppressWarnings("UnusedParameters") View view){
Log.d("APP", "Retrieve button clicked: "+(DateTime.now())+". Calling async getCalendar.");
meetingsTextView.setText(R.string.retrieving_events);
try{
Futures.addCallback( genericClient.getCalendarEvents(), new FutureCallback<String>(){
#Override
public void onSuccess(final String resultCalendars) {
Log.d("APP", "Success. Result: "+resultCalendars);
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.d("APP", "Calendars SUCCESSFULLY retrieved.");
String meetingsRetrieved = getString(R.string.calendar)+resultCalendars;
meetingsTextView.setText(meetingsRetrieved);
Toast.makeText(getApplicationContext(), "Success!", Toast.LENGTH_LONG).show();
}
});
databaseConnector.asyncUpdateLocalDbWithResults(); // FUTURE
// callback here
// onSuccess, onFailure
}
#Override
public void onFailure(#NonNull Throwable t) {
Log.e( "APP", "Calendar error. Cause: "+t.getLocalizedMessage() );
String retrieveError = "Retrieve error. \n\n\n"+t.getLocalizedMessage();
meetingsTextView.setText(retrieveError);
Toast.makeText(getApplicationContext(), "Fail!", Toast.LENGTH_LONG).show();
}
});
}catch(Exception ex){
Log.e("APP","Something went wrong in your code. Cause:"+ex);
}
}
Best option ever:
Use LocalBroadcastManager. More reference here.
MyService.java:
private LocalBroadcastManager localBroadcastManager;
private final String SERVICE_RESULT = "com.service.result";
private final String SERVICE_MESSAGE = "com.service.message";
#Override
public void onCreate() {
super.onCreate();
// Other stuff
localBroadcastManager = LocalBroadcastManager.getInstance(this);
}
Add below method in service, whenever you want to update data from service to Activity, call method by passing Arguments.
private void sendResult(String message) {
Intent intent = new Intent(SERVICE_RESULT);
if(message != null)
intent.putExtra(SERVICE_MESSAGE, message);
localBroadcastManager.sendBroadcast(intent);
}
HomeActivity.java:
private BroadcastReceiver broadcastReceiver;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_home);
broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String s = intent.getStringExtra(MyService.SERVICE_MESSAGE);
// do something here.
}
};
}
#Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver((broadcastReceiver),
new IntentFilter(MyService.SERVICE_RESULT));
}
#Override
protected void onStop() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
super.onStop();
}
Hope this will help you.
I think your approach is ok with BroadCastReceiver. However, BroadCastReceiver should be used for a global purpose (like communicating between 2 applications). If you intend to use BroadCastReceiver for your app only, I prefer using LocalBroadcastManager instead. Using LocalBroadcastManager is faster and more security when it can be caught only by your app.
There's another way to communicate between your activitys and your services is using EventBus. It will be much easier than using BroadCastReceiver (especially in passing data between them).
Update: About your update question:
is it a good approach to just write my 30 seconds updates to the local db and allow the activities to update themselves every few seconds simply reading from the local db? --> Of course NO. You should let your activities update themselves when they need. When you update your local db, you should know that is there any changes or not. If there is any change, use LocalBroadcastmanager to notify your activity to update.
Would that affect the performance too much? --> Yes, that do. The db connection will take time to execute and it will block your UI in some cases. in that case, you should use a thread with ExecutorService for each execute (insert, update...). One more thing to consider is updating that frequently will drain your phone battery very, very fast.
You can bind the services to the activities and update your UI.
Or you can use libraries like Otto or EventBus to create a publisher/subscriber dependency and notify your activities everytime your services publish an update of information.
Use event bus for this communication. EventBus allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another (and thus be aware of each other). It is designed exclusively to replace traditional Java in-process event distribution using explicit registration.
There are a lot of them:
http://square.github.io/otto/
https://github.com/greenrobot/EventBus
This is an example of Otto usage:
Bus bus = new Bus();
bus.post(new AnswerAvailableEvent(42));
#Subscribe public void answerAvailable(AnswerAvailableEvent event) {
// TODO: React to the event somehow!
}
bus.register(this); // In order to receive events, a class instance needs to register with the bus.
To post from any thread (main or background), in you case a Service and receive events on the main thread:
public class MainThreadBus extends Bus {
private final Handler mHandler = new Handler(Looper.getMainLooper());
#Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
super.post(event);
} else {
mHandler.post(new Runnable() {
#Override
public void run() {
MainThreadBus.super.post(event);
}
});
}
}

How to return to main thread Android

I have a simple Activity with two buttons "On" and "Off". I want start changing color of background in cycle with button "On" and stop this with button "Off". Also I need to have red color by click on "Off" button. I have wrote simple programm and everything is fine, but I can't understand one thing. Why the last color not always red? If I use code in main threads cycle
Thread.sleep(100);
or
Thread.sleep(1000);
I always have red color, but if I set
Thread.sleep(10);
I have random last color. Why??
Thank you !!
I have this code:
public class MyActivity extends Activity {
final Handler myHandler = new Handler();
private int randColor;
final Runnable updateColor = new Runnable() {
public void run() {
final Random random = new Random();
randColor = Color.rgb(random.nextInt (255), random.nextInt (255), random.nextInt (255));
mRelativeLayout.setBackgroundColor(randColor);
}
};
private ColorChanger myThread;
class ColorChanger extends Thread {
private volatile boolean mIsStopped = false;
#Override
public void run() {
super.run();
do
{
if (!Thread.interrupted()) {
myHandler.post(updateColor);
}
else
{
return;
}
try{
Thread.sleep(100);
}catch(InterruptedException e){
return;
}
}
while(true);
}
public void stopThis() {
this.interrupt();
}
}
private RelativeLayout mRelativeLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
mRelativeLayout = (RelativeLayout)findViewById(R.id.relativeLayout);
}
public void onflagClick(View view) {
myThread = new ColorChanger();
myThread.start();
}
public void onflagoffClick(View view) throws InterruptedException {
myThread.interrupt();
if(myThread.isAlive())
{
try {
myThread.join();
} catch(InterruptedException e){
}
}
else
{
mRelativeLayout.setBackgroundColor(getResources().getColor(R.color.redColor));
}
mRelativeLayout.setBackgroundColor(getResources().getColor(R.color.redColor));
}
}
I agree with the previous answer-ers, but propose a different solution.
First let me say that I recommend you stop using Runnables. In general posting a Runnable to a Handler is less efficient then sending a Message, although there are very rare exceptions to this rule.
Now, if we send Messages, what should we do? What we basically want to do is keep doing whatever we're doing until a condition is hit. A great way to do this is to write a Message Handler that receives a Message, does our work (setting the color), checks if we should keep going, and if so schedules a new Message in the future to do more work. Let's see how we might do this.
Assume the code below is inside an Activity.
private static final int MSG_UPDATE_COLOR = 1;
private static final int DELAY = 10; //10 millis
private final Object mLock = new Object();
private boolean mContinue = true;
Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_COLOR:
synchronized (mLock) {
if (mContinue) {
setColor(Color.rgb(random.nextInt (255), random.nextInt (255), random.nextInt (255)));
mHandler.sendEmptyMessageDelayed(MSG_UPDATE_COLOR, DELAY);
} else {
setColor(Color.RED);
}
}
break;
}
}
}
}
public void onflagClick(View view) {
mHandler.sendEmptyMessage(MSG_UPDATE_COLOR);
}
public void onflagoffClick(View view) throws InterruptedException {
synchronized (mLock) {
mContinue = false;
}
// cancel any pending update
mHandler.removeMessages(MSG_UPDATE_COLOR);
// schedule an immediate update
mHandler.sendEmptyMessage(MSG_UPDATE_COLOR);
}
Okay, so, what is happening here. We've created a Handler that will do all the color updates. We kick that off when our start event happens. Then the Message schedules a new message (and therefore color update) in ten milliseconds. When the stop event happens we reset a flag that the message handler reads to determine if a new update should be scheduled. We then unschedule all update messages because it might be scheduled for several milliseconds in the future and instead send an immediate message that does the final color update.
For bonus points we eliminate the use of a second thread which saves resources. Looking carefully I've used synchronized blocks, but these are actually unnecessary because everything is happening on the main thread. I included these just in case someone was changing mContinue from a background thread. Another great point of this strategy is that all color updates happen in one place in the code so it is easier to understand.
When you post to Handler, it will run your Runnable at some given time in the future. It is not immediate. It also works in a queue so the more times you post to Handler you are going to stack up the commands that will all get executed in order eventually.
You're facing a race condition because with Thread.sleep(10), the program is most likely stacking up a lot of Runnables to execute. They will run regardless of whether or not your Thread is running because they've been queued up to run on the main thread. Thread.sleep(100) or Thread.sleep(1000) doesn't have this issue simply because you're giving the system enough time to execute all color commands. However, it is still possible to have this issue if you pressed the off button at just the right time.
As DeeV told you, Handler sends Runnables to a Looper that is basically a Thread looping inside processing messages or runnables in each loop. You are queuing messaged to the main Looper and then you are sleeping your worker Thread. Its possible that you are sending for example 2 runnables in a row between each loop of your worker thread, but the main looper has only executed the last one so you cannot see each color as you want.
If you want a simple solution to make it work, you can use an Object or a CountDownLatch to synchronize your main Looperwith your worker Thread.
For example: Just before you will sleep your worker Thread you can do the next thing myLockObject.wait()
Then, you should change post(Runnable) to sendMessage(Message). In handleMessage from your Handler you can do myLockObject.notify() (Keep in mind that handleMessage will be executed inside the Looper that you have created your Handler or you can specify any Looper you want explicity). To obtain a new Message you should use myHandler.obtainMessage().
This will make your worker Thread wait your main Looperto process your runnable just before you wait X time until you post next color. Obviously you should create your new Object as a field of your Activity for example:
private myLockObject = new Object()

Android - multithreading issues when changing activity

I have a main menu with an action bar. On create, I run a thread that hits my server for a current status. When Complete, the thread calls a handler which kicks off a constantly running thread that cycles through the items and uses another handler call to change the test in the actionbar. The problem is that when I change views, I either get android.view.WindowLeaked or View not attached to window manager
Here is some sample code
public class MainMenuActivity extends ProtectedWithActionBarActivity{
private int STATUS_COUNTER;
private final int RESULT_STATUS_LOADED = 2000;
private final int RESULT_SHOW_STATUS = 2001;
private CurrentStatusModel currentStatus;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainmenu);
ActionBar footerbar = (ActionBar)findViewById(R.id.footerbar);
footerbar.setTitle("Currently connected to " + PreferencesHelper.getCurrentEnvironment().name());
STATUS_COUNTER = 0;
statusLoadThread.start();
}
Thread statusLoadThread = new Thread()
{
#Override
public void run()
{
//set currentStatus with data from server
}
};
Thread statusDisplayThread = new Thread()
{
int sleep = 5000;
boolean threadDone = false;
public void done()
{
threadDone = true;
}
#Override
public void run()
{
while(true)
{
//pick message to send to handler
//increment STATUS_COUNTER or reset to 0 when out of bounds
try
{
sleep(sleep);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch(msg.what)
{
case RESULT_STATUS_LOADED:
statusDisplayThread.start();
break;
case RESULT_SHOW_STATUS:
ActionBar footerbar = (ActionBar)findViewById(R.id.footerbar);
String message = ((Object[])msg.obj)[0].toString();
OnClickListener listener = (OnClickListener)((Object[])msg.obj)[1];
footerbar.setTitle(message);
footerbar.setOnTitleClickListener(listener);
break;
case ActivityBase.RESULT_ERROR:
break;
}
}
};
}
I'm not sure if what I'm doing is just wrong or if there is something blatantly obvious that I am missing. What needs to happen is the threads need to stop any time I change screens. Should I use Thread.interrupt(); before starting the next activity?
AsyncTasc allows you to implement doInBackground(), where your thread can crank away at its task. This is similar to the functionality you'd get from Thread.
The real magic happens when you override onPreExecute() and onPostExecute(), which are both executed on the UI thread. This should keep you from getting messages about your Activity not being attached.
Edit - this answer contains a small code example for AsyncTask that could get you started.
You are trying to update UI elements after the owning Activity has been detached from the windowing system.
You will make your life a lot simpler if you use AsyncTask instead of vanilla threads (no handler needed, for one thing) and cancel() the background tasks from your Activity.onPause().
Can't you set a flag in onPause that each of your Threads checks for? If the flag is set then the thread drops out of its loop. Thus whenever the Activity is moved to the background each of your Threads will stop. You would need to handle restarting the threads in onResume. You could alternatively use the AsyncTask approach, but this is not guaranteed to actually cancel when you call its cancel() method, it only attempts to cancel the task.

Categories

Resources