Reputation: 721
I finally got the Local Service Sample to work using the LocalServiceActivities.java and the Basics of Android : Part III – Android Services.
Here's my code Controller.java, LocalService.java, Binding.java, and ILocalService.java all baked together one after another separated by comment headers:
/**************************************************************************************************
* Filename: Controller.java
* Project name: Local Service Sample
* Application name: Local Service
* Description: This file contains the primary activity for this application
**************************************************************************************************/
package com.marie.localservicesample;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
/*
* Example of explicitly starting and stopping the local service.
* This demonstrates the implementation of a service that runs in the same
* process as the rest of the application, which is explicitly started and stopped
* as desired.
*/
//public static class Controller extends Activity {
public class Controller extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.local_service_controller);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.start);
button.setOnClickListener(mStartListener);
button = (Button)findViewById(R.id.stop);
button.setOnClickListener(mStopListener);
}
private OnClickListener mStartListener = new OnClickListener() {
public void onClick(View v) {
// Make sure the service is started. It will continue running
// until someone calls stopService(). The Intent we use to find
// the service explicitly specifies our service component, because
// we want it running in our own process and don't want other
// applications to replace it.
//startService(new Intent(Controller.this, LocalService.class));
Intent startSvc = new Intent(Controller.this, LocalService.class);
startSvc.putExtra(LocalService.EXTRA_MESSENGER, new Messenger(handler));
startSvc.putExtra(LocalService.EXTRA_SONG, 7);
startService(startSvc);
Intent binding = new Intent(Controller.this, Binding.class);
startActivity(binding);
}
};
private OnClickListener mStopListener = new OnClickListener() {
public void onClick(View v) {
// Cancel a previous call to startService(). Note that the
// service will not actually stop at this point if there are
// still bound clients.
stopService(new Intent(Controller.this,
LocalService.class));
}
};
/*
* This is a handler to be passed to the Service via a Messenger.
*/
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// simple handler test
String obj = (String) msg.obj;
Log.i("handleMessge", "obj: " + obj);
}
};
}
/**************************************************************************************************
* Filename: LocalService.java
* Project name: Local Service Sample
* Application name: Local Service
* Description: This file contains a local service
**************************************************************************************************/
package com.marie.localservicesample;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;
public class LocalService extends Service {
private NotificationManager mNM;
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
//private int NOTIFICATION = R.string.local_service_started;
private int NOTIFICATION = R.string.local_service_started;
private int statusCode = 10;
// This is the object that receives interactions from clients. See
// RemoteService for a more complete example.
private final IBinder mBinder = new LocalBinder();
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/**
* 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 implements ILocalService {
LocalService getService() {
return LocalService.this;
}
@Override
public int getStatusCode() {
return statusCode;
}
}
public static final String EXTRA_MESSENGER = "com.marie.localservicesample.EXTRA_MESSENGER";
private Messenger messenger;
public static final String EXTRA_SONG = "com.marie.localservicesample.EXTRA_SONG";
private int song;
@Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// Display a notification about us starting. We put an icon in the status bar.
showNotification();
Thread thr = new Thread(null, new ServiceWorker(), "BackgroundService");
thr.start();
}
/*
* This is the ServiceWorker thread that passes messages to the handler defined in
* the MainActivity class.
* NOTE: Instead of passing messages to a handler in MainActivity I would like
* it to pass messages to a handler defined in the RcvMessages activity.
*/
class ServiceWorker implements Runnable
{
public void run() {
// do background processing here... something simple
while (messenger == null);
// send a message to the handler
try {
Message msg = Message.obtain();
msg.obj = "Hello " + "Song " + song;
msg.arg1 = song;
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
// stop the service when done...
// LocalService.this.stopSelf();
// Or use the unbindBtn in the MainActivity class.
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
Bundle extras = intent.getExtras();
messenger = (Messenger)extras.get(EXTRA_MESSENGER);
try {
song = (Integer) extras.get(EXTRA_SONG);
} catch (NullPointerException e) {
e.printStackTrace();
song = 0;
}
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
@Override
public void onDestroy() {
// Cancel the persistent notification.
mNM.cancel(NOTIFICATION);
// Tell the user we stopped.
Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
}
/**
* Show a notification while this service is running.
*/
private void showNotification() {
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.local_service_started);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.stat_sample, text,
System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
//PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, LocalServiceActivities.Controller.class), 0);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Controller.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.local_service_label),
text, contentIntent);
// Send the notification.
mNM.notify(NOTIFICATION, notification);
}
/* Duplicate added by Eclipse
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
*/
}
/**************************************************************************************************
* Filename: Binding.java
* Project name: Local Service Sample
* Application name: Local Service
* Description: This file contains the binding for this application
**************************************************************************************************/
package com.marie.localservicesample;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
/*
* Example of binding and unbinding to the local service.
* This demonstrates the implementation of a service which the client will
* bind to, receiving an object through which it can communicate with the service.
*/
public class Binding extends Activity {
private boolean mIsBound;
private LocalService mBoundService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
mBoundService = ((LocalService.LocalBinder)service).getService();
ILocalService localService = (ILocalService)service;
int statusCode = localService.getStatusCode();
Log.d("Binding","called onServiceConnected. statusCode: " + statusCode);
Toast.makeText(Binding.this, R.string.local_service_connected,
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
mBoundService = null;
Log.d("Binding", "called onServiceDisconnected");
Toast.makeText(Binding.this, R.string.local_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
void doBindService() {
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(new Intent(Binding.this,
LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
doUnbindService();
}
private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
doBindService();
}
};
private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
doUnbindService();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.local_service_binding);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);
}
}
/**************************************************************************************************
* Filename: ILocalService.java
* Project name: Local Service Sample
* Application name: Local Service
* Description: This file contains an example interface for LocalService
**************************************************************************************************/
package com.marie.localservicesample;
public interface ILocalService {
// An example method for ILocalService
public int getStatusCode();
}
My question is why would anyone want to provide an ILocalService.java if they already have a startService() with a thread and a handler like I do? My ILocalService.java is a trivial demo which asks for a statusCode. As far as I can tell my ILocalService.java will only consist of getters of status and no setters. So will I only be asking for status info of my local service? What would be an example of a setter to my local service?
Upvotes: 0
Views: 2039
Reputation: 64700
Your binder looks like this:
public class LocalBinder extends Binder implements ILocalService {
LocalService getService() {
return LocalService.this;
}
@Override
public int getStatusCode() {
return statusCode;
}
}
The key part of interest is getService
. What this means is that any of your Activities that bind to your Service (since this is a local service) can actually obtain the service object and can call ANY functions on that service function, not just getters. You aren't limited to just the narrow communication channel that is startService
and the Intent
, you have the full method interface for the service object. In the past I've passed BluetoothDevice
instances, Handler
instances and other complex Java objects through to the service object.
Upvotes: 1