Android Service to show toast - android

This code is supposed to use a service to show a toast message. There are no errors, but it doesn't show the toast.
main activity
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent i= new Intent(this, BackgroundMusic.class);
this.startService(i);
}
}
service (its called Background Music but for now it is supposed to show a toast message)
public class BackgroundMusic extends IntentService {
public BackgroundMusic() {
super("BackgroundMusic");
}
#Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
Context context = getApplicationContext();
CharSequence text = "Hello toast!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
}
manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.starwars"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<application
android:allowBackup="true"
android:debuggable="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<service android:name=".BackgroundMusic" />
<activity
android:name="com.example.starwars.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:label="#string/app_name" android:name="BackgroundMusic"/>
</application>
</manifest>

Try this:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast.makeText(YourService.this.getApplicationContext(),"My Awesome service toast...",Toast.LENGTH_SHORT).show();
}
});

See this part of the docs
(An IntentService has a few limitations:
It can't interact directly with your user interface. To put its
results in the UI, you have to send them to an Activity.
You need to put it on the main Thread. See the answer here by rony of a way to do that.
and from the full documentation on IntentService
handles each Intent in turn using a worker thread

It's probably best to delegate all GUI activities (including toast) to the Activity that is using your Service. For example, I have a bound service for doing location capture in the background and posting updates to the screen while my app is visible.
My app implements a simple interface:
public interface ICapture {
void update(Location location);
}
and my class def looks like this:
public class MyActivity extends Activity implements ICapture {
...
Here's the stuff for handling the bound service:
private CaptureService captureService;
private ServiceConnection captureServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
CaptureService.MyLocalBinder binder = (CaptureService.MyLocalBinder) service;
captureService = binder.getService();
captureService.setOwner(ICapture.this);
}
public void onServiceDisconnected(ComponentName arg0) {
}
};
The only thing here that's not standard is the line
captureService.setOwner(ICapture.this);
which provides the service with a reference to the app's implementation of ICapture. See below for how it's used.
I start the Service in onCreate():
Intent intent = new Intent(this, CaptureService.class);
startService(intent);
bindService(intent, captureServiceConnection, Context.BIND_AUTO_CREATE);
and I use these methods to tell the service when the app is visible and able to satisfy GUI requests:
#Override
public void onPause() {
super.onPause();
if (captureService != null) {
captureService.setOwner(null);
}
}
#Override
public void onResume() {
super.onResume();
if (captureService != null) {
captureService.setOwner(this);
}
}
The Service looks like this:
package *****;
import android.app.Service;
import android.content.Intent;
import android.location.Location;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
public class CaptureService extends Service implements
com.google.android.gms.location.LocationListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private static final long UPDATE_INTERVAL = 1000 * 10;
private static final long FASTEST_INTERVAL = 1000 * 5;
private final IBinder myBinder = new MyLocalBinder();
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private ICapture owner;
#Override
public void onCreate() {
if (isGooglePlayServicesAvailable()) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mGoogleApiClient.connect();
}
}
#Override
public void onConnected(Bundle bundle) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
/**************************************************************************
* The binder that returns the service activity.
*/
public class MyLocalBinder extends Binder {
public CaptureService getService() {
return CaptureService.this;
}
}
#Override
public IBinder onBind(Intent arg0) {
return myBinder;
}
/**************************************************************************
* Bound methods.
*
* Set the owner, to be notified when the position changes.
*
* #param owner
*/
public void setOwner(ICapture owner) {
this.owner = owner;
}
/**************************************************************************
* Start the service and keep it running when the phone is idle.
*/
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
/**
* Callback when the location changes. Inform the owner.
*
* #param location
*/
#Override
public void onLocationChanged(Location location) {
if (owner != null) {
owner.update(location);
}
}
private boolean isGooglePlayServicesAvailable() {
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (ConnectionResult.SUCCESS == status) {
return true;
} else {
return false;
}
}
}
All this is pretty standard code you can find elsewhere. The main thing is that when a location update occurs the code calls the app via its implemented ICapture interface, but only if the app is visible. The implementation of onPause() and onResume() in the app makes sure that the service knows when the app can accept calls.
To do a toast popup, add another method to the ICapture interface and implement it in the app. Your service can then call it any time it knows the screen can accept it. In fact, toast popups will still come through even when the app isn't in the foreground, but I believe they get blocked when the screen goes inactive, which in turn blocks the service. So it's better to only send them when the app is in the foreground.

