I wan't to refresh a list of website every X Minutes.
After reading this thread: nested postDelayed / Runnable / Handler Android
I decided to use a Runnable + Handler instead of a TimerTask.
Unfortunately my Runnable won't run a second time. It will refresh the data once, then nothing happens.
my onCreate() Method looks like this:
...
fooWebsitesListView = (ListView) findViewById(R.id.fooWebsitesListView);
fooWebsitesAdapter = new ArrayAdapter<fooWebsite>(
getApplicationContext(), R.layout.list_black_text,
R.id.list_content, websiteList);
fooWebsitesListView.setAdapter(fooWebsitesAdapter);
final fooWebsitesActivity fooWebsitesRefreshActivity = new fooWebsitesActivity();
mHandler = new Handler();
refreshfooWebsitesRunnable = new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
Log.d("scheduled task", "---Scheduled Task: Refresh fooWebsites");
try {
fooWebsitesRefreshActivity.execute();
//mHandler.postDelayed(refreshfooWebsitesRunnable,3000);
mHandler.postDelayed(this, 3000);
} catch (Exception e) {
// Toast.makeText(getApplicationContext(),
// "Unable to receive Data", Toast.LENGTH_SHORT)
// .show();
}
}
};
startRepeatingTask();
}
void startRepeatingTask() {
refreshfooWebsitesRunnable.run();
}
LOG:
12-14 14:56:47.624: D/scheduled task(7995): ---Scheduled Task: Refresh fooWebsites
And there has been no chance since yet. The UI Thread is working fine, the app won't freeze or anything like that. Why won't my runnable loop?
Try to call mHandler.post() instead of refreshfooWebsitesRunnable.run() and call a mHandler.removeCallbacks() in the beginning of your run() method.
Related
I have a listener that is getting updates from a separate process. (I'm using IPC.)
Log.i("Test1", Thread.currentThread().toString()); // Thread[Binder_3,5,main]
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i("Test2", Thread.currentThread().toString()); // Thread[main,5,main]
switch (taskId) {
case Constants.DOWNLOAD_TASK_EXECUTED: {
long time = prefs.getLong(Constants.LAST_UPDATED_KEY, System.currentTimeMillis());
String msg = Utils.getLastUpdatedString(res, time, Locale.getDefault(), false);
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show();
break;
}
case Constants.DELETED_TASK_EXECUTED: {
String msg = res.getString(R.string.delete_success);
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show();
break;
}
}
}
});
Is it necessary to call the runOnUiThread method and pass in a Runnable to display toast messages when I get updates? Or is it okay to just display them in the same thread (Binder_3)? I'm not modifying anything in the UI.
Tost Or Log Work in thread or other processes (but it's better to NOT Risk ! ) but Changing the UI must always Run in UI thread as you told with runOnUiThread or using handler
Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
}
});
My Professional Android 4 Application Development book states, "Toasts must be created and shown on the GUI thread; otherwise, you risk throwing a cross-thread exception."
I have an AsyncTask class that I execute that downloads a big list of data from a website.
In the case that the end user has a very slow or spotty data connection at the time of use, I'd like to make the AsyncTask timeout after a period of time. My first approach to this is like so:
MyDownloader downloader = new MyDownloader();
downloader.execute();
Handler handler = new Handler();
handler.postDelayed(new Runnable()
{
#Override
public void run() {
if ( downloader.getStatus() == AsyncTask.Status.RUNNING )
downloader.cancel(true);
}
}, 30000 );
After starting the AsyncTask, a new handler is started that will cancel the AsyncTask after 30 seconds if it's still running.
Is this a good approach? Or is there something built into AsyncTask that is better suited for this purpose?
Yes, there is AsyncTask.get()
myDownloader.get(30000, TimeUnit.MILLISECONDS);
Note that by calling this in main thread (AKA. UI thread) will block execution, You probably need call it in a separate thread.
Use CountDownTimer Class in side the extended class for AsyncTask in the onPreExecute() method:
Main advantage, the Async monitoring done internally in the class.
public class YouExtendedClass extends AsyncTask<String,Integer,String> {
...
public YouExtendedClass asyncObject; // as CountDownTimer has similar method -> to prevent shadowing
...
#Override
protected void onPreExecute() {
asyncObject = this;
new CountDownTimer(7000, 7000) {
public void onTick(long millisUntilFinished) {
// You can monitor the progress here as well by changing the onTick() time
}
public void onFinish() {
// stop async task if not in progress
if (asyncObject.getStatus() == AsyncTask.Status.RUNNING) {
asyncObject.cancel(false);
// Add any specific task you wish to do as your extended class variable works here as well.
}
}
}.start();
...
change CountDownTimer(7000, 7000) -> CountDownTimer(7000, 1000) for example and it will call onTick() 6 times before calling onFinish(). This is good if you want to add some monitoring.
Thanks for all the good advice I got in this page :-)
In the case, your downloader is based upon an for an URL connection, you have a number of parameters that could help you to define a timeout without complex code:
HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
urlc.setConnectTimeout(15000);
urlc.setReadTimeout(15000);
If you just bring this code into your async task, it is ok.
'Read Timeout' is to test a bad network all along the transfer.
'Connection Timeout' is only called at the beginning to test if the server is up or not.
I don't think there's anything like that built into AsyncTask. Your approach seems to be a good one. Just be sure to periodically check the value of isCancelled() in your AsyncTask's doInBackground method to end this method once the UI thread cancels it.
If you want to avoid using the handler for some reason, you could check System.currentTimeMillis periodically within your AsyncTask and exit on timeout, although I like your solution better since it can actually interrupt the thread.
Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
//async task
final RunTask tsk = new RunTask ();
tsk.execute();
//setting timeout thread for async task
Thread thread1 = new Thread(){
public void run(){
try {
tsk.get(30000, TimeUnit.MILLISECONDS); //set time in milisecond(in this timeout is 30 seconds
} catch (Exception e) {
tsk.cancel(true);
((Activity) mContext).runOnUiThread(new Runnable()
{
#SuppressLint("ShowToast")
public void run()
{
Toast.makeText(mContext, "Time Out.", Toast.LENGTH_LONG).show();
finish(); //will close the current activity comment if you don't want to close current activity.
}
});
}
}
};
thread1.start();
}
You can put one more condition to make cancellation more robust. e.g.,
if (downloader.getStatus() == AsyncTask.Status.RUNNING || downloader.getStatus() == AsyncTask.Status.PENDING)
downloader.cancel(true);
Inspiring from question I have written a method which do some background task via AsyncTask and if processing takes more then LOADING_TIMEOUT then an alert dialogue to retry will appear.
public void loadData()
{
final Load loadUserList=new Load();
loadUserList.execute();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (loadUserList.getStatus() == AsyncTask.Status.RUNNING) {
loadUserList.cancel(true);
pDialog.cancel();
new AlertDialog.Builder(UserList.this)
.setTitle("Error..!")
.setMessage("Sorry you dont have proper net connectivity..!\nCheck your internet settings or retry.")
.setCancelable(false)
.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
loadData();
}
})
.setNegativeButton("Exit", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
System.exit(0);
}
})
.show();
}
}
}, LOADING_TIMEOUT);
return;
}
that code will help me explain my problem:
public class TestHandlerActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new Thread(){
public void run(){
for (int i=0;i<20;i++){
handler.post(run);
}
}
}.start();
}
Handler handler=new Handler(){
};
Runnable run = new Runnable(){
public void run(){
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.d("TAG", "Message processed");
}
};
}
That way the new thread makes the petitions which are served by the handler in the main thread.
What i need to do is exactly the opposite. The UI thread launches petitions wich are served sequentially by another thread (the order is important), and the UI thread don't need to be notified when each petition end.
Some points are important for this: The petitions have 3 parameters (int, int, boolean), they make changes in the database and are generated by the interaction of the user with the UI, so they aren't launched simultaneously
Thanks in advance
One option is to use this for making your thread: http://developer.android.com/reference/android/os/HandlerThread.html
This will create a thread with its own message queue and loop. You can create a Handler to run work on the thread like so:
HandlerThread handlerThread = new HandlerThread("My Handler");
handlerThread.start();
Handle myHandler = new Handler(handlerThread.getLooper());
This does require that all work done by thread be done so by sending messages and scheduling Runnables on it through Handlers.
I am using simple thread to execute the httpGet to server when a button is clicked, but I get this after execution.
Button b_back = (Button) findViewById(R.id.bback);
b_back.setOnClickListener(this);
Button b_sign_up = (Button) findViewById(R.id.signup_button);
b_sign_up.setOnClickListener(this);
#Override
public void onClick(View arg0)
{
// TODO Auto-generated method stub
switch (arg0.getId())
{
case R.id.bback:
Intent i = new Intent(this, MainSwitch.class);
finish();
startActivity(i);
break;
// More buttons go here (if any) ...
case R.id.signup_button:
if(username.getText().toString().equalsIgnoreCase("") ||
password.getText().toString().equalsIgnoreCase("") ||
email.getText().toString().equalsIgnoreCase(""))
{
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setMessage("Please fill in all the gaps!");
dialog.show();
}
else
{
//****** Call method that sends the information to server.
Thread background = new Thread (new Runnable()
{
public void run()
{
// Call the time consuming method
handler.sendEmptyMessage(0);
}
});
background.start();
}
}
}
private Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
Toast.makeText(getApplicationContext(),
"Done thread",
Toast.LENGTH_SHORT).show();
}
};
The errorline you get:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
is typically linked to problems where you try to do stuff to UI-elements on a non-UI-thread.
I suppose that by stating // Call the time consuming method you have left out some of your code. The fact that this time consuming method runs on a regulare Thread means it cannot interact with UI-elements.
If you post more code (and also which specify the line where the error occurs) we can probably provide more info on how to solve it.
I have a thread in java/Android like this:
Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
update_i();
}
};
#Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
Thread myThread = new Thread(new Runnable() {
public void run() {
while (true) {
try {
handler.sendMessage(handler.obtainMessage());
Thread.sleep(timer);
} catch (Throwable t) {
}
}
}
});
myThread.start();
}
The thread works fine when I run my application. But I want to start/restart the thread with a button.
Button.OnClickListener StartButtonOnClickListener = new Button.OnClickListener() {
#Override
public void onClick(View v) {
//start/restart the thread
}
};
If I copy the thread into the button I just make a new thread every time the user clicks on the button. I want to run the thread when the user first time click on the button, "kill it" and start from the beginning if the user click on the button a second time (I don’t want to start a second thread).
I think that Colin is wright you can´t just restart you need to make a new instance, and the interrupt function will stop the running thread if it is still running.
I recommend you make an inner class, instand of an inline implementation, it will make it easier to understand.
if(myThread.isAlive()){
myThread.interrupt();
}
myThread = new MyThread();
myThread.start();
hope this helps
You can't restart a Thread.
From the documentation :
Throws IllegalThreadStateException
if the Thread has been started before
You can kill the previous thread, but in the end you will have to create a second instance of your thread.
Resources :
Javadoc Android - Thread.start()