I'm trying to implement a callback method to be called whenever a Thread is is done It's work.
I'm using the interface approach and not the Handler approach.
I have a main UI Thread which is the onCreate(Bundle) method and a Thread i call from within the onCreate(Bundle) method.
(Only relevant code posted).
MainActivity.java:
public class MainActivity extends AppCompatActivity implements GetDataFromTheWebThreadCallback
{
public static GetDataFromTheWebThread getDataFromTheWebThread;
private GetDataFromTheWebEventNotifier eventNotifier;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.eventNotifier = new GetDataFromTheWebEventNotifier(MainActivity.this);
// The thread that will search the web for data
this.getDataFromTheWebThread = new GetDataFromTheWebThread();
getDataFromTheWebThread.start();
}
#Override
public void finishParsing() // The callback method that never called
{
Toast.makeText(MainActivity.this,"Callback Method Called",Toast.LENGTH_LONG).show();
Log.d("Callback:", "Callback Method Called");
}
}
GetDataFromTheWebEventNotifier.java:
public class GetDataFromTheWebEventNotifier
{
private GetDataFromTheWebThreadCallback callbackInterface;
public GetDataFromTheWebEventNotifier(GetDataFromTheWebThreadCallback callbackInterface)
{
this.callbackInterface = callbackInterface;
}
public void onEvent()
{
this.callbackInterface.finishParsing();
}
}
GetDataFromTheWebThreadCallback.java:
public interface GetDataFromTheWebThreadCallback
{
void finishParsing(); // The method i wish to invoke when certain event will happen
}
GetDataFromTheWebThread.java:
public class GetDataFromTheWebThread extends Thread
{
public static boolean isFinished = false; // False - the thread is still running. True - the thread is dead
#Override
public void run()
{
GetDataFromTheWebThread.isFinished = false;
try
{
// Some internet computations...
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
GetDataFromTheWebThread.isFinished = true;
}
}
So what's wrong with my callback?
As for your ThreadClass, have a constructor with the callback :
public class GetDataFromTheWebThread extends Thread {
public static boolean isFinished = false; // False - the thread is still running. True - the thread is dead
private GetDataFromTheWebThreadCallback mCallback;
public GetDataFromTheWebThread(GetDataFromTheWebThreadCallback c) {
mCallback = c;
}
#Override
public void run() {
GetDataFromTheWebThread.isFinished = false;
try {
// Some internet computations...
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
GetDataFromTheWebThread.isFinished = true;
if (mCallback !- null) {
mCallback.finishParsing();
}
}
}
As for your Activity, simply pass the callback when creating your Thread :
this.getDataFromTheWebThread = new GetDataFromTheWebThread(this);
As well as :
#Override
public void finishParsing() {
// You know that this function is called from a background Thread.
// Therefore from here, run what you have to do on the UI Thread
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this,"Callback Method Called",Toast.LENGTH_LONG).show();
Log.d("Callback:", "Callback Method Called");
}});
}
You never call onEvent(). Is your notifier supposed to be watching the isFinished variable or something?
Actually you didn't call onEvent(). And check AsyncTask.
Related
EDIT: I've found that what I'm describing below only occurs on my emulated device (Nexus 5, target api 19, 4.4.2 with Intel Atom (x86) cpu), but NOT on my physical device (HTC One)....
EDIT2: Edit1 was due to an IllegalStateException that I didnt catch. Added some code to check if the thread was already running before trying to start it. This combined with the accepted answer resolved my issue.
I have implemented an activty that starts a new thread in the activity's onCreate method, like this:
...
private boolean running;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
running = true;
new Thread(null, work, "myThread").start();
}
Runnable work = new Runnable() {
#Override
public void run() {
while (running) {
//Doing work
}
}
};
I'm "pausing" my thread with my activity's onPause method, like this:
#Override
protected void onPause() {
running = false;
super.onPause();
}
So I thought that resuming it would be just as easy...ยจ
#Override
protected void onResume(){
running = true;
super.onResume();
}
but my thread isn't resuming. Any ideas why? Thankful for any help.
Marcus
All of the answers i think have some issues about your running variable because you can not write and read a variable from two different Threads without synchronized block so i post my own answer:
package com.example.threadandtoast;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity {
public class MonitorObject{
public boolean running = true;
public String message = "";
public boolean mustBePost = true;
}
Thread t;
int threadNameCounter = 0; // i use this variable to make sure that old thread is deleted
// when i pause, you can see it and track it in DDMS
Runnable work = new Runnable() {
boolean myRunning;
#Override
public void run() {
synchronized(mSync) {
myRunning = mSync.running;
}
while (myRunning) {
runOnUiThread(new Runnable() { // in order to update the UI (create Toast)
#Override // we must switch to main thread
public void run() {
// i want to read the message so i must use synchronized block
synchronized(mSync) {
// i use this variable to post a message just for one time because i am in an infinite loop
// if i do not set a limit on the toast i create it infinite times
if(mSync.mustBePost){
Toast.makeText(MainActivity.this, mSync.message, Toast.LENGTH_SHORT).show();
// the message post so i must set it to false
mSync.mustBePost = false;
// if i am going to pause set mSync.running to false so at the end of infinite loop
//of thread he reads it and leaves the loop
if(mSync.message.equals("Main Activity is going to pause")){
mSync.running=false;
}
}
}
}
});
synchronized(mSync) {
myRunning = mSync.running;
}
}
}
};
final MonitorObject mSync = new MonitorObject();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onPause() {
super.onPause();
synchronized(mSync) {
// mSync.running = false; you can not set it here because
// it is possible for the thread to read it and exit the loop before he posts your message
mSync.mustBePost=true;
mSync.message = "Main Activity is going to pause";
}
}
#Override
protected void onResume(){
super.onResume();
threadNameCounter++;
synchronized(mSync) {
mSync.running = true;
mSync.mustBePost=true;
mSync.message = "Main Activity is going to resume";
}
t = new Thread(work,"My Name is " + String.valueOf(threadNameCounter));
t.start();
}
}
Or you can use this code:
public class MainActivity extends ActionBarActivity {
Thread t;
int threadNameCounter = 0; // i use this variable to make sure that old thread is deleted
// when i pause, you can see it in DDMS
String message = "";
boolean isPost = false;
Runnable work = new Runnable() {
#Override
public void run() {
while (true) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if(!isPost){
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
isPost = true;
if( message.equals("Main Activity is going to pause")){
t.interrupt();
}
}
}
});
if(Thread.currentThread().isInterrupted()){
break;
}
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onPause() {
super.onPause();
message = "Main Activity is going to pause";
isPost = false;
}
#Override
protected void onResume(){
super.onResume();
message = "Main Activity is going to resume";
isPost = false;
threadNameCounter++;
t = new Thread(work,"My Name is " + String.valueOf(threadNameCounter));
t.start();
}
}
you can also use semaphore or wait-notify approach.
i put public String message = ""; and public boolean mustBePost = true; in to mSync object but it is
not necessary because only main thread have an access to them.
if you have any problem please ask.
The statement running = false; will stop execution of the Thread, instead of pausing it. Use two variables: One for stopping current Thread, and another for pausing and resuming the Thread, as follow:
boolean isThreadPause=false;
Runnable work = new Runnable() {
#Override
public void run() {
while (running) {
if (!isThreadPause) {
// Doing work
}
}
}
};
In the onPause event of the Activity, set isThreadPause to true, and in the onResume event, set isThreadPause to false.
This is because your Runnable object stops when the while loop stops. You could try this:
Runnable work = new Runnable() {
#Override
public void run() {
while () {
if(running){
//Doing work
}
}
}
};
Okay this is driving me nuts. I have a worker thread, (Network call) that needs to run separate of the UI thread, (Its actually a ThreadPoolExecutor but I simplified it to prove my point). If you run this code in portrait, without rotation, the text updates. I put in the delay there to allow for rotations to show my issue. If you start in portrait and before text updates rotation to landscape the text does not update. If you comment the code you can see the listener fire but the text never updates.
I am trying to simulate a custom network call running in a separate thread that may take some time to come back if the user rotates in between then the data gets lost. We are trying to prevent multiple network calls to save data usage on a phone.
package com.example.test;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.content.res.Configuration;
import android.view.Menu;
import android.widget.TextView;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int orientation = getResources().getConfiguration().orientation;
if (Configuration.ORIENTATION_LANDSCAPE == orientation) {
//Do SomeThing; // Landscape
} else {
startBackgroundWork();
//Do SomeThing; // Portrait
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private Handler mHandler = new Handler();
public void startBackgroundWork() {
new WorkingThread(new SomeListener() {
public void onSomethingDone(final Object result) {
mHandler.post(new Runnable() {
public void run() {
((TextView)findViewById(R.id.text)).setText((String)result);
//showMyDialog(result);
}
});
}
}).start();
}
public interface SomeListener {
public void onSomethingDone(Object result);
}
public class WorkingThread extends Thread {
private SomeListener mListener;
public WorkingThread(SomeListener listener) {
mListener = listener;
}
public void run() {
/* do some work */
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mListener.onSomethingDone("New Text");
}
}
}
It's because on rotation, the activity is re created, therefore all your code is binded to the old activity. You should make a reference to your working thread :
private static WorkingThread mWorkingThread;
public void startBackgroundWork() {
mWorkingThread = new WorkingThread(new SomeListener() {
public void onSomethingDone(final Object result) {
mHandler.post(new Runnable() {
public void run() {
((TextView)findViewById(R.id.text)).setText((String)result);
//showMyDialog(result);
}
});
}
}).start();
}
then onCreate update it :
public class WorkingThread extends Thread {
private SomeListener mListener;
public WorkingThread(SomeListener listener) {
mListener = listener;
}
public void run() {
/* do some work */
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mListener.onSomethingDone("New Text");
}
public void updateListener(SomeListener listener) {
mListener = listener;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startBackgroundWork();
}
public void startBackgroundWork() {
if (mWorkingThread == null || mWorkingThread.isFinished()) { // Use a boolean to know if it still running
mWorkingThread = new WorkingThread(new SomeListener() {
public void onSomethingDone(final Object result) {
mHandler.post(new Runnable() {
public void run() {
((TextView)findViewById(R.id.text)).setText((String)result);
//showMyDialog(result);
}
});
}
});
mWorkingThread.start();
} else {
mWorkingThread.updateListener(new SomeListener() {
public void onSomethingDone(final Object result) {
mHandler.post(new Runnable() {
public void run() {
((TextView)findViewById(R.id.text)).setText((String)result);
//showMyDialog(result);
}
});
}
});
}
}
But there is some improvments you could make :
The WorkingThread class should be static to avoid direct reference to the old activity : Java: Static vs non static inner class
Then make a reference to the current activity, and update it when it is recreated
Make a method for update of the text, instead of having the code directly in the listener
I'm coding a program which fetches the data from MySql from server (using JSON) and it updates the UI accordingly,
I'm fetching two types of data using AsyncTask from Server
1) Bubble Answers
2) Comments
The parseBubbleAnswers method successfully runs and Updates UI,
but parseComments class which is AsyncTask, and which call parseComments method in doInBackground, is not running runOnUiThread(new Runnable() { run() });
Can anyone help me in solving this
Here is my code :
public class FetchServer extends Activity
{
protected void onCreate(Bundle savedInstanceState)
{
String photoId = "1"; // photo id for which the data is fetched
checkBubbleData(photoId); // which call AsyncTask - 2 differnt calls
}
public void checkBubbleData(String photoId)
{
new parseBubbleAnswers().execute(photoId); // to fetch bubble answers
new parseComments().execute(photoId); // to fetch comments
}
class parseBubbleAnswers extends AsyncTask<String, Integer,String>
{
#Override
protected String doInBackground(String... params)
{
// TODO Auto-generated method stub
Looper.prepare();
parseBubbleAnswers(); // which has runOnUiThread(new Runnable() which updates (successfully !) the UI
return null;
}
}
class parseComments extends AsyncTask<String, Integer,String>
{
#Override
protected String doInBackground(String... params)
{
// TODO Auto-generated method stub
Looper.prepare();
String parseComReturn = parseComments();
if(parseComReturn=="end")
{
commentBuilder(); // which will update UI after fetch data by parseComments() method
}
}
}
public void commentBuilder()
{
runOnUiThread(new Runnable() // while debugging, it comes here, on Step Over it stick for 2 times and then move at the end of method without error
{
public void run()
{
// update UI code
}
});
}
}
Try this way :
First create one Handler :
Handler mHandler = new Handler();
Change this,
public void commentBuilder()
{
runOnUiThread(new Runnable() // while debugging, it comes here, on Step Over it stick for 2 times and then move at the end of method without error
{
public void run()
{
// update UI code
}
});
}
With,
public void commentBuilder()
{
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
while (isRunning) {
try {
// Thread.sleep(10000);
mHandler.post(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
// Write your code here to update the UI.
}
});
} catch (Exception e) {
// TODO: handle exception
}
}
}
}).start();
}
Stop thread by this once you are done with UI,
isRunning = false;
EDIT :
Try to Use Async Task in this way :
class parseComments extends AsyncTask<String, Integer,String>
{
protected String doInBackground(String... params) {
String parseComReturn = parseComments();
return parseComReturn;
}
protected void onPostExecute(String result) {
if(result.equals("end"))
{
commentBuilder();
}
}
}
Thanks.
runOnUiThread is a method of Activity, AsyncTask has no reference to Activity.
however, AsyncTask already runs on the UI thread and was designed to do exactly that.
just deal with the UI changes in onPostExecute.
I faced the similar issue.
Just pass the reference of the Activity class to the parseComments class.
class parseComments extends AsyncTask<String, Integer,String>{
Activity activity;
public parseComments(Activity activity){
this.activity = activity;
}
}
After that you can use runOnUiThread as
activity.runOnUiThread(new Runnable(){
#Override
public void run(){
}
});
It will only work with Activity class. Not Context class.
I need a timer which will send a message for its' handler. I've made a class that implements Runnable and I feed its' object to the thread runnable constructor. When I start the thread it hangs application and it obviously isn't working asyncroniously. I could've used AsyncTask but I've also heard that they're designed for short-term operations while my background timer must work throughout activity onResumed state. Would you mind pointing out my mistake and maybe giving useful links on the subject of threads in android. Thanks.
Here's the code I've written:
#Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
_myTimerInstance = new MyTimer(new Handler() {
#Override
public void dispatchMessage(Message msg) {
super.dispatchMessage(msg);
// ...
}
});
_myThread = new Thread(_myTimerInstance);
_myThread.run();
}
private static class MyTimer implements Runnable {
private Handler _myHandler;
private boolean _activityHasBeenLeft;
public MyTimer(Handler myHandler) {
_myHandler = myHandler;
}
public void setActivityHasBeenLeft(boolean b) {
_activityHasBeenLeft = b;
}
#Override
public void run() {
while (!_activityHasBeenLeft) {
try {
Thread.sleep(2000);
_myHandler.sendEmptyMessage(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
You should always use Thread.start() not Thread.run()
Thread.run() is like a normal method call and is run on the same thread.
use
_myThread.start();
I have a handler that I am using as follows:
handler.postDelayed(Play, 1000);
when my application onPause() is called before this is done, I need to pause it and tell it not to perform the "postDelayed" until I resume.
is this possible, or is there an alternative way?
My problem is that when onPause() is called I pause the audio (SoundManager), but if this handler.postDelayed is called after that, the audio will not be paused and will continue to play with my application in the background.
#Override
public void onPause()
{
Soundmanager.autoPause()
}
but then the postDelayed after 1000ms starts the audio playing again.
You need to subclass Handler and implement pause/resume methods as follows (then just call handler.pause() when you want to pause message handling, and call handler.resume() when you want to restart it):
class MyHandler extends Handler {
Stack<Message> s = new Stack<Message>();
boolean is_paused = false;
public synchronized void pause() {
is_paused = true;
}
public synchronized void resume() {
is_paused = false;
while (!s.empty()) {
sendMessageAtFrontOfQueue(s.pop());
}
}
#Override
public void handleMessage(Message msg) {
if (is_paused) {
s.push(Message.obtain(msg));
return;
}else{
super.handleMessage(msg);
// otherwise handle message as normal
// ...
}
}
//...
}
Have you tried with:
#Override
public void onPause()
{
handler.removeCallbacks(Play);
Soundmanager.autoPause()
}
Ger
Modifying the answer given by CpcCrunch. There handleMessage not worked for me, so instead of it using dispatchMessage. Note: Below code is written in Kotlin:
class CustomHandler: Handler() {
var s = Stack<Message>()
var is_paused = false
#Synchronized
fun pause() {
is_paused = true
}
#Synchronized
fun resume() {
is_paused = false
while (!s.empty()) {
sendMessageAtFrontOfQueue(s.pop())
}
}
override fun dispatchMessage(msg: Message?) {
if (is_paused) {
s.push(Message.obtain(msg))
return
} else {
super.dispatchMessage(msg)
}
}
}
public class YourActivity extends AppCompatActivity {
private static boolean handlerflag=false;
private Handler handler;
private Runnable runnable;
private int myind=0,index=0,count=0;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_activtiy);
//oncreate exe only
handlerflag=true;
handler = new Handler();
startyourtime(0);
}
private void startyourtime(int a) {
myind=0;
for (index=a; index<10 ;index++) {
myind++;
runnable=new Runnable() {
count++;
#Override
public void run() {
//your code here
}
};handler.postDelayed(runnable, Constants.TIME_LIMIT * myind);
}
#Override
protected void onPause() {
super.onPause();
handlerflag=false;
handler.removeCallbacksAndMessages(null);
}
#Override
protected void onResume() {
super.onResume();
if(!handlerflag)
{
startyourtime(count);
}
}
}
I came up with an alternative to CpnCrunch when wanting to pause/resume Runnables in a queue. To have methods that has been called whilst still connecting and is offline, once online, resume the queue and all runnables are executed.
Instead of using Handler, use ExecutorService:
public class ExecutorQueueService extends ThreadPoolExecutor {
private Stack<Runnable> runnables = new Stack<>();
private boolean paused = false;
public ExecutorQueueService() {
super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
public synchronized void pause() {
paused = true;
}
public synchronized void resume() {
paused = false;
while (!runnables.empty()) {
execute(runnables.pop());
}
}
public synchronized boolean isPaused() {
return paused;
}
#Override
public void execute(Runnable runnable) {
if (paused) {
runnables.push(runnable);
} else {
super.execute(runnable);
}
}
}
Using it is similar to Handler, but instead of post(runnable), use execute(runnable)