Updating two consecutive textviews using threads - android

I have been reading up about threads in my java book and I wanted to try making updates with two consecutive threads at the same time.
OF course though threads can't update the UI. I have used handlers a little bit but only by passing messengers from one class to another to capture location from a service to an activity.
But this must be simpler than that.
I assume I should use AsyncTask but i want to count 1-10 in each field and I don't want to keep dipping in and out of the asynctask class.
Any pointers appreciated
Stephen
UPDATE
So I did a little more experimenting. I wrote a handler method to handle any messages sent to it. It checks which field it should be updating and then performs a setText on said field. Here is the code
Handler myHandler = new Handler(){
public void handleMessage(Message msg){
msg = Message.obtain();
Bundle bundle = msg.getData();
String fieldName = bundle.getString("fieldName");
int count = bundle.getInt("count");
if(fieldName=="text1"){
text1.setText(count);
}else{
text2.setText(count);
}
}
};
I then have some code in the onCreate of my activity which launches two threads and each thread passes a messages with my int count to the handler. It also passes in a TextView name so my handler knows which textview it should be updating. Here is the code...
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
text1 = (TextView)findViewById(R.id.text1);
text2 = (TextView)findViewById(R.id.text2);
new Thread(){
public void run(){
for(int i=1;i<10;i++){
message = Message.obtain(myHandler);
Bundle bundle = new Bundle();
bundle.putCharSequence("fieldName", "text1");
message.setData(bundle);
myHandler.sendMessageDelayed(message, 1000);
}
}
}.start();
new Thread(){
public void run(){
for(int i=1;i<10;i++){
message = Message.obtain(myHandler);
Bundle bundle = new Bundle();
bundle.putCharSequence("fieldName", "text2");
message.setData(bundle);
myHandler.sendMessageDelayed(message, 1000);
}
}
}.start();
}
But I am getting an error on the setText
05-17 17:13:00.013: ERROR/AndroidRuntime(966): android.content.res.Resources$NotFoundException: String resource ID #0x0
Any ideas?

Here's what you can do. You are on the right track about using AsyncTask, so extend the class and put your counting in the progress update function. You should have something like this:
private class BackgroundTask extends AsyncTask<Void, Void, Void> {
int i;
protected void onPreExecute() {
i = 0;
}
#Override
protected Void doInBackground(Void... arg0) {
i++;
publishProgress(i);
}
protected void onProgressUpdate(Integer... progress) {
TextView tv1 = (TextView) Main.this.findViewById(R.id.textView1)
TextView tv2 = (TextView) Main.this.findViewById(R.id.textView2)
tv1.setText(i);
tv2.setText(i);
}
protected void onPostExecute(Void result) {
i = null;
}
}
where Main is your activity and textView1 and textView2 are your text views that you are going to update.

If you really want to run two additional threads, that can be done with handlers. Simply create ONE handler for your activity and switch on what. Then launch two threads and when each thread completes send a message to the handler using the appropriate what value. Then update the appropriate text view in the handler as you are back in the UI thread in the handler:
private Handler myHandler= new Handler(){
#Override
public void handleMessage(Message msg){
switch(msg.what){
case 0:
this.removeMessages(0);
Toast.makeText(Main.this,"Message0", Toast.LENGTH_SHORT).show();
break;
case 1:
this.removeMessages(1);
Toast.makeText(Main.this,"Message1", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
break;
}
}
};
Simply replace the toast with your UI update code. You can launch a thread as in:
// DISPLAY ON CLICK HANDLER
threadButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
Thread thread= new Thread( new Runnable() {
public void run() {
try { // DO NOT TOUCH THE UI HERE
Thread.sleep(1000); // mimic time consuming task
}
catch(Exception e){
}
myHandler.sendEmptyMessage(0);
}
});
thread.setDaemon(true);
thread.start();
}
});
If you just want to count from i= 1-10 seconds, you do not even need to use threads, just use postMessageDelayed by looping and calling:
myHandler.sendEmptyMessageDelayed(0, 1000); // what 0, 1 second
You can pass (preferably immutable) data in the messages as in:
Message msg= Message.obtainMessage(0);
Bundle b= new Bundle();
b.putString("stringData",outString);
msg.setData(b);
handler.sendMessage(msg);
and retrieve it as in:
Bundle b= msg.getData();
String data="";
if (b != null){
data= b.getString("stringData");
}
If you only need one additional thread, I would use asyncTask.

Related

why my Handler doesn't work with UI thread