Related

Service not binding

I have an android device with an integrated barcode scanner. I'm setting up the service as follows:
public class BarcodeService extends Service {
private final LocalBinder binder = new LocalBinder();
public class LocalBinder extends Binder {
public BarcodeService getService() {
return BarcodeService.this;
}
}
#Override
public IBinder onBind(Intent arg0) {
return binder;
}
#Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("ServiceStartArguments");
thread.start();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//Get scanner
}
}
The service is also in the AndroidManifest.xml. The class that makes use of this service is:
public class BarcodeReader extends Activity {
private BarcodeService barcodeService;
private boolean isBound = false;
private ServiceConnection barcodeServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
barcodeService = ((BarcodeService.LocalBinder)service).getService();
isBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
barcodeService = null;
isBound = false;
}
};
#Override
protected void onStart() {
super.onStart();
if (!isBound) {
Intent intent = new Intent(this, BarcodeService.class);
startService(intent);
bindService(intent, barcodeServiceConnection, Context.BIND_AUTO_CREATE);
}
}
#Override
protected void onStop() {
super.onStop();
if (isBound) {
unbindService(barcodeServiceConnection);
}
}
}
However the service is not binding, ie. barcodeService is always null. The code never reaches onServiceConnected.
What am I missing? And is it necessary to use a class that extends Activity?
Common Android Service troubleshooting
Just some general remarks and stuff to check if your service is not starting.
Service class defined in Manifest
Common mistake is not to have the service in manifest (android doesn't warn you about that) or have it there but misspelled the class name.
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
Or you might have it in the manifest (or one of the manifests) but the final manifest after merging that is used within the apk doesn't have the service definition. For that check:
project_folder/app_folder/build/intermediates/manifests/full/...
A project clean and rebuild might help.
Check bindService return value
When debugging check the boolean return value on the bindService call to see if service was started successfully or not.
Debug Activity and Service implementation
Also the service might be running but not bind or might not execute anything hence have no visual effect that it's running in the background. For that use the debugger on both the bound Activity and the Service itself.
Check onBind, onStartCommand in Service class or even the onCreate there.
In Activity check bindService, ServiceConnection and so.
Resources
also check https://developer.android.com/guide/components/services.html

How to launch android wear activity from mobile

I have been working on a project where I need a button on a mobile to start up an activity on the watch. I have been going through the data layer sample in the sdk but can not get it working. I set up a wearable listener service class, but it is not picking up any messages. The service is added to the manifest but it is still not working. I do have other services too and I am thinking I might have too many services.
On the Wear watch, does an activity have to be running in order for it to start another activity? I want the watch to run nothing until it receives a message, is this possible?
Also, what should my edit configuration settings for wear module be? (eg, do not launch activity, launch default or launch from activity) I just want the wearable to boot up when a message is received.
If anybody can point me in the right direction, it would be hugely appreciated.
Thanks.
Moblie
Accessing the Wearable Data Layer
MainActivity.java
public class MainActivity extends Activity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener{
private GoogleApiClient mGoogleApiClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
mGoogleApiClient.connect();
}
#Override
public void onConnected(Bundle bundle) {
Log.d("GoogleApi", "onConnected: " + bundle);
}
#Override
public void onConnectionSuspended(int i) {
Log.d("GoogleApi", "onConnectionSuspended: " + i);
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d("GoogleApi", "onConnectionFailed: " + connectionResult);
}
GetConnectedNodes
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
#Override
public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
for (Node node : getConnectedNodesResult.getNodes()) {
sendMessage(node.getId());
}
}
});
SendMessage
public static final String START_ACTIVITY_PATH = "/start/MainActivity";
private void sendMessage(String node) {
Wearable.MessageApi.sendMessage(mGoogleApiClient , node , START_ACTIVITY_PATH , new byte[0]).setResultCallback(new ResultCallback<MessageApi.SendMessageResult>() {
#Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
if (!sendMessageResult.getStatus().isSuccess()) {
Log.e("GoogleApi", "Failed to send message with status code: "
+ sendMessageResult.getStatus().getStatusCode());
}
}
});
}
Wear
Implement a Message Listener
WearDataLayerListenerService.java
public class WearDataLayerListenerService extends WearableListenerService {
public static final String START_ACTIVITY_PATH = "/start/MainActivity";
#Override
public void onMessageReceived(MessageEvent messageEvent) {
super.onMessageReceived(messageEvent);
if(messageEvent.getPath().equals(START_ACTIVITY_PATH)){
Intent intent = new Intent(this , MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}}
Add Listener Service to Manifest
<service
android:name=".WearDataLayerListenerService">
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>

Registering BroadcastReceivers from singleton

I'm trying to create a singleton class to report connectivity status. The idea being, I can reuse this "service" anywhere in my app e.g:
getApplication().getConnectivityStatus().isConnected()
The singleton has a reference to the ApplicationContext.
I'd like to use a BroadcastReceiver behind the scenes to detect connectivity changes. I'm registering and unregistering it dynamically within the connectivity "service" to implement a higher level listener for connectivity e.g:
getApplication().getConnectivityStatus().onConnectivity(mConnectivityCallback)
I only want the BroadcastReceiver to be registered at my discretion. I see a hole here, though, when the app is killed. Since there are no lifecycle callbacks for Application, there's nothing I can hook onto to unregister the BroadcastReceiver if the app is killed.
So, 1: my app dies but the BroadcastReceiver (with its reference to the ApplicationContext) stays registered and its onReceive method gets called... Do bad things happen?
2: Is there any way to cleanly do what I'm trying to do here or is my pattern just misconceived?
I would simply create BroadcastReceiver registered in the AndroidManifest.xml.
From that receiver you could update your singleton's connection status and you don't have to be worried about unregistering. If you don't want to other classes be able to change status, put receiver and singleton classes in the separate package and make setter method visibility to the package only.
Update
Your idea sound really interesting, having hermetic "service" that handles network changes and inform registered listeners. But, at the end your listeners are Activites which means that there will always be one listener at a time. Moreover, you cannot keep strong references to Activity in long lived objects and you have to use Activite's lifecycle callbacks.
I came to the conclusion that it would be better for you to go with very simple pattern(which I used to implement) of manager class.
Manager class could look like this:
public class NetworkManager {
private boolean connected;
private NetworkCallback callback;
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
updateStatus(context);
}
};
private void updateStatus(Context context) {
ConnectivityManager conn = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = conn.getActiveNetworkInfo();
//TODO do the rest here, e.g. set connected flag
connected = networkInfo.isConnected();
if (callback != null) {
callback.onConnectionChanged(connected);
}
}
public void registerContext(Context context, NetworkCallback callback) {
this.callback = callback;
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(receiver, filter);
updateStatus(context);
}
public void unregisterContext(Context context) {
callback = null;
context.unregisterReceiver(receiver);
}
public interface NetworkCallback {
void onConnectionChanged(boolean connected);
}
}
It doesn't have to be a singleton if it's used by Activity only.
And Activity that use it:
public class NetworkActivity extends Activity implements NetworkManager.NetworkCallback {
private NetworkManager networkManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
networkManager = new NetworkManager();
}
#Override
protected void onResume() {
super.onResume();
networkManager.registerContext(this, this);
}
#Override
protected void onPause() {
networkManager.unregisterContext(this);
super.onPause();
}
#Override
public void onConnectionChanged(boolean connected) {
//TODO do something here
}
}
Hint
Do not force yourself to create code using sophisticated Patterns where it is not needed. Use Patterns only if your application actually benefit from their advantages.
Here am posting sample code who test internet connection through service and let user know is internet present or not through service may it will help you
BroadcastReceiverClass.java
package com.sample;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.widget.Toast;
public class BroadcastReceiverClass extends android.content.BroadcastReceiver {
Context _con;
public BroadcastReceiverClass(Context _con) {
this._con = _con;
}
#Override
public void onReceive(Context context, Intent intent) {
intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
NetworkInfo currentNetworkInfo = (NetworkInfo) intent
.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
if (currentNetworkInfo.isConnected()) {
Toast.makeText(_con, "Connected", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(_con, "Not Connected", Toast.LENGTH_LONG).show();
}
}
}
/////////////////////////////////////////////////////////////////////////
BroadCastSampleActivity .java
package com.sample;
import android.app.Activity;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.os.Bundle;
public class BroadCastSampleActivity extends Activity {
BroadcastReceiverClass mConnReceiver;
/** Called when the activity is first created. */
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
mConnReceiver = new BroadcastReceiverClass(this);
this.registerReceiver(mConnReceiver, new IntentFilter(
ConnectivityManager.CONNECTIVITY_ACTION));
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
// this.unregisterReceiver(mConnReceiver);
}
// private BroadcastReceiver mConnReceiver = new BroadcastReceiver() {
// public void onReceive(Context context, Intent intent) {
// intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY,
// false);
// intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
// intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
//
// NetworkInfo currentNetworkInfo = (NetworkInfo) intent
// .getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
// intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
//
// if (currentNetworkInfo.isConnected()) {
// Toast.makeText(getApplicationContext(), "Connected",
// Toast.LENGTH_LONG).show();
// } else {
// Toast.makeText(getApplicationContext(), "Not Connected",
// Toast.LENGTH_LONG).show();
// }
// }
// };
}
/////////////////////////////////////////////
manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sample"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="#drawable/ic_launcher" android:label="#string/app_name">
<activity android:name=".BroadCastSampleActivity"
android:label="BroadCast">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="9" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

