is the order of a broadcast intent guaranteed? that is, if i do,
sendBroadcast(intent1);
sendBroadcast(intent2);
are the receivers guaranteed to get intent1 before intent2? i suspect the answer to this is no, but in that case, i'm not quite sure how to solve my problem.
i'm trying to create a "busy" indicator for my app that shows busy when the device is talking on the network, and then goes away when the network communication is done. all network communication happens in an intent service.
my attempt at this was to send a BUSY_START intent when i begin network communication in the service, and a BUSY_STOP when network communication ends. this seems to mostly work, but i'm finding occasionally that i get the stop and start messages out of order.
is there a better way to solve this problem?
i'm thinking of adding an ID to each busy intent, so they can be paired. that way if i receive a start for which i've already received a stop, i can ignore it. or, perhaps more simply, add an integer sequence number into each broadcast. if i ever receive a broadcast for which the sequence of the current intent is less than the sequence of the last received intent, ignore it.
Have you considered using a Handler object to communicate from the background thread in the IntentService? The advantage of a Handler over the BroadcastReciver approach is that the Handler uses a message queue to sequence the Message objects.
(I'm assuming your Service is in the same process as the app's main thread).
At least one viable alternative to intents is to execute messaging through the application class, i.e.,
create a listener interface
Manager a collection of listener objects in the application / provide methods to add / remove listener
Interested entities call the application methods to add / remove themselves as listeners
Add "notify" methods in the application, that call the appropriate listener interface method on each of the registered listeners
Services call the application's notification methods to
For example,
public class MyApplication extends Application {
public interface MyListener {
void onEvent();
}
private Set<MyListener> listeners = new HashSet<Listener>();
public void addListener(MyListener l) {
listeners.add(l);
}
public void removeListener(MyListener l) {
listeners.remove(l);
}
public void sendEvent() {
for (MyListener l: listeners) { l.onEvent(); }
}
}
Now, from your activity (or fragment),
public class MyActivity extends Activity implements MyListener {
...
...
...
#Override
public void onEvent() {
// do something
}
#Override
protected void onResume() {
super.onResume();
((MyApplication)getApplication()).addListener(this);
}
#Override
protected void onPause() {
super.onPause();
((MyApplication)getApplication()).removeListener(this);
}
}
And in your service,
((MyApplication)getApplication()).sendEvent();
This provides synchronous messaging without using intents or static variables.
Related
I'm trying to start a service from one activity to make an api call and obtain its result in another activity. I use a BroadcastReceiver to receive the data but how do I make sure that the activity is created and the receiver is attached before sending the broadcast from the service. Is there something wrong with the way that I'm designing this?
Thanks in advance.
Edit: To simply this, I'm starting a 3 second animation when the app is called. I do not want to waste that time and so I'm trying to get data from network and then display it on the activity called after the animation ends. I assumed IntentService would be the way to go but if its not please suggest me how to go about this.
You can use Sticky Events from EventBus library. Basically it will cache your data in memory before broadcasting events. The data can be delivered to subscribers. Thus, you don’t need any special logic to consider already available data.
First you need to declare a class to hold data which you get from network.
public class MyDataEvent {
String token;
// Write more properties here
}
In the service, after getting the data from network, post event which contains data to subscribers.
MyDataEvent data = new MyDataEvent();
data.token = "123456789abcxyz";
EventBus.getDefault().postSticky(data);
In activity which you want to receive the data
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
// UI updates must run on MainThread
#Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onMyDataEvent(MyDataEvent data) {
// Process the data here
Log.i("TAG", data.token);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
I want to develop an Android App with three activities and two services.
The first Service, named WebClientService, calls a REST API every 30 seconds, using an Handler, and has to notify the active Activity with the result.
It also has to notify a second Service, named DatabaseService, in order to update a local DB.
The Database Service will be called just once onCreate of the activity (in case of app crash and restart) and just once at onRestart (in this way we have data to show in case there were connectivity issues). The activities will then keep themselves updated thanks to the WebClientService that notifies the "alive" activity every 30 seconds.
Questions are:
What's the best way to notify for an update both the active activity and the background DatabaseService?
My idea is to use sendBroadcast() within WebClientService and a BroadcastReceiver in every activity and within the DatabaseService, is it the right approach?
Should I use the same approach for the communication between AllMeetingRoomActivity and DatabaseService or should I use a Bound Service?
Thanks
UPDATE:
DatabaseService won't be a background service anymore but just a shared instance of the db layer between WebClientService and the activities.
So question now is: is it a good approach to just write my 30 seconds updates to the local db and allow the activities to update themselves every few seconds simply reading from the local db?
Would that affect the performance too much?
Context:
Follows what I've implemented so far but using SettableFutures and thus needs to be re-implemented using Services and Broadcasts once I've clear how to make them communicate effectively:
public class MainActivity extends AppCompatActivity {
private TextView meetingsTextView;
private EditText mEdit, editSubject;
private final ConnectorInitializer clientInitializer = new ConnectorInitializer();
private AppConnector genericClient; // can use OutlookClient or a test client to talk with a mock server
#Override
protected void onCreate(Bundle savedInstanceState) {
// initializes client based on the settings in "config.json"
genericClient = clientInitializer.create(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
meetingsTextView = (TextView) findViewById(R.id.NowMeeting);
mEdit = (EditText)findViewById(R.id.editText);
editSubject = (EditText)findViewById(R.id.editSubject);
Futures.addCallback(genericClient.logon(this, scopes), new FutureCallback<Boolean>() {
#Override
public void onSuccess(Boolean result) {
Log.d("APP", "-- Logged in. --");
databaseConnector.synchronouslyGetBackupFromLocalDatabase() // FUTURE
// callback here
// onSuccess, onFailure
}
#Override
public void onFailure(#NonNull Throwable t) {
Log.e("\n ~~~~>> logon \n", t.getMessage());
meetingsTextView.setText(R.string.Login_Failed);
}
});
}
/** At the moment the UI is not updated automatically every 30 seconds
* but manually using a refresh button
*/
public void getBookings(#SuppressWarnings("UnusedParameters") View view){
Log.d("APP", "Retrieve button clicked: "+(DateTime.now())+". Calling async getCalendar.");
meetingsTextView.setText(R.string.retrieving_events);
try{
Futures.addCallback( genericClient.getCalendarEvents(), new FutureCallback<String>(){
#Override
public void onSuccess(final String resultCalendars) {
Log.d("APP", "Success. Result: "+resultCalendars);
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.d("APP", "Calendars SUCCESSFULLY retrieved.");
String meetingsRetrieved = getString(R.string.calendar)+resultCalendars;
meetingsTextView.setText(meetingsRetrieved);
Toast.makeText(getApplicationContext(), "Success!", Toast.LENGTH_LONG).show();
}
});
databaseConnector.asyncUpdateLocalDbWithResults(); // FUTURE
// callback here
// onSuccess, onFailure
}
#Override
public void onFailure(#NonNull Throwable t) {
Log.e( "APP", "Calendar error. Cause: "+t.getLocalizedMessage() );
String retrieveError = "Retrieve error. \n\n\n"+t.getLocalizedMessage();
meetingsTextView.setText(retrieveError);
Toast.makeText(getApplicationContext(), "Fail!", Toast.LENGTH_LONG).show();
}
});
}catch(Exception ex){
Log.e("APP","Something went wrong in your code. Cause:"+ex);
}
}
Best option ever:
Use LocalBroadcastManager. More reference here.
MyService.java:
private LocalBroadcastManager localBroadcastManager;
private final String SERVICE_RESULT = "com.service.result";
private final String SERVICE_MESSAGE = "com.service.message";
#Override
public void onCreate() {
super.onCreate();
// Other stuff
localBroadcastManager = LocalBroadcastManager.getInstance(this);
}
Add below method in service, whenever you want to update data from service to Activity, call method by passing Arguments.
private void sendResult(String message) {
Intent intent = new Intent(SERVICE_RESULT);
if(message != null)
intent.putExtra(SERVICE_MESSAGE, message);
localBroadcastManager.sendBroadcast(intent);
}
HomeActivity.java:
private BroadcastReceiver broadcastReceiver;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_home);
broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String s = intent.getStringExtra(MyService.SERVICE_MESSAGE);
// do something here.
}
};
}
#Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver((broadcastReceiver),
new IntentFilter(MyService.SERVICE_RESULT));
}
#Override
protected void onStop() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
super.onStop();
}
Hope this will help you.
I think your approach is ok with BroadCastReceiver. However, BroadCastReceiver should be used for a global purpose (like communicating between 2 applications). If you intend to use BroadCastReceiver for your app only, I prefer using LocalBroadcastManager instead. Using LocalBroadcastManager is faster and more security when it can be caught only by your app.
There's another way to communicate between your activitys and your services is using EventBus. It will be much easier than using BroadCastReceiver (especially in passing data between them).
Update: About your update question:
is it a good approach to just write my 30 seconds updates to the local db and allow the activities to update themselves every few seconds simply reading from the local db? --> Of course NO. You should let your activities update themselves when they need. When you update your local db, you should know that is there any changes or not. If there is any change, use LocalBroadcastmanager to notify your activity to update.
Would that affect the performance too much? --> Yes, that do. The db connection will take time to execute and it will block your UI in some cases. in that case, you should use a thread with ExecutorService for each execute (insert, update...). One more thing to consider is updating that frequently will drain your phone battery very, very fast.
You can bind the services to the activities and update your UI.
Or you can use libraries like Otto or EventBus to create a publisher/subscriber dependency and notify your activities everytime your services publish an update of information.
Use event bus for this communication. EventBus allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another (and thus be aware of each other). It is designed exclusively to replace traditional Java in-process event distribution using explicit registration.
There are a lot of them:
http://square.github.io/otto/
https://github.com/greenrobot/EventBus
This is an example of Otto usage:
Bus bus = new Bus();
bus.post(new AnswerAvailableEvent(42));
#Subscribe public void answerAvailable(AnswerAvailableEvent event) {
// TODO: React to the event somehow!
}
bus.register(this); // In order to receive events, a class instance needs to register with the bus.
To post from any thread (main or background), in you case a Service and receive events on the main thread:
public class MainThreadBus extends Bus {
private final Handler mHandler = new Handler(Looper.getMainLooper());
#Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
super.post(event);
} else {
mHandler.post(new Runnable() {
#Override
public void run() {
MainThreadBus.super.post(event);
}
});
}
}
I've have develop an android application with an IntentService.
This IntentService does nothing more like a fileupload.
I just want to implement the functionality to upload multiple "file-upload-threads".
The problem is to stop one specified "file-upload-thread".
For example:
User choose ten files from the device and upload these.
A notification is created to gives the user "transfer-feedback".
While the thread is working the user can choose other files to upload.
Another notification is created but does not start until the first thread is done.
Is it possible to implement a "cancel-functionality" for the user to cancel the first thread and the second thread start automatically?!
Maybe is it better to user an Service instead of an IntentService?
IntentServices are designed to run-one-at-a-time. Of course you could cancel your IntentService since in inherits from Service.
class MyService extends IntentService {
public static volatile boolean shouldStop = false;
public MyService() {
super("My Service");
}
#Override
protected void onHandleIntent(Intent intent) {
doStuff();
}
private void doStuff() {
// do something
// continue doing something
// put those checks wherever you need
// check the condition
if (shouldStop) {
stopSelf();
return;
}
}
}
And from your activity code you can change the state of the boolean flag to true if you want to stop the service. Hope this will help you.
An IntentService is designed to stop itself only when all the requests present in the work queue have been handled.As per docs,IntentService class "Stops the service after all of the start requests are handled, so you never have to call stopSelf()
In case you want to cancel requests,you can probably use volley and cancel previous requests
https://developer.android.com/training/volley/simple.html#cancel
My app has activities for the user interaction and a background service which is the only place where the data model is being modified. The background service listens to actions that where made by the user as well as incoming messages from the network. Therefore concurrency issues can arise which I try to prevent by using a handler.
For the event layer I use greenrobots Eventbus.
This is all working well but I wonder if there is a smarter/faster/less code extensive (and therefore less error prone) way to handle this use case?
To be more specific:
Is there a way to ensure serial execution of the onEvent methods
without a handler?
Is there an alternative to having onEvent methods
for each possible event?
Is there a better pattern for what I am
doing here?
This is my approach:
In the oncreate method I do register the service (in case of an activity I do this in onstart)
#Override
public void onCreate() {
super.onCreate();
...
EventBus.getDefault().register(this);
}
And in the onDestroy I do unregister again:
#Override
public void onDestroy() {
super.onDestroy();
....
EventBus.getDefault().unregister(this);
}
Whenever I react to an incoming event I want to ensure serial execution as there can be concurreny issues because there are incoming events from user interactions as well as from other users via networking. So I decided to work with a handler:
private Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
Object receivedEvent = msg.obj;
if(receivedEvent instanceof EditUser)
{
processEditUserBroadcast((EditUser)receivedEvent);
}
else if(receivedEvent instanceof JoinParty)
{
processJoinPartyBroadcast((JoinParty)receivedEvent);
}
else if(receivedEvent instanceof LeaveParty)
{
processLeavePartyBroadcast();
}
else if(receivedEvent instanceof SendMessage)
{
processSendMessageBroadcast((SendMessage)receivedEvent);
}
else if(receivedEvent instanceof ReceivedMessage)
{
processReceivedMessageBroadcast((ReceivedMessage)receivedEvent);
}
else if(receivedEvent instanceof Reset)
{
processResetBroadcast();
}
else if(receivedEvent instanceof ImageDownloadFinished)
{
processImageDownloadFinishedBroadcast((ImageDownloadFinished)receivedEvent);
}
}
};
return handler;
}
For each event of interest I do have an onEvent method which is doing nothing but passing the event to the handler to ensure serial execution via a small "passToHandler" helper function
public void passToHandler(Handler handler, Object object)
{
Message message = handler.obtainMessage();
message.obj = object;
handler.sendMessage(message);
}
public void onEvent(EditUser editUser)
{
passToHandler(handler,editUser);
}
public void onEvent(JoinParty joinParty)
{
passToHandler(handler,joinParty);
}
public void onEvent(LeaveParty leaveParty)
{
passToHandler(handler,leaveParty);
}
public void onEvent(SendMessage sendMessage)
{
passToHandler(handler,sendMessage);
}
public void onEvent(ReceivedMessage receivedMessage)
{
passToHandler(handler,receivedMessage);
}
public void onEvent(Reset reset)
{
passToHandler(handler,reset);
}
public void onEvent(ImageDownloadFinished imageDownloadFinished)
{
passToHandler(handler,imageDownloadFinished);
}
The "process.." methods are where the "data magic" happens and shouldn´t be relevant for my question.
And of course for each possible event I did create a class which is usually quite slim like this:
public class JoinParty {
private String partyCode;
public JoinParty(String partyCode) {
super();
this.partyCode = partyCode;
}
public String getPartyCode() {
return partyCode;
}
}
Thank you for posting this Matthias! I think you bring up a very important point about thread safety with GreenRobot EventBus that can easily be missed by users of it.
I think you are quite possibly heading down the right path, though I'm new to GreenRobot EventBus and Android (but not Java). If I read the GreenRobot EventBus source code correctly, one other possible benefit to this approach is that post of the SendMessage event to your onEvent() method immediately returns (after calling sendMessage on the Handler) allowing the EventBus to continue posting it to any other subscribers without delay of the actual processing by your class. This may or may not be what you desire though.
With the approach that you have given, the other thing you need to ensure is that if you take an approach like this that there are no other public methods to your class that has all of your onEvent() methods and the methods such as processEditUserBroadcast(). Otherwise, while you have ensured that all of the processing of the events received from the EventBus are actually handled on a single thread (in a serial manner), some other class might call a public method of this class on a different thread and then cause you thread safety issues again.
If you know that you do need to support other public methods on this class, doing what you have done here at least gets all of the onEvent() methods handling onto a single thread (that of the Looper for the thread that creates the Looper from what I read in the doc for the Looper class) and that simplifies things at least some. You may also then need to apply some synchronization to the public methods and all of the other methods such as processEditUserBroadcast() so as to guarantee safe access to the data members of the class from multiple threads if you are going to have other public methods on this class. Alternatively, depending on what those data members are and what your needs are, you might be able to get by with simply making some of them volatile, atomic, or using the concurrent collections, etc. It all depends on what the read and write access needs are and also the needed granularity of those accesses.
Does this help at all? For those that are well versed with Android, Loopers, Handlers, GreenRobot EventBus, etc. have I misspoken at all?
I have a Service like this (this is not the actual Service, it's just for describing my problem).
public class UploadService {
private BlockingQueue<UploadData> queue = null;
private UploadInfoReceiver receiver = null;
public void onStart(...) {
queue = new LinkedBlockingQueue<UploadData>();
(new Processor()).start();
// creating and reigtering receiver
}
public void onDestroy() {
queue.add(new ServiceDestroyedData());
// unregistering the receiver
}
private class Processor extends Thread() {
public void run() {
while (true) {
UploadData data = queue.take();
if (data instanceof ServiceDestroyedData) {
return;
}
// processing data
}
}
}
private class UploadInfoReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
queue.add(new UploadData(/* getting data from intent */));
}
}
}
And my problem is that if I do something like this in my App:
if (!isUploadServiceRunning()) {
// start the Service
}
Then it starts the Service, but when I move my App to the background and open task manager (android 4.2.2), and kill the app, Android restart my Service, and I can see that it creates a whole new instance of it, and I can see that onDestroy never gets called for the previous Service instance. And I also can see that the instance of the previous Processor Thread is no longer running. How can this be? If onDestroy never gets called how does Android know that it should stop my Thread?
Thanks for your answers.
Android will kill off anything that it finds that is attached to your apps classloader when you select force stop from the menu. Think kill -9 on Linux. There will be no nice callbacks to any onDestroy methods, the system will just end everything.
Now for your service:
while(true) should really NEVER be used. It will instantly kill the battery and will not do any work 99% of the time anyway.
You area already using a receiver, you can just put your while logic into there and once the upload is done call the next upload and so on. There is absolutely no need for the loop.