I created a service which syncs data from the web on a background thread and want to notify a list activity when the service has completed so it can update it's cursor? What would be the best way to do this? I'm thinking of sending a broadcast when the service is done but not sure if that's the best way to do it. I need to requery the cursor when the service has finished so I'm not sure if that will work well with broadcast receivers? I haven't done alot of android in awhile so thanks in advance.
Use a Handler in your service that registers a client when your ListActivity connects to the service; that is, in your onServiceConnected method in your ListActivity, send a Message to your service that enables you to keep track of connected clients. Then you can simply loop through these clients in your Service and send them a Message when something takes place in your Service that you want to notify your ListActivity about. For more information you can look at code in an on-going project of mine: my ListActivity and my Service stub.
In short, in your MainActivity, start and bind to your service with:
Intent i = new Intent(this, NetworkService.class);
startService(i);
bindService(i, networkServiceConnection, Context.BIND_AUTO_CREATE);
Define a messenger to respond to messages from the service like:
Messenger messenger = new Messenger(new IncomingHandler());
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case NetworkService.MSG_SOMETHING:
// do something here
break;
default:
super.handleMessage(msg);
}
}
}
And then write your service connection code like:
private ServiceConnection networkServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
networkService = new Messenger(service);
try {
Message msg = Message.obtain(null, NetworkService.MSG_REGISTER_CLIENT);
msg.replyTo = messenger;
networkService.send(msg);
log.debug("Connected to service");
} catch (RemoteException e) {
// Here, the service has crashed even before we were able to connect
}
}
Note that the replyTo is the messenger we just created.
In your NetworkService, keep track of connected clients with:
ArrayList<Messenger> clients = new ArrayList<Messenger>();
and create your handler like:
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
log.debug("Adding client: " + msg.replyTo);
clients.add(msg.replyTo);
break;
default:
super.handleMessage(msg);
break;
}
}
}
Then, when you want to send a message back to your MainActivity, just do something like the following:
for (int i = 0; i < clients.size(); i++) {
try {
clients.get(i).send(Message.obtain(null, MSG_SOMETHING));
} catch (RemoteException e) {
// If we get here, the client is dead, and we should remove it from the list
log.debug("Removing client: " + clients.get(i));
clients.remove(i);
}
}
If you're already using the support library, you could just as easily fire a broadcast from the service using the LocalBroadcastManager back to your activity that would listen for the broadcast being sent.
Using LocalBroadcastManager ensures only your own application would ever receive the broadcast so you don't have to worry about leaking private data or opening up potential security holes.
Also see: how to use LocalBroadcastManager?
EDIT (09/2014):
A better way to do this would be to use an event bus framework like Otto (my favourite) or GreenRobot/EventBus to avoid coupling your components too tightly.
As per The Busy Coder's Guide to Advanced Android Development
An Event Bus is a great way for the service to let other pieces
of the app know that certain work was done. It provides a standard
communications channel (or “bus”) that event producers and event
consumers can hook into. Event producers merely need to hand the event
to the bus; the bus will handle directing those events to relevant
consumers. This reduces the coupling between the producers and
consumers, sometimes even reducing the amount of code needed to source
and sink these events.
Related
What is the recommended approach for checking for new data regardless if the app is in the foreground or background? I am wondering which Android API people are typically using to do this. There seems to be a few ways to achieve my goal, and I want to make sure I'm on the right path.
I have something put together which uses AlarmManager.SetInexactRepeating() to call an IntentService which does the sync and inserts/updates data in the database. This works while the app is in the foreground and background, but if I force stop the app then I keep seeing "Unfortunately, has stopped working" messages when the AlarmManager alarm would've triggered. In this case, I only care about checking for new data only when the app is running in the foreground or background.
My first thought is to detect when the app is force closed, and stop the alarm, but that does not seem possible. So I am asking here, is my approach wrong? If so, which approach is used to perform some periodic task regardless if the phone is in the foreground or background? The problem with the AlarmManager solution I am using is the alarms continue to fire even when the app is closed.
If your idea is to check if your API has new data and perform a background sync to your local database or other data storage, I think you would like to take a look at this:
Creating a Sync Adapter
Running a Sync Adapter
The Sync adapter is the recommended way of achieving this in Android. The pros of using it are multiple:
Optimisations out of the box - the OS bundles calls, uses the most appropriate windows to run the sync adapter at a minimal bandwidth and battery cost
The lifecycle of your background sync component is managed internally by the OS
Observers can be notified when data has been changed so the UI can be updated easily
Multiple ways of running the sync - at intervals, automatically with the OS message to keep TCP/IP connections open or on demand
However, implementing this requires some things, that can cause a bit of a pain at first:
It is mandatory that the adapter works with a ContentProvider
Sync Adapters use Account for authentication. If this is not needed, a Stub has to be provided
For backgrounding on Android usually you use even a Service that can run alone and independently from the App or a Bounded service that takes and returns data from the App. A complete reference on backgrounding can be found here
Using a Service is the right way to go. Have your app start the Service and it will continue running while the app is in the foreground or the background. Then, if you want to kill the Service when your app closes, you could just call stopService(yourServiceIntent); from the onDestroy() override in your app's activity. That should effectively shut down the service when the app closes.
So some sample code of how this works (taken from the Services docs)...
The Service (just Logs a message every 1 second for 60 seconds):
public class MyService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
long endTime = System.currentTimeMillis() + 60*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(1000);
Log.d("SERVICE", "The service is still running.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
}
}
And in your activity you would do something like:
public class MainActivity extends AppCompatActivity {
Intent serviceIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serviceIntent = new Intent(this, MyService.class);
startService(serviceIntent);
}
#Override
protected void onDestroy() {
stopService(serviceIntent);
super.onDestroy();
}
I have 2 service (Service A and B) that exchange data using binding. One of the 2 services (Service A) can bind to multiple services (now there is only Service B, but I'll have to add also other 2 services, C and D). All the services exchanges data in the same way and use the same messages.
I would like to know if it is possible to retrieve the name of the service that sent the message.
In particular my Service A when receives a message from B needs to store this information so when it receives the same message from C or D behaves differently. Is it possible without adding a String in a bundle attached to the Message?
This is the code where I would like to get this info in Service A
class myHandler extends Handler {
#Override
public void handleMessage(Message msg) {
String senderName;
switch (msg.what) {
case REGISTER:
senderName = ???
addToRecord(senderName);
sendConfirm(msg.replyTo, SUCCESS);
break;
case UNREGISTER:
senderName = ???
removeFromRecord(senderName);
sendConfirm(msg.replyTo, SUCCESS);
break;
default:
super.handleMessage(msg);
}
}
}
Message's structure like :
Message{
what,arg1,arg2,obj,when,data,replyTo.
}
and Bind of Messenger :
oneway interface IMessenger {
void send(in Message msg);
}
So your background service can not obtain the name of original service.
As you said, putting a String in a bundle to the message can resolve this.
In some circumstances for avoid fake name, you can assign the particular String to the original service when binding. And let them resend to your service when call the remote method.
I have a application which starts service on the first launch.
After that it pulls data from the server periodically.
I have opened my activity and if there is refresh button, I already have service which is already fetching data in the background that moment I want to disable the button and as soon as new data is loaded I have to show it in activity and enable refresh button.
If activity is not running then it should show notification.
So second point was the easiest and done. I'm stuck on the point 1. How to send periodically data to activity from service? I'm using database to store the data.
Any help on this ?
You can have your service send 'Messages' to your Activity Messenger to make it react as soon as service detects new content (see this android developers help section on Activity/Service Messenging).
Here are samples for Two-Way messaging (from Service to Activity and from Activity to Service). Quoting the doc:
You can see an example of how to provide two-way messaging in the
MessengerService.java (service) and
MessengerServiceActivities.java (client) samples.
Here's the relevant parts.
Incoming Handler in Activity:
/**
* Activity Handler of incoming messages from service.
*/
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MessengerService.MSG_SET_VALUE:
mCallbackText.setText("Received from service: " + msg.arg1);
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Activity target published for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
In the service, showing only the relevant parts:
/**
* Handler of incoming messages from clients.
*/
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
//obtain Activity address from Message
Messenger mClient=msg.replyTo;
try {
// try to send it some mValue
mClient.send(Message.obtain(null,MSG_SET_VALUE, mValue, 0));
} catch (RemoteException e) {
// The client is dead. Remove it
mClient=null;
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
Also you can bind to your service from your activity and periodically call one of your service method to check for new content. For this, if your service is another application, you must use aidl (this is harder). If it's in the same package, I advise you to use the much easier 'local service binding'
I'm building a Bluetooth application. I want to periodically scan for nearby Bluetooth devices. This program should start when the device starts and continue discovering devices based on a schedule (every 10 mins for example). I've been looking over the Android example of "BlueTooth Chat" in the API documentation, and I don't kow why it never uses the "Service" class. Should I use Service or Activity?
Furthermore, I understand that Services are supposed to be used for "long running tasks," but I also at some point want to provide some kind of GUI notification to the users via this class that discovers Bluetooth devices.
So, can someone please explain which one to use? What is the advantage?
You should use service if you want your scheduling running.Because android will eventually destroy your activity.
Definitely use a Service. In your MainActivity bind to the Service using bindService providing a ServiceConnection object. In this ServiceConnection object send a Message to the service with a reference to a local Messenger object (as part of a replyTo) that the Service can then use to later on send a Message back to your MainActivity. This will enable you to update your MainActivity GUI based on the results of your bluetooth scan.
In short, in your MainActivity, start and bind to your service with:
Intent i = new Intent(this, NetworkService.class);
startService(i);
bindService(i, networkServiceConnection, Context.BIND_AUTO_CREATE);
Define a messenger to respond to messages from the service like:
Messenger messenger = new Messenger(new IncomingHandler());
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case NetworkService.MSG_SOMETHING:
// do something here
break;
default:
super.handleMessage(msg);
}
}
}
And then write your service connection code like:
private ServiceConnection networkServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
networkService = new Messenger(service);
try {
Message msg = Message.obtain(null, NetworkService.MSG_REGISTER_CLIENT);
msg.replyTo = messenger;
networkService.send(msg);
log.debug("Connected to service");
} catch (RemoteException e) {
// Here, the service has crashed even before we were able to connect
}
}
Note that the replyTo is the messenger we just created.
In your NetworkService, keep track of connected clients with:
ArrayList<Messenger> clients = new ArrayList<Messenger>();
and create your handler like:
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
log.debug("Adding client: " + msg.replyTo);
clients.add(msg.replyTo);
break;
default:
super.handleMessage(msg);
break;
}
}
}
Then, when you want to send a message back to your MainActivity, just do something like the following:
for (int i = 0; i < clients.size(); i++) {
try {
clients.get(i).send(Message.obtain(null, MSG_SOMETHING));
} catch (RemoteException e) {
// If we get here, the client is dead, and we should remove it from the list
log.debug("Removing client: " + clients.get(i));
clients.remove(i);
}
}
I make login in my main activity.
If the login is correct, I wanna see my profile and download some data from server, but I cannot keep the connection, if I change activity.
How can I do this?
Once you verify the login is correct (by testing the connection to the server and
authenticating), you can store the login details in SharedPreferences or something similar.
Then you can just make subsequent requests using those login details (no matter which activity you are in). This is, of course, assuming the server accepts authentication this way.
You could also use a Service. Services are long running background tasks that live through Activities.
In your login form you could send a startService intent similar to starting a new activity. This calls onStartCommand on the Service class where you can create the connection and store it in the Service object itself. When you require information from the connection, you can acquire the service instance using bindService call.
Below is an example of a basic login service adapted from Google's basic example.
public class LocalService extends Service {
private MyConnection conn;
/**
* Class for clients to access. Because we know this service always
* runs in the same process as its clients, we don't need to deal with
* IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent i = new Intent("my.service.connected");
try {
conn = new MyConnection(
intent.getStringExtra("username"),
intent.getStringExtra("password"));
i.putExtra("succeeded", true);
sendBroadcast(i);
} catch (ConnectionException ex) {
i.putExtra("succeeded", false);
sendBroadcast(i);
}
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// This is the object that receives interactions from clients. See
// RemoteService for a more complete example.
private final IBinder mBinder = new LocalBinder();
}
The Android.Manifest.xml also needs <service android:name="LocalService"></service> element inside the <application />.
Services don't come without their downsides though. Generally you should pass the startup parameters in an Intent which means they need to be serializable or parcelable. This prevents you from creating the connection in your Activity, checking that login succeeded and sending it the service.* Instead you need to send the login parameters to the service and perform the login there.
The complications come when you need to resolve whether the connection succeeded and you should proceed to the next activity or if it failed and you should show an error message. There are several solutions, but all of them require some setup. Especially when you consider that the user may pause your activity between pressing 'Login' and actually logging in.
As shown above, one option is to notify the activity by sending broadcasts from the Service. Below is an activity that launches the above service and listens to the broadcasts. The below activity does not take into account the possibility that the activity may be paused at any moment.
public class LoginActivity extends Activity {
class MyReceiver extends BroadcastReceiver {
#Override
public final void onReceive(Context ctx, Intent i) {
if (i.getBooleanExtra("succeeded", false)) {
startMyMainActivity();
} else {
showErrorMessage();
}
}
}
private BroadcastReceiver bcReceiver;
private void doLogin(String username, String password) {
// Register receiver that listens for connected messages.
bcReceiver = new MyReceiver();
IntentFilter ifilter = new IntentFilter("my.service.connected");
registerReceiver(bcReceiver, ifilter);
// Send command to start service (connects)
Intent i = new Intent(this, LocalService.class);
i.putExtra("username", username);
i.putExtra("password", password);
startService(i);
}
#Override
protected void onPause() {
if (bcReceiver != null) unregisterReceiver(bcReceiver);
super.onPause();
}
}
When the Activity should handle paused activity gracefully you should keep track of its state in a shared variable. If it was previously in a state where it tracked the Service it should bind to the Service and check its state when resuming. If the service is still connecting (as opposed to connected or disconnected) it should re-register the broadcast listener.
I wonder why you need to keep the connection. Maybe you can just reopen the connection and login again?
But if you really need to keep the connection open: You could can keep the connection in the Application. But make sure you close the connection when the user goes away from your app! I really don't recommend this. Or set a timeout for the connection to be closed like after 5 minutes of inactivity.
http://developer.android.com/reference/android/app/Application.html
You will have to say in the AndroidManifest which Application your App uses.
http://developer.android.com/guide/topics/manifest/application-element.html#nm (see andriod:name).
Then you can get the application from the activity through: getApplication(). Don't forget to cast it. Than add functions where you can login and so on.
Edit:
A little more detailed:
You will have to create a class extending Application. The name of this class you enter in the android:name of the application in the manifest. In that class you write functions with which you handle the connection. Like: startConnection() doLogin() stopConnection() ... In the Activities you call:
NameOfYouApplication app = (NameOfYouApplication) getApplication();
app.startConnection();
app.doLogin();
kind of like that. But beware that even if the Activity is closed your Application will stay alive. You better find a good time to close the connection.