Service wont start / bind to variable?

I'm trying to start a service on android in order to performe some network-related tasks in background. I have written a basic network-manager for my app, which is a service. I basically used the tutorial from the android documentation. The basic structure goes as following:
public class MyNetworkManager extends Service {
// some code
private final IBinder mBinder = (IBinder) new MyBinder();
#Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
public class MyBinder extends Binder {
MyNetworkManager getService() {
return MyNetworkManager.this;
}
}
public void onCreate() {
// some network related stuff like setting up sockets etc.
}
public void onStart(Intent intent, int startid) {
while(true) {
// receive new connections etc
}
}
The calling app/activity is then:
public class AndroidNetworkManagerClient extends Activity {
private Button buttonSend;
private EditText inputText;
private TextView outputText;
private MyNetworkManager networkManager;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
networkManager = ((MyNetworkManager.MyBinder) binder).getService();
}
public void onServiceDisconnected(ComponentName className) {
networkManager = null;
}
};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
inputText = (EditText) findViewById(R.id.textInput);
outputText = (TextView) findViewById(R.id.textOutput);
buttonSend = (Button)this.findViewById(R.id.buttonSend);
buttonSend.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (inputText.getText().length() != 0) {
outputText.append("Out: " + inputText.getText() + "\n");
networkManager.sendData("localhost", inputText.getText().toString());
}
}
});
bindService(new Intent(this, MyNetworkManager.class), mConnection, Context.BIND_AUTO_CREATE);
doSomeAppRelatedStuff();
}
bindService() seems to be called without any problems, but the variable "networkManager" is always null! I already tried to debug into the onCreate() method or onServiceConnected() but it seems, that these parts are not reached at all (at least no breakpoint was triggered).
The service is already registered in the AndroidManifest.xml:
package="some.random.name"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" />
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name=".AndroidNetworkManagerClient"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MyNetworkManager"></service>
</application>
Anyone an idea?
Chances are, your Activity is getting into doSomeAppRelatedStuff() and trying to use networkManager before the binding is complete.
If doSomeAppRelatedStuff() absolutely must have the network manager to function, move your call to doSomeAppRelatedStuff() into onServiceConnected() so it won't actually start until the binding is complete. Note that if you do that, your onStart() and onResume() calls will probably (but not guaranteed!) happen before the binding is complete, so program accordingly.

