I wrote the below code to better understand the Handler and the Looper. I would liek to know how can I quit the Looper on occurence of a specific condition
for example, when a counter reaches a specific limit.
In the below code, I want to call .quit() on the looper of the Thread T when "what" is equal to 10.
According to the code i wrote below, even when the contents of "what" exceeds 10, the "handleMessage" method is getting called...i expected that when .quit()
is called, the "handleMessage" will no longer be called.
please let me know how can I properly quit a Looper.
app.gradle
public class ActMain extends AppCompatActivity {
private static final String TAG = ActMain.class.getSimpleName();
private Handler mHandler = null;
private Button mBtnValues = null;
private int i = -1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_act_main);
this.mBtnValues = findViewById(R.id.btnValues);
this.mBtnValues.setOnClickListener(x-> {
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putString("what", "" + i++);
Log.d(TAG, "i: " + i);
msg.setData(bundle);
mHandler.sendMessage(msg);
});
new T().start();
}
private class T extends Thread {
private String str = "";
public T() {}
#Override
public void run() {
super.run();
Log.d(TAG, "run method started");
Looper.prepare();
Log.d(TAG, "beginning of the looped section");
final String[] cnt = {""};
mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String what= msg.getData().getString("what");
str += what;
Log.d(TAG, "new str: " + str);
}
};
Log.d(TAG, "end of the looped section");
if (i == 10) {
Looper.myLooper().quit();
}
Looper.loop();
}
}
}
Related
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();
}
};
}
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 trying to create an example to know how Handlers work. As shown in the code belwo, I created a workerThread that runs for ever and increment the variable i. within the
run() method, i want to pass the value of the incremented variable to a TextView as shown in the body of the Handler class.
The problem is, the line in which there is "new Handler()" and "handler.sendMessage(m)" are marked with red
please let me know how to solve this problem
MainActivity
public class MainActivity extends AppCompatActivity {
private final static String TAG = MainActivity.class.getClass().getSimpleName();
private TextView mtvIncr = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.mtvIncr = (TextView) findViewById(R.id.tvValue);
new WorkerThread().start();
}
Handler handler =new Handler() {//ERROR
#Override
public void handleMessage(Message msg) {
int sentInt = msg.getData().getInt("what");
mtvIncr.setText("r:"+Integer.toString(sentInt));
}
};
class WorkerThread extends Thread {
private int i = 0;
android.os.Handler mHandler = null;
#Override
public void run() {
super.run();
Looper.prepare();
while(true) {
Log.d(TAG, SubTag.bullet("WorkerThread", "i: " + (i++)));
SystemClock.sleep(1000);
Message m = new Message();
Bundle b = new Bundle();
b.putInt("what", i); // for example
m.setData(b);
handler.sendMessage(m);//ERROR
}
}
}
}
Use Message.obtain() to get a Message object
While the constructor of Message is public, the best way to get one of
these is to call Message.obtain() or one of the
Handler.obtainMessage() methods, which will pull them from a pool of
recycled objects.
And use obj attribute msg.obj = b;
obj: An arbitrary object to send to the recipient.
Info from:
https://developer.android.com/reference/android/os/Message.html
Example:
Handler handler =new Handler() {
#Override
public void handleMessage(Message msg) {
int sentInt = ((Bundle)msg.obj).getInt("what");
mtvIncr.setText("r:"+Integer.toString(sentInt));
}
};
class WorkerThread extends Thread {
private int i = 0;
android.os.Handler mHandler = null;
#Override
public void run() {
super.run();
Looper.prepare();
while(true) {
Log.d(TAG, SubTag.bullet("WorkerThread", "i: " + (i++)));
SystemClock.sleep(1000);
Message msg = Message.obtain();
Bundle b = new Bundle();
b.putInt("what", i); // for example
msg.obj = b;
handler.sendMessage(msg);
}
}
}
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();
}