I have the following scenario: a foreground service spawns 2 HandlerThreads (A and B). From time to time A must send some data to B, B performs a certain action on this data and sends back to A the result of the operation. I do not know how to effectivelly pass data between these 2 threads. Originally I was posting messages to the MainService thread (via handlers), which in turn posted it to the appropriate thread. But it seems like a bit overkill:
(thread A) post message to main thread handler
(main thread) receive the message and post it to the thread B handler
(thread B) do something and post the result back to the main thread handler
(main thread) receive the message and post it to the thread A handler
(thread A) do something
I thought that I could use LocalBroadcastManager, but it seems to be deprecated now. Do you have any suggestion on that?
EDIT 1
Simplified version of the program. I am afraid that the showed code will get too much complex after adding more signals between threads.
MainService.java
public class MainService extends Service {
private static final String TAG = "MainService";
public static final Integer START_A = 0;
public static final Integer DO_STH_IN_B = 1;
public static final Integer RESPOND_FROM_B_TO_A = 2;
private Handler mServiceHandler = new Handler(Looper.getMainLooper()) {
// this is a dispatcher for messages between threads
#Override
public void handleMessage(#NonNull Message msg) {
super.handleMessage(msg);
Message msg_s;
if (msg.what == DO_STH_IN_B)
msg_s = Message.obtain(myThreadB.getHandler());
else if (msg.what == RESPOND_FROM_B_TO_A)
msg_s = Message.obtain(myThreadA.getHandler());
else return;
msg_s.what = msg.what;
msg_s.sendToTarget();
}
};
private MyThreadA myThreadA = new MyThreadA(mServiceHandler);
private MyThreadB myThreadB = new MyThreadB(mServiceHandler);
#Override
public void onCreate() {
super.onCreate();
myThreadA.start();
myThreadB.start();
SystemClock.sleep(100);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification =
new NotificationCompat.Builder(this, "M_ID")
.setContentTitle("Notification Title")
.setContentText("Notification Text")
.setSmallIcon(R.drawable.ic_android)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
Message msg = Message.obtain(myThreadA.getHandler());
msg.what = START_A;
msg.sendToTarget();
//stopSelf();
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
myThreadA.quit();
myThreadB.quit();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
MyThreadA.java
public class MyThreadA extends HandlerThread {
private static final String TAG = "MyThreadA";
private Handler mHandler;
private Handler mMainServiceHandler;
public MyThreadA(Handler handler) {
super(TAG, Process.THREAD_PRIORITY_BACKGROUND);
mMainServiceHandler = handler;
}
#SuppressLint("HandlerLeak")
#Override
protected void onLooperPrepared() {
mHandler = new Handler() {
#Override
public void handleMessage(#NonNull Message msg) {
if (msg.what == MainService.START_A) {
mHandler.post(myRoutine);
} else if (msg.what == MainService.RESPOND_FROM_B_TO_A) {
mHandler.post(myRoutineRespond);
}
}
};
}
public Handler getHandler() { return mHandler; }
private Runnable myRoutine = new Runnable() {
#Override
public void run() {
Log.d(TAG, "run: [thread A] myRoutine");
// do something here (may be blocking)
SystemClock.sleep(1000);
// send message to Thread B (by passing it through the main service)
Message msg = Message.obtain(mMainServiceHandler);
msg.what = MainService.DO_STH_IN_B;
msg.sendToTarget();
// repeat the routine
mHandler.post(myRoutine);
}
};
private Runnable myRoutineRespond = new Runnable() {
#Override
public void run() {
Log.d(TAG, "run: [thread A] respond handler");
// do something here with the respond from B
SystemClock.sleep(2000);
}
};
}
MyThreadB.java
public class MyThreadB extends HandlerThread {
private static final String TAG = "MyThreadB";
private Handler mHandler;
private Handler mMainServiceHandler;
public MyThreadB(Handler handler) {
super(TAG, Process.THREAD_PRIORITY_BACKGROUND);
mMainServiceHandler = handler;
}
#SuppressLint("HandlerLeak")
#Override
protected void onLooperPrepared() {
mHandler = new Handler() {
#Override
public void handleMessage(#NonNull Message msg) {
if (msg.what == MainService.DO_STH_IN_B) {
mHandler.post(myRoutine);
}
}
};
}
public Handler getHandler() { return mHandler; }
private Runnable myRoutine = new Runnable() {
#Override
public void run() {
Log.d(TAG, "run: [thread B] do something and respond");
// do something here (may be blocking)
SystemClock.sleep(2000);
// send respond back to Thread A
Message msg = Message.obtain(mMainServiceHandler);
msg.what = MainService.RESPOND_FROM_B_TO_A;
msg.sendToTarget();
}
};
}
Related
I want to create another thread to loop itself and perform something.
May I know any mistake I done? Because it just perform single time and stop.
public class LooperClazz extends Thread {
private MessageQueue messageQueue;
private Context context;
private long counter = 0;
public LooperClazz(Context context){
this.context = context;
}
#Override
public void run() {
Looper.prepare();
long threadId1 = Thread.currentThread().getId(); //new thread
final Handler responseHandler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
long mainThreadId = Thread.currentThread().getId(); //1
//Here success to Toast something.
}
};
messageQueue = Looper.myQueue();
messageQueue.addIdleHandler(new MessageQueue.IdleHandler() {
#Override
public boolean queueIdle() {
//I'm expecting running this in a loop, but it not, why?
long threadId2 = Thread.currentThread().getId();
//Same as threadId1
counter++;
Message msg = new Message();
msg.obj = counter+ "";
responseHandler.sendMessage(msg);
SystemClock.sleep(3000);
return true;
}
});
Looper.loop();
}
}
In MyApplication, I just start it with
new LooperClazz(context).start();
I am working on an Android Application which have an one activity class and service class. In service, Continuous bulk data (1090 bytes) will be received every 10 milliseconds. I need to update the text view continuously with these bulk data. What is recommended way to update Text view from a continuous background service?
Service Class
public class RecepService extends Service {
public static Handler mHandler;
StringBuilder hexstring;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
init();
}
private void init() {
mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (msg.what == 0x123) {
byte[] readBuf = (byte[]) msg.obj;
int readBuflen = msg.arg1;
// here will receive 1090 bytes of data
// every 10 milliseconds
Receivepatientattributes(readBuf,readBuflen);
}
}
};
}
public void Receivepatientattributes(byte[] readBuf, int len) {
String total_data = "";
total_data = bytetohex(readBuf, len);
MainActivity.recep.setText(MainActivity.recep.getText().toString() + "\t" +
"" + total_data );
}
String bytetohex(byte[] txt, int len) {
String p="";
byte[] text = new byte[len];
text = txt;
hexstring = new StringBuilder();
for (int j = 0; j < len; j++) {
String hex= Integer.toHexString(0xFF & txt[j]);
if (hex.length()==1) {
hexstring.append("0");
}
hexstring.append(hex+" ");
}
p=p+hexstring.toString();
return p;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
}
If you want to use Schedule and timer task then you can See My Answer
To solve current issue follow this bellow instructions.
Suppose your activity has a Broadcast Receiver
private BroadcastReceiver mReceiver;
Then you override methods onResume() where your broadcast receiver will be registered and also onPause() where will your receiver be unregistered:
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
IntentFilter intentFilter = new IntentFilter(
"android.intent.action.MAIN");
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//extract your message from intent
String msg_for_me = intent.getStringExtra("YOUR_MESSAGE");
//log your message value
Log.i("MyTag", msg_for_me);
}
};
//registering your receiver
this.registerReceiver(mReceiver, intentFilter);
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
//unregister your receiver
this.unregisterReceiver(this.mReceiver);
}
Here the broadcast receiver is filtered via android.intent.action.MAIN and from Service the message will BroadCast using this filter
Now your Method Receivepatientattributes will like this :
public void Receivepatientattributes(byte[] readBuf, int len) {
String total_data = "";
total_data = bytetohex(readBuf, len);
Intent i = new Intent("android.intent.action.MAIN").putExtra("YOUR_MESSAGE", total_data);
this.sendBroadcast(i);
}
Thats it. :)
User LocalBroadcastManager
public void Receivepatientattributes(byte[] readBuf, int len) {
String total_data = "";
total_data = bytetohex(readBuf, len);
Intent intent = new Intent("update-text");
// add data
intent.putExtra("message", total_data);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
In MainActivity
#Override
public void onResume() {
super.onResume();
// Register mMessageReceiver to receive messages.
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("update-text"));
}
private boolean mCanBeUpdated = true;
private static final int ONE_SEC = 1000; //ms
private static final int RECEPTION_SPEED = 10; //ms
private static final int CYCLES = (int) (ONE_SEC / RECEPTION_SPEED);
private int mCurrentCycle = -1;
private String mMsgCache = "";
// handler for received Intents for the "update-text" event
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Extract data included in the Intent
String message = intent.getStringExtra("message");
Log.d("receiver", "Got message: " + message);
mMsgCache = mMsgCache + "\t" + message;
if (mCanBeUpdated) {
// No problem updating UI here, refer --> http://stackoverflow.com/a/5676888/1008278
final Handler handler = new Handler(context.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
MainActivity.recep.append(mMsgCache);
mMsgCache = "";
}
});
mCanBeUpdated = false;
} else if (mCurrentCycle >= CYCLES) {
mCurrentCycle = -1;
mCanBeUpdated = true;
} else {
mCurrentCycle++;
}
}
};
#Override
protected void onPause() {
// Unregister since the activity is not visible
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
super.onPause();
}
Reference
You can use Timer for Continously updating your textview.
Set value in preferences every time when your service is running with the latest value.
Now in Timer get that value from preferences and update your TextView with that value.
Here is some code :
class UpdateTimeTask extends TimerTask {
public void run() {
textview.setText("updated value");
}
}
Set in onCreate ();
Timer timer = new Timer();
UpdateTimeTask UpdateTimeTask = new UpdateTimeTask ();
timer.schedule(UpdateTimeTask, 1000);
Use handler beacuse A Handler allows communicating back with UI thread from other background thread.
boolean handlerStop = false;
void handleHandler(){
Handler handler =new Handler();
final Runnable r = new Runnable() {
public void run() {
handler.postDelayed(this, 30000);
if(!handlerStop) {
updateTextView() //update your text with other thread like asyncronous thread
}
}
};
handler.postDelayed(r, 0000);
}
#Override
public void onResume() {
super.onResume();
handlerStop=false;
handleHandler();
}
#Override
public void onPause() {
super.onPause();
handlerStop=true;
handleHandler();
}
#Override
public void onStop() {
super.onStop();
handlerStop=true;
handleHandler();
}
I believe I'm not understanding how Handler can pass/receive messages.
I have a handler created on a different thread, and then have the main thread send it a message. Here is how I did it
public void startAniHandlerThread(int timer){
final int sleeptime = timer;
int test2;
Thread thread1 = new Thread(){
public void run(){
Looper.prepare();
Log.v("loop","looping");
Handler handler = new Handler(){
#Override
public void handleMessage(Message inputMessage) {
Log.v("Thread handler", "checking");
if(inputMessage.what == 10) {
if (inputMessage.arg1 == 2) {
Log.v("Thread handler", "stop");
} else if (inputMessage.arg1 == 1) {
Log.v("Thread handler", "continue");
} else{
Log.v("nothing", "nothing");
}
}
}
};
Looper.loop();
}
};
thread1.start();
}
Here is how I attempted to send a message to it from the UI thread:
public void stopAniHandlerThread(){
Handler newHandler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
return false;
}
});
Runnable newRunable = new Runnable() {
#Override
public void run() {
Handler handler = new Handler();
Message message = new Message();
message.what = 10;
message.arg1 = 2;
handler.sendMessage(message);
}
};
newHandler.postDelayed(newRunable, 2000);
}
Here is the Log I get(I'm expected this to get called, but it doesn't:
Log.v("Thread handler", "stop");
V/stopAniHandlerThread﹕ posting
V/loop﹕looping
i ahve a problem with a thread handler receiving a message. all other thread i implemnted this pattern works fine. here my code:
Start thread
InternalScoresThread t = new InternalScoresThread(
this.game.getApplicationContext(),
this.map.fileName, this.map.getCurrentTime(),
new Handler() {
#Override
public void handleMessage(Message msg) {
Log.d("DEBUG", "message received");
if (msg.getData().getBoolean("record")) {
Player.this.showtRecordMessage();
} else {
Player.this.showtFinishMessage();
}
Player.this.showTimeMessage();
Player.this.showRestartMessage();
}
});
t.start();
Thread class
public class InternalScoresThread extends Thread {
private Handler handler;
private String map;
private float time;
private Context context;
public InternalScoresThread(Context context, String map, float time, Handler handler) {
this.context = context;
this.handler = handler;
this.map = map;
this.time = time;
}
#Override
public void run() {
Log.d("DEBUG", "thread started");
Database db = Database.getInstance(this.context);
float bestTime = db.getBestTime(this.map);
db.addRace(this.map, this.time);
Log.d("DEBUG", "race added");
Message msg = new Message();
Bundle b = new Bundle();
b.putBoolean("record", this.time < bestTime || bestTime == 0);
msg.setData(b);
this.handler.sendMessage(msg);
Log.d("DEBUG", "message sent");
}
}
The "thread started, "race added" and "message sent" logs appear in logcat, but not the "message received" in the handler.
well, I dunno why, but dispatchMessage() instead of sendMessage() solved the problem...
I know this is an old question, but Google.
The problem is that you created the Handler in the UI thread. It then receives messages on that thread. You need to create the Handler in the new thread:
public void run() {
Log.d("DEBUG", "creating Handler in thread " + Thread.currentThread().getId());
Looper.prepare();
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
Log.d("DEBUG", "message received");
}
};
Looper.loop();
Implementing a Thread by providing a new class that extends Thread and overriding its run() method is new to me. I've tried all day to get it to work. Here's my code:
/*
* see http://developer.android.com/reference/java/lang/Thread.html
*/
class threadClass extends Thread {
private Handler mHandler;
private Message mMsg;
// constructor
public threadClass(Handler handler, Message msg) {
// do something like save the Handler reference
mHandler = handler;
mMsg = msg;
}
#Override
public void run() {
// do some background processing, call the Handler?
mHandler.sendMessage(mMsg);
}
}
public Thread passHandlerToThread(Handler handler) {
handler.sendEmptyMessage(10);
Message msg = Message.obtain();
msg.what = 10;
Thread thread = new threadClass(handler, msg);
return thread;
}
private Handler localHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
int what = msg.what;
if (what == 10) Log.i("localHandler", "what: " + what);
}
};
public void startThread() {
Thread thread = passHandlerToThread(localHandler);
thread.start();
}
I call startThread() in my LocalService onCreate() but nothing happens. What am I doing wrong? I was expecting localHandler() to be called twice: once in passHandlerToThread() and again in run().
Do something like this:
private final Handler handler = new Handler();
// toast runnables
final Runnable updateTwitterNotification = new Runnable() {
#Override
public void run() {
dismissProgressSpinner();
Toast.makeText(getBaseContext(), "Tweet sent!", Toast.LENGTH_LONG).show();
}
};
final Runnable updateCreateError = new Runnable() {
#Override
public void run() {
Toast.makeText(getBaseContext(), "Tweet error!", Toast.LENGTH_LONG).show();
}
};
postMessageInThread();
//implementation:
private void postMessageInThread() {
Thread t = new Thread() {
#Override
public void run() {
try {
connectToTwitterService() // or whatever
handler.post(updateTwitterNotification);
} catch (Exception ex) {
Log.e(TAG, "Error sending msg", ex);
handler.post(updateCreateError);
}
}
};
t.start();
}