I am doing an Android service that gives content to other apps that can register as callback.
I am not 100% sure about how the Android Handler class works, so can someone confirm me that this code is thread safe?
public class MyService extends Service {
private static final String MESSAGE = "message";
private final RemoteCallbackList<IMyCallback> readerCallbacks = new RemoteCallbackList<IMyCallback>();
private static final int REPORT_MSG = 1;
private Thread readerThread;
#Override
public void onCreate() {
readerThread = new Thread(readerRunnable);
readerThread.setDaemon(true);
readerThread.start();
}
private Runnable readerRunnable = new Runnable() {
#Override
public void run() {
while (!Thread.interrupted()) {
// Blocking call
byte[] message = JniCommunicator.readMessage();
if (message == null || message.length == 0) {
continue;
}
Bundle b = new Bundle();
b.putByteArray(MESSAGE, message);
Message m = readHandler.obtainMessage(REPORT_MSG);
m.setData(b);
readHandler.sendMessage(m);
}
}
};
private final Handler readHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case REPORT_MSG:
byte[] message = msg.getData().getByteArray(MESSAGE);
// Broadcast the new message to all clients
final int N = readerCallbacks.beginBroadcast();
for (int i = 0; i < N; i++) {
try {
readerCallbacks.getBroadcastItem(i).newMessage(message);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
readerCallbacks.finishBroadcast();
break;
}
}
};
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IService.Stub mBinder = new IService.Stub() {
public void registerCallback(IMyCallback cb) {
if (cb != null)
readerCallbacks.register(cb);
}
public void unregisterCallback(IMyCallback cb) {
if (cb != null)
readerCallbacks.unregister(cb);
}
};
}
In particular, if someone calls unregisterCallback() while the Handler is in the for loop, will it crash?
From my understanding, the Handler run in the same thread, so it is thread safe, but I am not sure.
Thanks
Handlers are thread safe, that is their entire purpose.
I'll agree that the documentation on the thread safety of handlers isn't the best but it would be very ironic if a class designed to communicate between thread weren't thread safe.
About the remote callbacks, they are also designed to be thread safe, you should read the documentation on this, it states clearly:
Performs locking of the underlying list of interfaces to deal with multithreaded incoming calls, and a thread-safe way to iterate over a snapshot of the list without holding its lock
All you have to make sure is that all variables multiple thread access are thread safe (which they are in your case) and that they aren't being changed (yours are final so no worries there either)
Related
First, I extended IntentService to build my DonwloadService class. It was OK, but a download queue had been created and 2nd download had to wait for the 1st to finished etc.
So I decided to extend Service class (following the docs). I've just added download function and function for publishing results (listened by receiver in activity):
public class DownloadService extends Service
{
private static final String TAG = DownloadService.class.getSimpleName();
public static final String EXTRA_DOWNLOAD = "EXTRA_DOWNLOAD";
public static final String EXTRA_POSITION = "EXTRA_POSITION";
public static final String INTENT_NOTIFICATION = "INTENT_NOTIFICATION";
public static final String EXTRA_RESULT = "EXTRA_RESULT";
public static final String EXTRA_PROGRESS = "EXTRA_PROGRESS";
public static final String EXTRA_PATH = "EXTRA_PATH";
public static final String EXTRA_INDETERMINABLE = "EXTRA_INDETERMINABLE";
public static final int RESULT_PROGRESS = 123;
private int mResult = Activity.RESULT_CANCELED;
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler
{
public ServiceHandler(Looper looper)
{
super(looper);
}
#Override
public void handleMessage(Message message)
{
// Download file
download(message.getData());
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(message.arg1);
}
}
#Override
public void onCreate()
{
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread handlerThread = new HandlerThread("DownloadServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
handlerThread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = handlerThread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message message = mServiceHandler.obtainMessage();
message.setData(intent.getExtras());
message.arg1 = startId;
mServiceHandler.sendMessage(message);
return START_REDELIVER_INTENT;
}
#Nullable
#Override
public IBinder onBind(Intent intent)
{
// No binding provided
return null;
}
#Override
public void onDestroy()
{
Log.d(TAG, "Service done");
}
/**
* Handles file download
*
* #param bundle
*/
private void download(Bundle bundle)
{
if (bundle != null) {
return;
}
Download download = bundle.getParcelable(EXTRA_DOWNLOAD);
int position = bundle.getInt(EXTRA_POSITION, -1);
File downloadedFile = new File(Environment.getExternalStorageDirectory(), position + ".jpg");
if (downloadedFile.exists()) {
downloadedFile.delete();
}
FileOutputStream fileOutputStream = null;
int filesize = -1;
try {
fileOutputStream = new FileOutputStream(downloadedFile.getPath());
URL url = new URL(download.getUrl());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
filesize = connection.getContentLength();
InputStream reader = new BufferedInputStream(connection.getInputStream());
byte data[] = new byte[1024];
int next = -1;
int total = 0;
while ((next = reader.read(data)) != -1) {
mResult = RESULT_PROGRESS;
total += next;
publishResult(downloadedFile.getAbsolutePath(),
(filesize > 0) ? Math.round(total * 100 / filesize) : Math.round(total / 1024),
position, filesize <= 0);
fileOutputStream.write(data, 0, next);
}
mResult = Activity.RESULT_OK;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
publishResult(downloadedFile.getAbsolutePath(), 100, position, filesize <= 0);
}
private void publishResult(String downloadPath, int progress, int positionInList,
boolean indeterminable)
{
Intent intent = new Intent(INTENT_NOTIFICATION);
intent.putExtra(EXTRA_PATH, downloadPath);
intent.putExtra(EXTRA_PROGRESS, progress);
intent.putExtra(EXTRA_POSITION, positionInList);
intent.putExtra(EXTRA_INDETERMINABLE, indeterminable);
intent.putExtra(EXTRA_RESULT, mResult);
sendBroadcast(intent);
}
}
But still there is a download queue, no parallel downloads.
If you want to run a task repeatedly on different sets of data, but you only need one execution running at a time, an IntentService suits your needs. To automatically run tasks as resources become available, or to allow multiple tasks to run at the same time (or both), you need to provide a managed collection of threads. To do this, use an instance of ThreadPoolExecutor, which runs a task from a queue when a thread in its pool becomes free. To run a task, all you have to do is add it to the queue.
Reference:
Creating a Manager for Multiple Threads
I'm using HandlerThread to handle threading in Android,
public class HandlerTest extends HandlerThread {
private static final int MESSAGE_TYPE0 = 0;
private static final String TAG = "TAG";
Handler mHandler;
public interface Listener {
void onHandlerTestDone(String str);
}
#SuppressLint("HandlerLeak")
#Override
protected void onLooperPrepared() {
Log.i(TAG, "OnLoopPrep");
mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (msg.what == MESSAGE_TYPE0) {
#SuppressWarnings("unchecked")
String msgObj = (String) msg.obj;
handleRequest(msgObj);
}
}
};
}
private void handleRequest(final String token) {
final String str = token;
try {
this.sleep(5000, 0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
handleMessage(token);
}
public void clearQueue() {
mHandler.removeMessages(MESSAGE_TYPE0);
}
}
I have two activities, Activity 1 calls Activity 2, then On activity 2 I do this
HandlerTest hlrtest;
protected void onCreate(Bundle savedInstanceState) {
hlrtest.start();// start looper
hlrtest.getLooper();
hlrtest.PrepMessage("test1"); //will be handled by the thread then
//the thread will go to sleep for 5 second
hlrtest.PrepMessage("test2"); //will be queued
hlrtest.PrepMessage("test3"); //will be queued
hlrtest.PrepMessage("test4"); //will be queued
//Now quit this activity and go back to Activity 1
#Override
public void onDestroy() {
super.onDestroy();
hlrtest.clearQueue();
hlrtest.quit();
}
}
As you can see I make the thread sleep for 5 seconds to simulate that it's getting busy for that amount of time. when I send 4 requests and then I go back to Activity 1 the thread will handle only the first request and the queue will get cleared and the thread will exit as onDestroy() will do that after going back to Activity 1.
If I don't call clearQueue() and quit() in the destroy I will end up with a zombie thread.
How can I send many requests to the thread and I want the thread to handle them all and then quit when the queue is empty?
please note that I don't want to use quitSafely() as it's only supported from sdk 18 and above
You could create another message type that signals the Handler to clean up and quit. Perhaps something like this (untested) code for handleMessage():
#Override
public void handleMessage(Message msg) {
if (msg.what == MESSAGE_TYPE0) {
#SuppressWarnings("unchecked")
String msgObj = (String) msg.obj;
handleRequest(msgObj);
} else if (msg.what == MESSAGE_TYPE_FINISH) {
mHandler.clearQueue();
mHandler.quit();
}
};
I am quite new to Android and Java. Basically, I would like to realize an encapsulation of a background thread of Android, and inside this background thread, I have an infinite loop which will periodically take some operations of getting data(like from Internet, or from some hardware devices).
The encapsulated class must provide only a function like getData() for others to get data. But everytime when i call this getData() function from other classes, it never gives me the changing values, but only the initialized values.
I've studied both of the AsyncTask, Handler and Message ways to realize multithread. And both of them give me the initialized values.
Here is the encapsulated class of Handler and Message:
public class getDataFromUSB{
private int usb_data;
private Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==1){
usb_data=msg.arg1;
}
}
};
private Thread thread = new Thread(){
#Override
public void run(){
while(!Thread.currentThread().isInterrupted())
{
int a=read_usb();
Message msg = new Message();
msg.arg1 = a;
msg.what = 1;
handler.sendMessage(msg);
try {
Thread.sleep(15);
} catch (InterruptedException e) {
System.err.println("");
this.interrupt();
}
}
}
};
public void start(){
thread.start();
}
public int get_data(){
return usb_data;
}
public int read_usb()
{
int a=10;
return a;
}
}
And then in another class, here is the code of calling getDataFromUSB:
getDataFromUSB usb1= new getDataFromUSB();
usb1.start();
int a=usb1.getData();
Log.e(TAG,"a = " +a);
Then everytime i call this usb1.getData(), the value is always 0. I don't understand why.
Now I proceed to do some more realistic things. I add an object of random in my getDataFrom USB class to provide different numbers, I also change the way of assigning values to usb_data, I think it's better to do it just in the background thread, there is no need to move it to the handlemessage. So it becomes:
public class getDataFromUSB{
private int usb_data;
private Random random = new Random(555L);
private Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
private Thread thread = new Thread(){
#Override
public void run(){
while(!Thread.currentThread().isInterrupted())
{
int a=read_usb();
usb_data=a;
Message msg = new Message();
handler.sendMessage(msg);
}
}
};
public void start(){
thread.start();
}
public int get_data(){
return usb_data;
}
public int read_usb()
{
return random.nextInt();
}
}
Then I call it from another class like what Nikita suggested:
Handler h = new Handler();
for (int i=0;i<20;i++){
h.postDelayed(new Runnable() {
public void run() {
int data=usb1.get_data();
Log.e(TAG,"data= " +data);
}
},500);
}
The strange thing is that it then gives sometimes all the same numbers, sometimes several some numbers, sometimes all different numbers. As I understand, the usb_data has always been changed inside the background thread, so we are not obliged to wait for handlemessage to proceed. Everytime I call getData(), it should give me the newest value. Isn't that right?
The problem might be following: you run your code from main UI thread. When you new start thread - it sends message to handler. This message is added to main thread's queue and will be processed in main thread when it finishes it's current job. Currently main thread runs your code and there is no chance that message will be processed before you call usb.getData().
To check whether your update thread works properly you can post delayed runnable that will print value of usb1.getData():
final getDataFromUSB usb1= new getDataFromUSB();
usb1.start();
Handler h = new Handler();
h.postDelayed(new Runnable() {
public void run() {
int a=usb1.getData();
Log.e(TAG,"a = " +a);
}
}, 500); // Waits 500 milliseconds and runs runnable on current thread.
I'm developing an Android application.
This application will have a server to start a DatagramSocket as a server. It will wait for incoming message. When the socket get a message I will process it.
To start a UDP Server socket I'm going to use a Local Service. This service will have a worker thread where I'm going to listen to incoming messages.
This is my unfinished Local Service implementation:
public class UDPSocketBackgroundService extends Service
{
private static final String TAG = "UDPSocketBackgroundService";
private ThreadGroup myThreads = new ThreadGroup("UDPSocketServiceWorker");
private Handler mServiceHandler;
#Override
public void onCreate()
{
super.onCreate();
Log.v(TAG, "in onCreate()");
}
#Override
public IBinder onBind(Intent arg0)
{
try
{
new Thread(myThreads, new UDPServerThread("X", 8888)).start();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
And this is my also unfinished Worker Thread implementation:
public class UDPServerThread extends Thread
{
private static final int MESSAGE_SIZE = 256;
protected DatagramSocket socket = null;
protected boolean end = false;
public UDPServerThread(String serverName, int port) throws IOException
{
super(serverName);
socket = new DatagramSocket(port);
}
public void run()
{
while (!end)
{
try
{
byte[] buf = new byte[MESSAGE_SIZE];
// Wait an incoming message.
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
// TODO: Notify Service with packet received
}
catch (IOException e)
{
// TODO Mensaje de error.
e.printStackTrace();
}
}
}
}
Those classes have their own file (they are on different files).
Here:
socket.receive(packet);
//TODO: Notify Service with packet received
How can I notify service that we have received a packet? I want to send to service that packet also.
Here there is an example on how to communicate from Main thread to worker thread. But, I don't need that, I'm looking for an example on how to communicate from worker thread to service.
I've found this example, but I don't understand it very well because on that example both classes are declare it on the same file.
As you can see, I'm a newbie on Android development.
If you know a better approach, please tell me.
When you create the UDPServerThread, you could pass in a reference to the UDPSocketBackgroundService and then call a method on it (processPacket() for example) when packets are received. This processPacket() method will need to use some sort of synchronization.
Here's a small code excerpt of the related functions:
public class UDPSocketBackgroundService extends Service
{
....
#Override
public IBinder onBind(Intent arg0)
{
try
{
new Thread(myThreads, new UDPServerThread(this, "X", 8888)).start();
// Notice we're passing in a ref to this ^^^
}
...
}
public void processPacket(DatagramPacket packet)
{
// Do what you need to do here, with proper synchronization
}
}
public class UDPServerThread extends Thread
{
private static final int MESSAGE_SIZE = 256;
protected DatagramSocket socket = null;
protected boolean end = false;
protected UDPSocketBackgroundService = null;
public UDPServerThread(UDPSocketBackgroundService service, String serverName, int port) throws IOException
{
super(serverName);
this.service = service;
socket = new DatagramSocket(port);
}
...
public void run()
{
while (!end)
{
try
{
byte[] buf = new byte[MESSAGE_SIZE];
// Wait an incoming message.
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
service.processPacket(packet);
}
...
}
...
}
}
Notice that going this approach, the UDPSocketBackgroundService is now "tightly coupled" with the UDPServerThread. Once you get this working, you may consider refactoring it with a more elegant design where there is less coupling, but for now this should get you going :)
I am implementing an application which is kind of VOIP application. So my application is kind of network application. Now I want to implement two part in my application, one is GUI part and one is network part. My GUI part will just contain activities and handling of user interaction. My Network part should handle all network related activities like handling incoming network data and sending data to network based on GUI interaction. Now whenever there is any incoming data, I want to update some activity whose reference is not there in Network module. So what could be the best way to update activity from some other class? In my case some other class is my Network class. So in short I would like to ask that what should be the architecture in such scenario? i.e. Network part should run in separate thread and from there it should update GUI?
Depending on the type/size of data you need to send to the activity, you can use one of a number of options.
Use one of the methods described here.
Use a BroadcastReceiver: register it in the Activity and then fire off matching Intents in the Service that handles the networking code.
Make your Activity bind to your Service and then pass in a Handler that you send Messages to.
I have written apps like this, and I prefer the Handler method. In fact I have written an Abstract Activity class to do all the hard work and simply extend it in any activity that want to be notified of a change.
To Use the following code, just get your Activity to extend UpdatableActivity and override the dataUpdated() method. This method is called when your Service notifies the handler that data has been updated. In the Service code put your code to do an update in the update() method (Or modify to call your existing code). This allows an activity to call this.updateService() to force an update. The service can call the sendMessageToUI() method to notify all interested activities that the data has been updated.
Here is what the abstract activity looks like:
public abstract class UpdatableActivity extends Activity {
public static final String TAG = "UpdatableActivity (Abstract)";
private final Messenger mMessenger = new Messenger(new IncomingHandler());
private Messenger mService = null;
private boolean mIsBound;
protected class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
if (Constants.LOG_DEBUG) Log.d(TAG, "Service has notified us of an update: ");
switch (msg.arg1) {
case UpdateService.MSG_DATA_UPDATED:
dataUpdated();
break;
default: super.handleMessage(msg);
}
}
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
try {
Message msg = Message.obtain(null, UpdateService.MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
// In this case the service has crashed before we could even do anything with it
}
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been unexpectedly disconnected - process crashed.
mService = null;
}
};
/**Override this method in you acctivity to handle the update */
public abstract void dataUpdated();
void doBindService() {
if (Constants.LOG_DEBUG) Log.d(TAG, "Binding to service...");
bindService(new Intent(this, UpdateService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
// If we have received the service, and hence registered with it, then now is the time to unregister.
if (mService != null) {
try {
Message msg = Message.obtain(null, UpdateService.MSG_UNREGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
// There is nothing special we need to do if the service has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
public void updateService() {
if (Constants.LOG_DEBUG) Log.d(TAG,"Updating Service...");
if (mIsBound) {
if (mService != null) {
try {
Message msg = Message.obtain(null, UpdateService.MSG_SET_INT_VALUE, UpdateService.MSG_DO_UPDATE, 0);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
if (Constants.LOG_ERROR) Log.e(TAG,Log.getStackTraceString(e));
}
}
} else {
if (Constants.LOG_DEBUG) Log.d(TAG, "Fail - service not bound!");
}
}
pu
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.doBindService();
}
#Override
protected void onDestroy() {
super.onDestroy();
try {
doUnbindService();
} catch (Throwable t) {
if (Constants.LOG_ERROR) Log.e(TAG, "Failed to unbind from the service", t);
}
}
}
And here is what the Service looks Like:
public class UpdateService extends Service {
public static final String TAG = "UpdateService";
public static final int MSG_DATA_UPDATED = 0;
public static final int MSG_REGISTER_CLIENT = 1;
public static final int MSG_UNREGISTER_CLIENT = 2;
public static final int MSG_DO_UPDATE = 3;
public static final int MSG_SET_INT_VALUE = 4;
private static boolean isRunning = false;
private Handler handler = new IncomingHandler();
private final Messenger mMessenger = new Messenger(handler);
private ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
#Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
class IncomingHandler extends Handler { // Handler of incoming messages from clients.
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
case MSG_SET_INT_VALUE:
switch (msg.arg1) {
case MSG_DO_UPDATE:
if (Constants.LOG_DEBUG) Log.d(TAG,"UI has asked to update");
update();
break;
}
break;
default:
super.handleMessage(msg);
}
}
}
private void sendMessageToUI() {
if (Constants.LOG_DEBUG) Log.d(TAG, "Notifying "+mClients.size()+" UI clients that an update was completed");
for (int i=mClients.size()-1; i>=0; i--) {
try {
// Send data as an Integer
mClients.get(i).send(Message.obtain(null, MSG_SET_INT_VALUE, MSG_DATA_UPDATED, 0));
} catch (RemoteException e) {
// The client is dead. Remove it from the list; we are going through the list from back to front so this is safe to do inside the loop.
mClients.remove(i);
}
}
}
public static boolean isRunning()
{
return isRunning;
}
#Override
public void onCreate() {
super.onCreate();
isRunning = true;
if (Constants.LOG_DEBUG) Log.d(TAG, "Service Started");
update();
}
#Override
public void onDestroy() {
if (Constants.LOG_DEBUG) Log.d(TAG, "Service Destroyed");
isRunning = false;
}
private void update() {
/**Your code to do an update goes here */
}
}
Yes, personally i think that the network and UI should be in separate threads. The way I tend to communicate between the two, which is probably not the recommended proper way, but it works for me, is to create a global variable in your application class. hope this helps a little
I would directly post to the main UI thread,
Handler mHandler = new Handler(Looper.getMainLooper());
mHandler.post(new Runnable() {...});