I'm trying to understand how Handler works in a pair with Looper, but i have some problem. I need to do some long operation in a back thread and then to send some result in a textView.
I get the following error after pressing a button:
Only the original thread that created a view hierarchy can touch its views.
public class MainActivity extends AppCompatActivity {
Button mButton;
TextView mTextView;
ConsumeThread mConsumeThread;
class ConsumeThread extends Thread{
public Handler mHandler;
#Override
public void run() {
Looper.prepare();
mHandler = new Handler(){
#Override
public void handleMessage(Message msg){
int arg = msg.what;
someLongOperation(arg);
}
};
Looper.loop();
}
private void someLongOperation(int arg){
// do some long operation
arg += 1000;
mTextView.setText("Operation's code is " +arg); // fatal exception
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.txt_view);
mButton = (Button) findViewById(R.id.button);
mConsumeThread = new ConsumeThread();
mConsumeThread.start();
mButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
if(mConsumeThread.mHandler != null){
Message msg = mConsumeThread.mHandler.obtainMessage(10);
mConsumeThread.mHandler.sendMessage(msg);
}
}
});
}
To get Main Thread Handler You have get Handler as follows .
Because:-
Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it
So you need to get Handler which is associated with MainThread. For that you can use one of the following:-
With Context
Handler mainHandler = new Handler(getMainLooper()){
#Override
public void handleMessage(Message msg) {
}
};
Or Directly with Looper even when do not have Context
Handler mainHandler = new Handler(Looper.getMainLooper()){
#Override
public void handleMessage(Message msg) {
}
};
I assume that you are doing some long running task . So its better if you go with AsyncTask.
private void someLongOperation(int arg){
// do some long operation
arg =+ 1000;
mTextView.setText("Operation's code is " +arg); // fatal exception
}
//see here, you are in worker thread, so you can't excess UI toolkit or else exception, so if you want to do something ui related task in worker thread, use runOnUi, see here
runOnUiThread(new Runnable() {
#Override
public void run() {
textview.setText("");
}
})
You can't update the UI from another thread. You have to move the code that updates the UI to the UIThread.
Try Using:
runOnUiThread(new Runnable() {
#Override
public void run() {
//TextView Update Code
}
});
Tip: Try to reduce the number of lines of code you put inside this, as then there would be no purpose of using another thread.
Only the original thread that created a view hierarchy can touch its views
You have to do ui related work on the main thread...
So you can do it like this...
private void someLongOperation(int arg){
// do some long operation
arg =+ 1000;
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
mTextView.setText("Operation's code is " +arg); // fatal exception
}
});
}

Launch second Intent & View from Main Activity and display ProgressDialog with Thread

