In this article I’ve described:
- How to develop simple Java service for the Android Devices;
- How to communicate with a service from the other processes and a remote PC;
- How to install and start the service remotely from the PC.
1. Java Service Development for the Android Devices
Services are long running background processes provided by Android. They could be used for background tasks execution. Tasks can be different: background calculations, backup procedures, internet communications, etc. Services can be started on the system requests and they can communicate with other processes using the Android IPC channels technology. The Android system can control the service lifecycle depending on the client requests, memory and CPU usage. Note that the service has lower priority than any process which is visible for the user.
Let’s develop the simple example service. It will show scheduled and requested notifications to user. Service should be managed using the service request, communicated from the simple Android Activity and from the PC.
First we need to install and prepare environment:
- Download and install latest Android SDK from the official web site (http://developer.android.com);
- Download and install Eclipse IDE (http://www.eclipse.org/downloads/);
- Also we’ll need to install Android Development Tools (ADT) plug-in for Eclipse.
After the environment is prepared we can create Eclipse Android project. It will include sources, resources, generated files and the Android manifest.
1.1 Service class development
First of all we need to implement service class. It should be inherited from the
android.app.Service (http://developer.android.com/reference/android/app/Service.html) base class. Each service class must have the corresponding
<service> declaration in its package's manifest. Manifest declaration will be described later. Services, like the other application objects, run in the main thread of their hosting process. If you need to do some intensive work, you should do it in another thread.
In the service class we should implement abstract method
onBind. Also we override some other methods:
onCreate().It is called by the system when the service is created at the first time. Usually this method is used to initialize service resources. In our case the binder, task and timer objects are created. Also notification is send to the user and to the system log:
onStart(Intent intent, int startId).It is called by the system every time a client explicitly starts the service by calling
startService(Intent), providing the arguments it requires and the unique integer token representing the start request. We can launch background threads, schedule tasks and perform other startup operations.
onDestroy().It is called by the system to notify a Service that it is no longer used and is being removed. Here we should perform all operations before service is stopped. In our case we will stop all scheduled timer tasks.
onBind(Intent intent).It will return the communication channel to the service.
IBinderis the special base interface for a remotable object, the core part of a lightweight remote procedure call mechanism. This mechanism is designed for the high performance of in-process and cross-process calls. This interface describes the abstract protocol for interacting with a remotable object. The
IBinderimplementation will be described below.
To send system log output we can use static methods of the
android.util.Log class (http://developer.android.com/reference/android/util/Log.html). To browse system logs on PC you can use ADB utility command:
The notification feature is implemented in our service as the special runnable object. It could be used from the other threads and processes. The service class has method
showNotification, which can display message to user using the
Toast.makeText call. The runnable object also uses it:
Code will be executed in the service thread. To execute runnable method we can use the special object android.os.Handler. There are two main uses for the Handler: to schedule messages and runnables to be executed as some point in the future; and to place an action to be performed on a different thread than your own. Each Handler instance is associated with a single thread and that thread's message queue. To show notification we should set message and call post() method of the Handler’s object.
1.2 IPC Service
Each application runs in its own process. Sometimes you need to pass objects between processes and call some service methods. These operations can be performed using IPC. On the Android platform, one process can not normally access the memory of another process. So they have to decompose their objects into primitives that can be understood by the operating system , and "marshall" the object across that boundary for developer.
The AIDL IPC mechanism is used in Android devices. It is interface-based, similar to COM or Corba, but is lighter . It uses a proxy class to pass values between the client and the implementation.
AIDL (Android Interface Definition Language) is an IDL language used to generate code that enables two processes on an Android-powered device to communicate using IPC. If you have the code in one process (for example, in Activity) that needs to call methods of the object in another process (for example, Service), you can use AIDL to generate code to marshall the parameters.
Service interface example showed below supports only one sendNotification call:
IBinder interface for a remotable object is used by clients to perform IPC. Client can communicate with the service by calling Context’s
IBinder implementation could be retrieved from the onBind method. The
INotifyService interface implementation is based on the
android.os.Binder class (http://developer.android.com/reference/android/os/Binder.html):
As it was described above, the notifications could be send using the Handler object’s
post() method call. The
NotificaionRunnable object is passed as the method’s parameter.
On the client side we can request
IBinder object and work with it as with the
INotifyService interface. To connect to the service the
android.content.ServiceConnection interface implementation can be used. Two methods should be defined:
bindService method can be called from the client Activity context to connect to the service:
unbindService method can be called from the client Activity context to disconnect from the service:
1.3 Remote service control
Broadcasts are the way applications and system components can communicate. Also we can use broadcasts to control service from the PC. The messages are sent as Intents, and the system handles dispatching them, including starting receivers.
Intents can be broadcasted to
BroadcastReceivers, allowing messaging between applications. By registering a
BroadcastReceiver in application’s AndroidManifest.xml (using
<receiver> tag) you can have your application’s receiver class started and called whenever someone sends you a broadcast. Activity Manager uses the
IntentFilters, applications register to figure out which program should be used for a given broadcast.
Let’s develop the receiver that will start and stop notify service on request. The base class
android.content.BroadcastReceiver should be used for these purposes (http://developer.android.com/reference/android/content/BroadcastReceiver.html):
To send broadcast from the client application we use the
Context.sendBroadcast call. I will describe how to use receiver and send broadcasts from the PC in chapter 2.
1.4 Android Manifest
Every application must have an AndroidManifest.xml file in its root directory. The manifest contains essential information about the application to the Android system, the system must have this information before it can run any of the application's code. The core components of an application (its activities, services, and broadcast receivers) are activated by intents. An intent is a bundle of information (an Intent object) describing a desired action — including the data to be acted upon, the category of component that should perform the action, and other pertinent instructions. Android locates an appropriate component to respond to the intent, starts the new instance of the component if one is needed, and passes it to the Intent object.
We should describe 2 components for our service:
NotifyServiceclass is described in the
<service>tag. It will not start on intent. So the intent filtering is not needed.
ServiceBroadcastReceivedclass is described in the
<receiver>tag. For the broadcast receiver the intent filter is used to select system events:
2. Java service remote installation and start
2.1 Service installation
Services like the other applications for the Android platform can be installed from the special package with the .apk extension. Android package contains all required binary files and the manifest.
Before installing the service from the PC we should enable the USB Debugging option in the device Settings-Applications-Development menu and then connect device to PC via the USB.
On the PC side we will use the ADB utility which is available in the Android SDK tools directory. The ADB utility supports several optional command-line arguments that provide powerful features, such as copying files to and from the device. The shell command-line argument lets you connect to the phone itself and issue rudimentary shell commands.
We will use several commands:
- Remote shell command execution:
adb shell <command> <arguments>
- File send operation:
adb push <local path> <remote path>
- Package installation operation:
adb install <package>.apk
I’ll describe the package installation process in details. It consists of several steps which are performed by the ADB utility install command:
- First of all the .apk package file should be copied to the device. The ADB utility connects to the device and has limited “shell” user privileges. So almost all file system directories are write-protected for it. The /data/local/tmp directory is used as the temporary storage for package files. To copy package to the device use the command:
adb push NotifyService.apk /data/local/tmp
- Package installation. ADB utility uses special shell command to perform this operation. The “pm” (Package Manager?) utility is present on the Android devices. It supports several command line parameters which are described in the Appendix I. To install the package by yourself execute the remote shell command:
adb shell pm install /data/local/tmp/NotifyService.apk
- Cleanup. After the package is installed, ADB removes the temporary file stored in /data/local/tmp folder using the “rm” utility:
adb shell rm /data/local/tmp/NotifyService.apk.
- To uninstall package use the “pm” utility:
adb shell pm uninstall <package>
2.2 Remote service control
To be able to start and stop the
NotifyService from the PC we can use the “am” (Activity Manager?) utility which is present on the Android device. The command line parameters are described in the Appendix II. The “am” utility can send system broadcast intents. Our service has the broadcast receiver which will be launched by the system request.
NotifyService we can execute remote shell command:
adb shell am broadcast –a NotifyServiceStart
To stop the NotifyService we can execute remote shell command:
adb shell am broadcast –a NotifyServiceStop
Note, that the
NotifyServiceStop intents were described in the manifest file inside the
<receiver> … <intent-filter> tag. Other requests will not start the receiver.
Appendix I. PM Usage (from Android console)
pm [list|path|install|uninstall] pm list packages [-f] pm list permission-groups pm list permissions [-g] [-f] [-d] [-u] [GROUP] pm path PACKAGE pm install [-l] [-r] PATH pm uninstall [-k] PACKAGE The list packages command prints all packages. Use the -f option to see their associated file. The list permission-groups command prints all known permission groups. The list permissions command prints all known permissions, optionally only those in GROUP. Use the -g option to organize by group. Use the -f option to print all information. Use the -s option for a short summary. Use the -d option to only list dangerous permissions. Use the -u option to list only the permissions users will see. The path command prints the path to the .apk of a package. The install command installs a package to the system. Use the -l option to install the package with FORWARD_LOCK. Use the -r option to reinstall an exisiting app, keeping its data. The uninstall command removes a package from the system. Use the -k option to keep the data and cache directories around after the package removal.
Appendix II. AM Usage (from Android console)
am [start|broadcast|instrument] am start -D INTENT am broadcast INTENT am instrument [-r] [-e <ARG_NAME> <ARG_VALUE>] [-p <PROF_FILE>] [-w] <COMPONENT> INTENT is described with: [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>] [-c <CATEGORY> [-c <CATEGORY>] ...] [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...] [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...] [-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...] [-n <COMPONENT>] [-f <FLAGS>] [<URI>]
- Android Installation Guide.
- Android Developer reference.
- Jesse Burns. Developing Secure Mobile Applications for Android.
- Designing a Remote Interface Using AIDL
Download the Notify Service Source.
Continue reading with another article about Android apk development.