How to have Android Service communicate with Activity

I'm writing my first Android application and trying to get my head around communication between services and activities. I have a Service that will run in the background and do some gps and time based logging. I will have an Activity that will be used to start and stop the Service.
So first, I need to be able to figure out if the Service is running when the Activity is started. There are some other questions here about that, so I think I can figure that out (but feel free to offer advice).
My real problem: if the Activity is running and the Service is started, I need a way for the Service to send messages to the Activity. Simple Strings and integers at this point - status messages mostly. The messages will not happen regularly, so I don't think polling the service is a good way to go if there is another way. I only want this communication when the Activity has been started by the user - I don't want to start the Activity from the Service. In other words, if you start the Activity and the Service is running, you will see some status messages in the Activity UI when something interesting happens. If you don't start the Activity, you will not see these messages (they're not that interesting).
It seems like I should be able to determine if the Service is running, and if so, add the Activity as a listener. Then remove the Activity as a listener when the Activity pauses or stops. Is that actually possible? The only way I can figure out to do it is to have the Activity implement Parcelable and build an AIDL file so I can pass it through the Service's remote interface. That seems like overkill though, and I have no idea how the Activity should implement writeToParcel() / readFromParcel().
Is there an easier or better way? Thanks for any help.
EDIT:
For anyone who's interested in this later on, there is sample code from Google for handling this via AIDL in the samples directory: /apis/app/RemoteService.java
The asker has probably long since moved past this, but in case someone else searches for this...
There's another way to handle this, which I think might be the simplest.
Add a BroadcastReceiver to your activity. Register it to receive some custom intent in onResume and unregister it in onPause. Then send out that intent from your service when you want to send out your status updates or what have you.
Make sure you wouldn't be unhappy if some other app listened for your Intent (could anyone do anything malicious?), but beyond that, you should be alright.
Code sample was requested:
In my service, I have this:
// Do stuff that alters the content of my local SQLite Database
sendBroadcast(new Intent(RefreshTask.REFRESH_DATA_INTENT));
(RefreshTask.REFRESH_DATA_INTENT is just a constant string.)
In my listening activity, I define my BroadcastReceiver:
private class DataUpdateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(RefreshTask.REFRESH_DATA_INTENT)) {
// Do stuff - maybe update my view based on the changed DB contents
}
}
}
I declare my receiver at the top of the class:
private DataUpdateReceiver dataUpdateReceiver;
I override onResume to add this:
if (dataUpdateReceiver == null) dataUpdateReceiver = new DataUpdateReceiver();
IntentFilter intentFilter = new IntentFilter(RefreshTask.REFRESH_DATA_INTENT);
registerReceiver(dataUpdateReceiver, intentFilter);
And I override onPause to add:
if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver);
Now my activity is listening for my service to say "Hey, go update yourself." I could pass data in the Intent instead of updating database tables and then going back to find the changes within my activity, but since I want the changes to persist anyway, it makes sense to pass the data via DB.
There are three obvious ways to communicate with services:
Using Intents
Using AIDL
Using the service object itself (as singleton)
In your case, I'd go with option 3. Make a static reference to the service it self and populate it in onCreate():
void onCreate(Intent i) {
sInstance = this;
}
Make a static function MyService getInstance(), which returns the static sInstance.
Then in Activity.onCreate() you start the service, asynchronously wait until the service is actually started (you could have your service notify your app it's ready by sending an intent to the activity.) and get its instance. When you have the instance, register your service listener object to you service and you are set. NOTE: when editing Views inside the Activity you should modify them in the UI thread, the service will probably run its own Thread, so you need to call Activity.runOnUiThread().
The last thing you need to do is to remove the reference to you listener object in Activity.onPause(), otherwise an instance of your activity context will leak, not good.
NOTE: This method is only useful when your application/Activity/task is the only process that will access your service. If this is not the case you have to use option 1. or 2.
Use LocalBroadcastManager to register a receiver to listen for a broadcast sent from local service inside your app, reference goes here:
http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html
You may also use LiveData that works like an EventBus.
class MyService : LifecycleService() {
companion object {
val BUS = MutableLiveData<Any>()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
val testItem : Object
// expose your data
if (BUS.hasActiveObservers()) {
BUS.postValue(testItem)
}
return START_NOT_STICKY
}
}
Then add an observer from your Activity.
MyService.BUS.observe(this, Observer {
it?.let {
// Do what you need to do here
}
})
You can read more from this blog.
I am surprised that no one has given reference to Otto event Bus library
http://square.github.io/otto/
I have been using this in my android apps and it works seamlessly.
Using a Messenger is another simple way to communicate between a Service and an Activity.
In the Activity, create a Handler with a corresponding Messenger. This will handle messages from your Service.
class ResponseHandler extends Handler {
#Override public void handleMessage(Message message) {
Toast.makeText(this, "message from service",
Toast.LENGTH_SHORT).show();
}
}
Messenger messenger = new Messenger(new ResponseHandler());
The Messenger can be passed to the service by attaching it to a Message:
Message message = Message.obtain(null, MyService.ADD_RESPONSE_HANDLER);
message.replyTo = messenger;
try {
myService.send(message);
catch (RemoteException e) {
e.printStackTrace();
}
A full example can be found in the API demos: MessengerService and MessengerServiceActivity. Refer to the full example for how MyService works.
The other method that's not mentioned in the other comments is to bind to the service from the activity using bindService() and get an instance of the service in the ServiceConnection callback. As described here http://developer.android.com/guide/components/bound-services.html
Binding is another way to communicate
Create a callback
public interface MyCallBack{
public void getResult(String result);
}
Activity side:
Implement the interface in the Activity
Provide the implementation for the method
Bind the Activity to Service
Register and Unregister Callback when the Service gets bound and unbound with
Activity.
public class YourActivity extends AppCompatActivity implements MyCallBack{
private Intent notifyMeIntent;
private GPSService gpsService;
private boolean bound = false;
#Override
public void onCreate(Bundle sis){
// activity code ...
startGPSService();
}
#Override
public void getResult(String result){
// show in textView textView.setText(result);
}
#Override
protected void onStart()
{
super.onStart();
bindService();
}
#Override
protected void onStop() {
super.onStop();
unbindService();
}
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
GPSService.GPSBinder binder = (GPSService.GPSBinder) service;
gpsService= binder.getService();
bound = true;
gpsService.registerCallBack(YourActivity.this); // register
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
bound = false;
}
};
private void bindService() {
bindService(notifyMeIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private void unbindService(){
if (bound) {
gpsService.registerCallBack(null); // unregister
unbindService(serviceConnection);
bound = false;
}
}
// Call this method somewhere to start Your GPSService
private void startGPSService(){
notifyMeIntent = new Intent(this, GPSService.class);
startService(myIntent );
}
}
Service Side:
Initialize callback
Invoke the callback method whenever needed
public class GPSService extends Service{
private MyCallBack myCallback;
private IBinder serviceBinder = new GPSBinder();
public void registerCallBack(MyCallBack myCallback){
this.myCallback= myCallback;
}
public class GPSBinder extends Binder{
public GPSService getService(){
return GPSService.this;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent){
return serviceBinder;
}
}
Another way could be using observers with a fake model class through the activity and the service itself, implementing an MVC pattern variation. I don't know if it's the best way to accomplish this, but it's the way that worked for me. If you need some example ask for it and i'll post something.
Besides LocalBroadcastManager , Event Bus and Messenger already answered in this question,we can use Pending Intent to communicate from service.
As mentioned here in my blog post
Communication between service and Activity can be done using
PendingIntent.For that we can use
createPendingResult().createPendingResult() creates a new
PendingIntent object which you can hand to service to use and to send
result data back to your activity inside onActivityResult(int, int,
Intent) callback.Since a PendingIntent is Parcelable , and can
therefore be put into an Intent extra,your activity can pass this
PendingIntent to the service.The service, in turn, can call send()
method on the PendingIntent to notify the activity via
onActivityResult of an event.
Activity
public class PendingIntentActivity extends AppCompatActivity
{
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PendingIntent pendingResult = createPendingResult(
100, new Intent(), 0);
Intent intent = new Intent(getApplicationContext(), PendingIntentService.class);
intent.putExtra("pendingIntent", pendingResult);
startService(intent);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 100 && resultCode==200) {
Toast.makeText(this,data.getStringExtra("name"),Toast.LENGTH_LONG).show();
}
super.onActivityResult(requestCode, resultCode, data);
}
}
Service
public class PendingIntentService extends Service {
private static final String[] items= { "lorem", "ipsum", "dolor",
"sit", "amet", "consectetuer", "adipiscing", "elit", "morbi",
"vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam",
"vel", "erat", "placerat", "ante", "porttitor", "sodales",
"pellentesque", "augue", "purus" };
private PendingIntent data;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
data = intent.getParcelableExtra("pendingIntent");
new LoadWordsThread().start();
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
}
class LoadWordsThread extends Thread {
#Override
public void run() {
for (String item : items) {
if (!isInterrupted()) {
Intent result = new Intent();
result.putExtra("name", item);
try {
data.send(PendingIntentService.this,200,result);
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
SystemClock.sleep(400);
}
}
}
}
}
My method:
Class to manage send and receive message from/to service/activity:
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class MessageManager {
public interface IOnHandleMessage{
// Messages
int MSG_HANDSHAKE = 0x1;
void onHandleMessage(Message msg);
}
private static final String LOGCAT = MessageManager.class.getSimpleName();
private Messenger mMsgSender;
private Messenger mMsgReceiver;
private List<Message> mMessages;
public MessageManager(IOnHandleMessage callback, IBinder target){
mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_ACTIVITY));
mMsgSender = new Messenger(target);
mMessages = new ArrayList<>();
}
public MessageManager(IOnHandleMessage callback){
mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_SERVICE));
mMsgSender = null;
mMessages = new ArrayList<>();
}
/* START Getter & Setter Methods */
public Messenger getMsgSender() {
return mMsgSender;
}
public void setMsgSender(Messenger sender) {
this.mMsgSender = sender;
}
public Messenger getMsgReceiver() {
return mMsgReceiver;
}
public void setMsgReceiver(Messenger receiver) {
this.mMsgReceiver = receiver;
}
public List<Message> getLastMessages() {
return mMessages;
}
public void addMessage(Message message) {
this.mMessages.add(message);
}
/* END Getter & Setter Methods */
/* START Public Methods */
public void sendMessage(int what, int arg1, int arg2, Bundle msgData){
if(mMsgSender != null && mMsgReceiver != null) {
try {
Message msg = Message.obtain(null, what, arg1, arg2);
msg.replyTo = mMsgReceiver;
if(msgData != null){
msg.setData(msgData);
}
mMsgSender.send(msg);
} catch (RemoteException rE) {
onException(rE);
}
}
}
public void sendHandshake(){
if(mMsgSender != null && mMsgReceiver != null){
sendMessage(IOnHandleMessage.MSG_HANDSHAKE, 0, 0, null);
}
}
/* END Public Methods */
/* START Private Methods */
private void onException(Exception e){
Log.e(LOGCAT, e.getMessage());
e.printStackTrace();
}
/* END Private Methods */
/** START Private Classes **/
private class MessageHandler extends Handler {
// Types
final static int TYPE_SERVICE = 0x1;
final static int TYPE_ACTIVITY = 0x2;
private IOnHandleMessage mCallback;
private int mType;
public MessageHandler(IOnHandleMessage callback, int type){
mCallback = callback;
mType = type;
}
#Override
public void handleMessage(Message msg){
addMessage(msg);
switch(msg.what){
case IOnHandleMessage.MSG_HANDSHAKE:
switch(mType){
case TYPE_SERVICE:
setMsgSender(msg.replyTo);
sendHandshake();
break;
case TYPE_ACTIVITY:
Log.v(LOGCAT, "HERE");
break;
}
break;
default:
if(mCallback != null){
mCallback.onHandleMessage(msg);
}
break;
}
}
}
/** END Private Classes **/
}
In Activity Example:
public class activity extends AppCompatActivity
implements ServiceConnection,
MessageManager.IOnHandleMessage {
[....]
private MessageManager mMessenger;
private void initMyMessenger(IBinder iBinder){
mMessenger = new MessageManager(this, iBinder);
mMessenger.sendHandshake();
}
private void bindToService(){
Intent intent = new Intent(this, TagScanService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
/* START THE SERVICE IF NEEDED */
}
private void unbindToService(){
/* UNBIND when you want (onDestroy, after operation...)
if(mBound) {
unbindService(mServiceConnection);
mBound = false;
}
}
/* START Override MessageManager.IOnHandleMessage Methods */
#Override
public void onHandleMessage(Message msg) {
switch(msg.what){
case Constants.MSG_SYNC_PROGRESS:
Bundle data = msg.getData();
String text = data.getString(Constants.KEY_MSG_TEXT);
setMessageProgress(text);
break;
case Constants.MSG_START_SYNC:
onStartSync();
break;
case Constants.MSG_END_SYNC:
onEndSync(msg.arg1 == Constants.ARG1_SUCCESS);
mBound = false;
break;
}
}
/* END Override MessageManager.IOnHandleMessage Methods */
/** START Override ServiceConnection Methods **/
private class BLEScanServiceConnection implements ServiceConnection {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
initMyMessenger(iBinder);
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mMessenger = null;
mBound = false;
}
}
/** END Override ServiceConnection Methods **/
In Service Example:
public class Blablabla extends Service
implements MessageManager.IOnHandleMessage {
[...]
private MessageManager mMessenger;
#Nullable
#Override
public IBinder onBind(Intent intent) {
super.onBind(intent);
initMessageManager();
return mMessenger.getMsgReceiver().getBinder();
}
private void initMessageManager(){
mMessenger = new MessageManager(this);
}
/* START Override IOnHandleMessage Methods */
#Override
public void onHandleMessage(Message msg) {
/* Do what you want when u get a message looking the "what" attribute */
}
/* END Override IOnHandleMessage Methods */
Send a message from Activity / Service:
mMessenger.sendMessage(what, arg1, arg2, dataBundle);
How this works:
on the activity you start or bind the service.
The service "OnBind" methods return the Binder to his MessageManager, the in the Activity through the "Service Connection" interface methods implementation "OnServiceConnected" you get this IBinder and init you MessageManager using it.
After the Activity has init his MessageManager the MessageHandler send and Handshake to the service so it can set his "MessageHandler" sender ( the "private Messenger mMsgSender;" in MessageManager ). Doing this the service know to who send his messages.
You can also implement this using a List/Queue of Messenger "sender" in the MessageManager so you can send multiple messages to different Activities/Services or you can use a List/Queue of Messenger "receiver" in the MessageManager so you can receive multiple message from different Activities/Services.
In the "MessageManager" instance you have a list of all messages received.
As you can see the connection between "Activity's Messenger" and "Service Messenger" using this "MessageManager" instance is automatic, it is done through the "OnServiceConnected" method and through the use of the "Handshake".
Hope this is helpful for you :) Thank you very much!
Bye :D
To follow up on #MrSnowflake answer with a code example.
This is the XABBER now open source Application class. The Application class is centralising and coordinating Listeners and ManagerInterfaces and more. Managers of all sorts are dynamically loaded. Activity´s started in the Xabber will report in what type of Listener they are. And when a Service start it report in to the Application class as started. Now to send a message to an Activity all you have to do is make your Activity become a listener of what type you need. In the OnStart() OnPause() register/unreg. The Service can ask the Application class for just that listener it need to speak to and if it's there then the Activity is ready to receive.
Going through the Application class you'll see there's a loot more going on then this.
As mentioned by Madhur, you can use a bus for communication.
In case of using a Bus you have some options:
Otto event Bus library (deprecated in favor of RxJava)
http://square.github.io/otto/
Green Robot’s EventBus
http://greenrobot.org/eventbus/
NYBus (RxBus, implemented using RxJava. very similar to the EventBus)
https://github.com/MindorksOpenSource/NYBus

Categories

Resources