I'm having some problems displaying a ProgressDialog. I have a method that scrapes information from a website, and I want to show the user some kind of "Loading" window instead of the app just looking like it is hanging for a second or two when it is working.
Everything works fine when I don't implement a ProgressDialog & Thread, but as soon as I try to implement a Thread to do the heavy lifting, the AboutMe View window is empty.
I have a MainActivity with a TextView that registers a OnClickListener.
A Click on the TextView does a:
startActivity(new Intent(getBaseContext(), AboutMe.class));
This is most of the AboutMe.class Activity:
public class AboutMe extends Activity {
private ProgressDialog aboutMeProgressDialog;
private String htmlAboutMe = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
getAboutMe(); // Get information from Internet
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFeatureDrawableResource(Window.FEATURE_NO_TITLE, android.R.drawable.ic_dialog_alert);
setContentView(R.layout.abutme);
TextView tvAbout = (TextView) findViewById(R.id.aboutMe);
tvAbout.setText(Html.fromHtml(htmlAboutMe));
}
private void getAboutMe() {
try {
aboutMeProgressDialog = ProgressDialog.show(AboutMe.this, "", "Loading");
new Thread() {
#Override
public void run() {
try {
/** Code to scape webpage **/
}
catch (Exception exp) {
exp.printStackTrace();
}
handler.sendEmptyMessage(0);
}
}.start();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
private final Handler handler = new Handler() {
#Override
public void handleMessage(final Message msg) {
aboutMeProgressDialog.dismiss();
}
};
I'm clearly missing out on something trivial, but I've tried to Google just about everything I can think of, but still can't get a Thread together with ProgressDialog to work for me.
please use run on ui thread method
instead of handler.sendEmptyMessage(0); use this code and remove handle message
runOnUiThread(new Runnable() {
#Override
public void run() {
aboutMeProgressDialog.dismiss();
}
});
dude let me know if this was successful,it works most of times
please call getAboutMe() method after calling super.onCreate(savedInstanceState);

Android: Best way to automatic connect to a webservice, without user interaction

I am new in android development and I am doing an application that needs to auto-connect to a webservice every X amount of time. For example connect to retrieve data every 30 seconds, without the user's interaction.
I tried with android.os.Handler but the problem with this Object is that it is running in the ui thread, so when the connection takes to much time the UI freeze...
The only way that I found to do this is to use asynctast and in onPostexecute() call again to the same Asynctask Object, something like that:
public class ScheduledAsyncTask extends AsyncTasc {
doInBackground() {
Thread.sleep(30000); //30 seconds...
// connect to the server and retrieve data...
}
onPostExecute() {
// show new data to the user
ScheduledAsyncTask task = new ScheduledAsyncTask();
task.execute();
}
}
This works great but I assume is not the best way to do it. All suggestions are welcome.
Thanks in advance,
New Example from my side:
I think this is the best way to do what I want to do, check it please:
My Code:
public class MainScheduledTime extends Activity implements OnClickListener{
private int counter = 0;
private int counterTemp = 0;
private TextView label;
private Button button;
private Timer timer = new Timer();
private Handler counterHandler = new Handler();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
label = (TextView) findViewById(R.id.label);
button = (Button) findViewById(R.id.button1);
button.setOnClickListener(this);
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
try {
Thread.sleep(5000); // This emulates an expensive task. For Example: call a webservice and parse the data.
counterTemp++;
setCounter(counterTemp); // This methods needs to be synchronized
counterHandler.post(counterTask);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 0, 10000);
}
private Runnable counterTask = new Runnable() {
public void run() {
label.setText(getCounter() + "");
}
};
public synchronized int getCounter() {
return this.counter;
}
public synchronized void setCounter(int count) {
this.counter = count;
}
#Override
public void onClick(View arg0) {
}
}
you should do your webservice calls in a Service. then you can schedule the service for execution with the AlarmManager
You can simply use a regular Thread.
AsyncTask is not made for infinite loops.

Having fun with Threads

I am having troubles with Threading ;)
I have done an script about my class as follow:
class Test{
TextToSpeech mTts = new TextToSpeech();
boolean volatile mVarGlobal = false;
class T1 extends Thread{
public void run(){
doSomething(){
mVarGlobal = false;
// network working...Take a while
mTts.speak(...) //Speech information received from Network
mVarGlobal = true;
}
}
T1 t = new T1();
t.start();
class CheckVarGlobal extends Thread{
public void run(){
if(!mVarGlobal) {
text2Speech.speak("something");
}
postAtTime(this, 3000);
}
}
CheckVarGlobal c = new CheckVarGlobal ();
c.start();
}
As you can see I have 2 Threads running, one which is getting network information and a second one which is cheking if the information from network has been received. The boolean variable mVarGlobal will be true, and then the Thread which is checking "CheckVarGlobal" will stop as the condition (!mVarGlobal) is false.
The problem is that once i get the information in the 1st Thread from network and speech the information, 2nd Thread still is running and speeching "something". I guess the 2nd Thread has not realize that CheckVarGlobal is true... I have typed the variable as volatile as it is used from 2 threads.
Any idea why is it happening, and how to solve it?
Thanks a lot,
Best.
David.
Dayerman... Here is some EXPERIMENTAL code with two handlers.
Edit. I have edited the code to comply with best practices, using a switch in a single handler:
private Handler myHandler= new Handler(){
#Override
public void handleMessage(Message msg){
switch(msg.what){
case 0:
if (!isDoneThinking){
editTextConfusedText.setText("Still Thinking "+new Integer(thinkSeconds).toString());
thinkSeconds++;
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0),1000);
}
else {
thinkSeconds= 0; // reset timer
}
break;
case 1:
isDoneThinking= true;
onThreadedMessage(msg);
break;
default:
super.handleMessage(msg);
break;
}
}
};
public void onThreadMessage(Message msg){
Bundle b= msg.getData();
String encryptedText="";
if (b != null){
encryptedText= b.getString("encryptedText");
}
editTextConfusedText.setText(encryptedText);
Log.d(TAG,encryptedText);
}
Usage:
buttonConfuseText.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
final String inString= editTextPlainText.getText().toString();
isDoneThinking= false;
myHandler.sendEmptyMessage(0); // <=== START TIMER
Thread thread= new Thread( new Runnable() { // <== START THREADED RUNNABLE
public void run() {
String outString= encrypt(password,inString);
Message msg= myHandler.obtainMessage(1);
Bundle b= new Bundle();
b.putString("encryptedText",outString);
msg.setData(b);
myHandler.sendMessage(msg);
Log.d(TAG,outString);
}
});
thread.setDaemon(true);
thread.start();
}
};
Your are checking the variable in if condition rather than while condition.That means whenever it comes to thread 2 that time only it is checking.Better to use while condition in thread2.

How to kill a thread and handler before going to new activity

