I am trying to display a progress bar in my application but am running into a problem with threading. Here is the code I am using to do it:
package com.integrated.mpr;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
public class Progess extends Activity {
static String[] display = new String[Choose.n];
private static final int Progress = 0;
ProgressBar bar;
TextView label;
Handler handler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.progress);
bar = (ProgressBar) findViewById(R.id.progBar);
new Thread(new Runnable() {
int i = 0;
int progressStatus = 0;
public void run() {
while (progressStatus < 100) {
progressStatus += doWork();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Update the progress bar
handler.post(new Runnable() {
public void run() {
bar.setProgress(progressStatus);
i++;
}
});
}
}
private int doWork() {
display = new Logic().finaldata();
return i * 3;
}
}).start();
Intent openList = new Intent("com.integrated.mpr.SENSITIVELIST");
startActivity(openList);
}
}
I am getting the following logcat message:
05-30 12:38:00.082: E/AndroidRuntime(17332):
java.lang.RuntimeException: Can't create handler inside thread that
has not called Looper.prepare()
Please help me, I am new to threading.
Instead of implementing thread this way, i would suggest you to implement AsyncTask which is known as Painless Threading in Android.
display progress bar inside onPreExecute()
do background tasks inside the doInBackground()
dismiss progress bar inside onPostExecute()
update progress bar inside onProgressUpdate()
For a quick one, try the code below.
runOnUiThread(new Runnable(){
#Override
public void run() {
bar.setProgress(progressStatus);
i++;
}
}
I think answer is in the error message from your question - just write Looper.prepare() before handler.post(new Runnable() ...).
Here are some references:
Looper and Handler in Android
Android - What is the purpose of Looper and How to user it (See Dharmendra's solution as anexample).
Android Multithreading in a UI environment
Related
Hello everyone, I am new to android application development. I have written and code and trying to update the UI from the message obtained from handler. I have tried to debug the code but i couldn't find what the error is. please help me out. Thank you.
package com.threadcommunicationexample;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener {
Button Click;
TextView Message;
Handler Mrmessenger;
int Counter = 0;
/*
*Initialisation area....
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Click = (Button) findViewById(R.id.ClickButton);
Message = (Button) findViewById(R.id.TextView);
Click.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// Operation to be performed after the button click
Runnable myThreadRunner = new Runnable() {
#Override
public void run() {
// Saving the text in bundle and passing it to handler ....
while (Counter < 100) {
try {
Thread.sleep(100);
Message msg = Mrmessenger.obtainMessage();
Bundle myBundle = new Bundle();
myBundle.putString("Communication", "Loading....");
msg.setData(myBundle);
//Sending the bundle to Handler
Mrmessenger.sendMessage(msg);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Mrmessenger = new Handler() {
public void handleMessage(final Message msg) {
Mrmessenger.post(new Runnable() {
// Getting the message from the handler and updating it using textview
#Override
public void run() {
Bundle ComBundle = msg.getData();
// TODO Auto-generated method stub
String myMessage = ComBundle
.getString("Communication");
Message.setText(myMessage);
}
});
}
};
Counter++;
}
}
};
Thread myRunner = new Thread(myThreadRunner);
//creating a thread and passing the runnable object.
myRunner.start();
}
}
For one thing, the line
Message msg = Mrmessenger.obtainMessage();
will fail since you don't initialize Mrmessenger until a few lines later.
Would recommend you use AsyncTask for this type of thing; it deals with all of the threading so you don't have to.
Also: per Java conventions, variable names should start with a lowercase letter, class names start with an uppercase letter. This would make your code easier for others to read.
the problem with your code is, that the run method in your handler is called on another thread than the main / ui thread.
To make the code working you can use the following code..
runOnUiThread(new Runnable() {
public void run() {
Message.setText(myMessage);
}
});
..to run the ui update explicitly on the ui thread.
The following code is for a splash screen in my app.What I need to be done is when the activity loads the image should be displayed as default and then after few seconds the image should be changed to another in the same activity. I have another image like the first one with different color. I wanted to change the screen for that image after few seconds.
I have done it like this in my code.
package com.ruchira.busguru;
import android.media.MediaPlayer;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.RelativeLayout;
public class SplashScreen extends Activity {
ImageView imgBus;
MediaPlayer introSound, bellSound;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash_screen);
imgBus = (ImageView) findViewById(R.id.imgBus);
imgBus.setImageResource(R.drawable.blackbus);
introSound = MediaPlayer.create(SplashScreen.this, R.raw.enginestart);
introSound.start();
Thread timer = new Thread(){
public void run(){
try{
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
imgBus.setImageResource(R.drawable.bluekbus);
}
}
};
timer.start();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.splash_screen, menu);
return true;
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
introSound.stop();
finish();
}
#Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
introSound.stop();
finish();
}
}
but the problem is the program stops by executing in the thread saying...
"android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."
How I can achieve this target ? Does anybody please help me to sort out this problem. I am new to android development..
Thanks.
You should use the ImageView's Handler and a Runnable. Handler's are schedulers specific to Android and they can run on the UI thread. Try this:
ImageView imgBus;
MediaPlayer introSound, bellSound;
Runnable swapImage = new Runnable() {
#Override
public void run() {
imgBus.setImageResource(R.drawable.bluekbus);
}
};
And inside onCreate() call:
imgBus = (ImageView) findViewById(R.id.imgBus);
imgBus.setImageResource(R.drawable.blackbus);
imgBus.postDelayed(swapImage, 3000); // Add me!
Understand that splash screens are frowned upon because you should focus on starting the app as soon as possible (while loading the slower element in the background). However sometimes a slight delay is unavoidable.
Write this inside finally
runOnUiThread(new Runnable() {
public void run() {
SplashScreen.class.img.setImageResource(R.drawable.bluekbus);
}
});
You should always update your UI from the UI Thread itself...
Try the following,
We cannot access and update the UI from worker threads, only we can access the UI from UIThread. If we need it to update with background thread then we can use Handler to do this like in the following code.
this.imgBus = (ImageView) findViewById(R.id.splashImageView);
imgBus.setImageResource(R.drawable.your_first_image);
this.handler = new Handler();
introSound = MediaPlayer.create(SplashTestForSO.this, R.raw.enginestart);
introSound.start();
Thread timer = new Thread(){
public void run(){
try{
sleep(2000);
YourActivity.this.handler.post(runnable);
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
}
}
};
timer.start();
this.runnable = new Runnable() {
public void run() {
//call the activity method that updates the UI
YourActivity.this.imgBus.setImageResource(R.drawable.your_scond_image_view);
}
};
I have one button in the main.xml which will link to another xml which include information from server. I include progress bar to avoid the blank screen while the system is loading the information. i already done the code as below but it's still not the things i wanted. the code below will "WAIT" for 1000 ms then only will execute the next code. how can i modify it so that the loading "WAIT TIME" will depends on the internet speed, if internet connection is slow, then the progress-bar-screen will show longer.
package com.android.myApps;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.TextView;
public class MainScr extends Activity {
private final int WAIT_TIME = 1000;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.MainScr);
}
public void onClickCategory(View view)
{
findViewById(R.id.mainSpinner1).setVisibility(View.VISIBLE);
new Handler().postDelayed(new Runnable(){
#Override
public void run() {
Intent mainIntent = new Intent(MainScr.this, Category.class);
MainScr.this.startActivity(mainIntent);
MainScr.this.finish();
}
}, WAIT_TIME);
}
}
The mistake you are doing here is you are dumping specific time into your code
You never know how much it will take to get response.
You should follow following approach
Step 1 Show progress dialog on screen
Step 2 Let download take its own time.But it should be done in new thread
Step 3 Once download is complete it will raise message that task is done,now remove that
progress dialog and proceed.
I am pasting sample code here.Hope it will help you.
package com.android.myApps;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
public class MainScr extends Activity
{
private Handler handler;
private ProgressDialog progress;
private Context context;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
context = AncActivity.this;
progress = new ProgressDialog(this);
progress.setTitle("Please Wait!!");
progress.setMessage("Wait!!");
progress.setCancelable(false);
progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
progress.dismiss();
Intent mainIntent = new Intent(context, Category.class);
startActivity(mainIntent);
super.handleMessage(msg);
}
};
progress.show();
new Thread()
{
public void run()
{
// Write Your Downloading logic here
// at the end write this.
handler.sendEmptyMessage(0);
}
}.start();
}
}
Did you try Asyntask? Your doing process will be update in UI.
public final class HttpTask
extends
AsyncTask<String/* Param */, Boolean /* Progress */, String /* Result */> {
private HttpClient mHc = new DefaultHttpClient();
#Override
protected String doInBackground(String... params) {
publishProgress(true);
// Do the usual httpclient thing to get the result
return result;
}
#Override
protected void onProgressUpdate(Boolean... progress) {
// line below coupled with
// getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS)
// before setContentView
// will show the wait animation on the top-right corner
MyActivity.this.setProgressBarIndeterminateVisibility(progress[0]);
}
#Override
protected void onPostExecute(String result) {
publishProgress(false);
// Do something with result in your activity
}
}
package com.aviyehuda.test.multithreading;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MultithreadingTest extends Activity {
Button btn;
private Handler myHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = (Button) findViewById(R.id.Button01);
}
public void buttonClicked(View v) {
myHandler = new Handler();
MyThread mThread = new MyThread();
mThread.start();
}
class MyThread extends Thread {
#Override
public void run() {
for (int i = 0; i < 30; i++) {
myHandler.post(new NewThreaad(i));
}
}
}
class NewThreaad implements Runnable{
int i;
public NewThreaad(int n) {
i=n;
}
#Override
public void run() {
((TextView) findViewById(R.id.TextView01)).setText("Hello:"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
I have code mentioned above but getting result Hello29 on TextView but i want Hello1,Hello2,hello3.................Hello29 one by one automatically
Please give me hint what I am doing wrong
A couple of things.
First, after changing the text, you should call invalidate on the TextView to force a refresh.
Second, to do operation on the UI, you should run that in the UI thread. Use runOnUiThread
Well, the main problem is that you're not appending you are overwriting. Instead of
((TextView) findViewById(R.id.TextView01)).setText("Hello:"+i);
do
TextView tv = ((TextView) findViewById(R.id.TextView01));
String text = tv.getText().toString();
tv.setText(text + " Hello:" + i);
You need to move the 500 ms delay to your for-loop, between posting of messages. I think you're expecting the messages to execute sequentially one after the other, but they don't, which is the reason you just see the result of the last one.
I have problem that how to change text inside the progressdialog (basically having STYLE_HORIZONTAL as in figure) (Using Android 1.6)
to text shown in figure.
Please help out in this case.
My code about the progressdialog refers like this:-
mProgressDialog = new ProgressDialog(PDFActivity.this);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setTitle(R.string.msgDownloadingWait);
mProgressDialog.setMessage(getResources().getString(
R.string.msgDownloading));
// User is not allowed to cancel the download operation.
mProgressDialog.setCancelable(false);
mProgressDialog.setMax(serverFileCount);
mProgressDialog.show();
Thanks in advance.
I got the answer related to this stuff some days back(but updating it today as got some free time).
Here the code that I have used for making this stuff best.I achieved above thing by Custom Dialog.Firstly here the code of activity from which I called the class of Custom Dialog.
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.widget.ProgressBar;
import android.widget.TextView;
public class ProgressThread extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyDialog dialog = new MyDialog(this);
dialog.show();
}
}
Now the code related to the Custom Dialog. Here I have used ProgressBar & TextViews in CustomDialog & made calculations on basis on download which in turn updates TextViews.The example used here updates the textviews & progressbar in dummy manner.You change that as per your need.
import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.os.Message;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MyDialog extends Dialog {
public static final int STATUS_UPDATE = 101;
public static final int STATUS_COMPLETE = 100;
ProgressBar progressBar;
TextView textView;
TextView percent;
int increment;
int progress;
public MyDialog(Context context) {
super(context);
setContentView(R.layout.progressbar);
setDialog();
}
private void setDialog() {
setTitle("Downloading Files....");
textView = (TextView) findViewById(R.id.textProgress);
progressBar = (ProgressBar) findViewById(R.id.progress_horizontal);
percent = (TextView) findViewById(R.id.textPercentage);
percent.setTextColor(Color.WHITE);
textView.setTextColor(Color.WHITE);
progressBar.setProgressDrawable(getContext().getResources()
.getDrawable(R.drawable.my_progress));
progressBar.setIndeterminate(false);
// set the maximum value
progressBar.setMax(1315);
launcherThread();
}
private void launcherThread() {
LoaderThread loaderThread = new LoaderThread();
loaderThread.start();
LauncherThread launcherThread = new LauncherThread();
launcherThread.start();
}
private class LoaderThread extends Thread {
#Override
public void run() {
try {
while (progressBar.getProgress() < progressBar.getMax()) {
// wait 500ms between each update
Thread.sleep(100);
increment++;
// active the update handler
progressHandler.sendEmptyMessage(STATUS_UPDATE);
}
progressHandler.sendEmptyMessage(STATUS_COMPLETE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// handler for the background updating
Handler progressHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case STATUS_UPDATE:
progressBar.setProgress(increment);
float value = increment / 1315F;
percent.setText(" " + ((int) (value * 100)) + "%");
System.out.println(value * 100);
textView.setText(String.valueOf(progressBar.getProgress())
.concat(" / " + progressBar.getMax()));
break;
case STATUS_COMPLETE:
dismiss();
}
}
};
private class LauncherThread extends Thread {
#Override
public void run() {
progressHandler.sendMessage(progressHandler.obtainMessage());
progressHandler.sendEmptyMessage(0);
}
}
}