Hey all - this code might be a bit messy in the way I'm trying to clean up the handler as I've been trying to track down where the crash happens...
I've got a dialog activity that is showing a password entry with a progressbar being animated by a thread and handler...
It seems that when I'm attempting to see if the progressbar is done, and trying to kill the thread, the way I'm doing this is messing something up when I try to go to a new activity - ie in a way calling a function and not having anything to return to or something...
public class RMO_Dialog extends Activity {
private ProgressBar progbar;
private Button dialogOK;
private EditText dialogPass;
private SharedPreferences prefs;
private String pass;
private int increment=10;
private Thread background;
private Boolean commCalled=false;
public void callCommunications(){
progbar.setVisibility(0);
progbar.setProgress(0);
background.stop();
Toast.makeText(getApplicationContext(), "Call communication should happen once.", Toast.LENGTH_LONG).show();
// Intent i = new Intent();
// i.setClass(RMO_Dialog.this, RMO_Comm.class);
// i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// startActivity(i);
// finish();
}
public void buzzUser(){
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
int dot = 200;
int dash = 500;
int short_gap = 200;
int medium_gap = 500;
int long_gap = 1000;
long[] pattern = {0,dot, short_gap, dot, short_gap, dot, medium_gap, dash, short_gap, dash, short_gap, dash, medium_gap, dot, short_gap,
dot, short_gap, dot, long_gap};
v.vibrate(pattern, -1);
}
public void killCountdown(){
progbar.setVisibility(0);
progbar.setProgress(0);
background.stop();
}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.dialogpassword);
buzzUser();
prefs = this.getSharedPreferences("RMO", MODE_WORLD_READABLE);
pass = prefs.getString("password", "");
dialogOK = (Button) findViewById(R.id.dialogOK);
dialogPass = (EditText) findViewById(R.id.dialogPass);
progbar = (ProgressBar) findViewById(R.id.progress);
progbar.setProgress(0);
background = new Thread(new Runnable(){
#Override
public void run() {
try{
while(progbar.getProgress()<=progbar.getMax()){
Thread.sleep(300);
progressHandler.sendMessage(progressHandler.obtainMessage());
}
}catch(java.lang.InterruptedException e){
Toast.makeText(getApplicationContext(), "Error thrown.", Toast.LENGTH_LONG).show();
}
}
});
background.start();
dialogOK.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(dialogPass.getText().toString().equals(pass.toString())){
killCountdown();
Toast.makeText(getApplicationContext(), "Guardian Angel next alert has been disengaged.", Toast.LENGTH_LONG).show();
Intent intent = new Intent();
intent.setClass(RMO_Dialog.this, RMO.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}else{
callCommunications();
}
}
});
}
Handler progressHandler = new Handler(){
public void handleMessage(Message msg){
progbar.incrementProgressBy(increment);
if(progbar.getProgress()==progbar.getMax()){
Toast.makeText(getApplicationContext(), "commcalled: "+ commCalled, Toast.LENGTH_LONG).show();
if(commCalled==false){
commCalled=true;
callCommunications();
}
}
}
};
}
Thread.stop is deprecated call, instead you should use the Thread.interrupt method.
public void killCountdown(int waitTime){
progbar.setVisibility(0);
progbar.setProgress(0);
// deprecated: background.stop();
background.interrupt(); // <-- OK
background.join(waitTime); // optionally wait for the thread to exit
}
Thread.Interrupt will cause a ThreadInterruptedException next time your thread blocks or sleeps and you're already handling in your thread body so that's good. Additionally, you might want to include a volatile flag that will allow you to stop the thread when it's not blocking or sleeping, but that's optional.
You might consider using an AsyncTask instance instead of a runnable and a handler.
If you need to cancel an AsycnTask instance just call .cancel(true) on your AsyncTask object reference. This will take care of both the background method (doInBackground()) and the progress updater (onProgressUpdate()).
I generally find AsyncTask easier to use than trying to handle all the details myself.
So, inside RMO_Dialog, use call execute() on an instance of a class you create that extends AsyncTask.
public class RMO_Dialog extends Activity {
...
// Get ref to your bg task for easily cancellation if needed
PassWordEntry background = new PassWordEntry();
// Start bg task
background.execute([PARAMS]);
...
// Cancel task
background.cancel(true);
...
// AsyncTask lets you encapsulate both your runnable and handler in it
private static class PassWordEntry() extends AsyncTask<[PARAMS], [PROGRESS], [RESULT]> {
protected [RESULT] doInBackground() {
... // Runnable stuff here
return [RESULT];
}
protected void onProgressUpdate([PROGRESS]... progress) {
... // progressHandler stuff here
}
protected void onPostExecute([RESULT]) {
// Clean up return data when all done w BG here
}
}
}

Categories

Resources