Developing own services and using system services in Android
This tutorial describes how to create and consume Android
services. It is based on Eclipse 4.2, Java 1.6 and Android 4.2.
A
service
is a component which runs in the background, without direct
interaction
with
the user. The Android platform provides and runs
predefined system services and every Android application can use
them, given the right permissions.
An Android application can, in addition to consuming the existing Android platform services, define and use new services.
An Android application can, in addition to consuming the existing Android platform services, define and use new services.
The Android platform provides pre-defined
services,
usually
exposed via
a specific Manager class. Access to them
can be
gained via
the
getSystemService()
method.
Every Android application can define and
start new
services
If you use asynchronous processing in activities or fragments the corresponding threads are still connected to the life-cycle of the corresponding activity. The Android system may decide to terminate them at any point in time.
Services run with a higher priority than inactive or invisible activities and therefore it is less likely that the Android system terminates them.
Defining your own services allows you to design very responsive applications. You can fetch the application via a service and once the application is started by the user, it can present fresh data to the user.
If you use asynchronous processing in activities or fragments the corresponding threads are still connected to the life-cycle of the corresponding activity. The Android system may decide to terminate them at any point in time.
Services run with a higher priority than inactive or invisible activities and therefore it is less likely that the Android system terminates them.
Defining your own services allows you to design very responsive applications. You can fetch the application via a service and once the application is started by the user, it can present fresh data to the user.
A
service
needs to be declared in the
A service runs by default in the same process as the application. in its own thread.
Therefore you need to use asynchronous processing in the service to to perform resource intensive tasks in the background.
AndroidManifest.xml
and the implementing class must extend the
Service
class
or one
of
its subclasses. The following code shows an example for
a
service
declaration and its implementation.
<service android:name="MyService" android:icon="@drawable/icon" android:label="@string/service_name" > </service>
public class MyService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { //TODO do something useful return Service.START_NOT_STICKY; } @Override public IBinder onBind(Intent intent) { //TODO for communication return IBinder implementation return null; } }
A service runs by default in the same process as the application. in its own thread.
Therefore you need to use asynchronous processing in the service to to perform resource intensive tasks in the background.
Services
which run in the process of the application are sometimes called
local
services.
You can also specify that your
The colon prefix before the name tells Android that the
Running a service in its own process will not block the application in case the service performs long running operations in its main thread. But as the services runs in its own process you need to use some interprocess communication (IPC) to communicate to your service from other parts.
Even if the service runs in its own process you need to use asynchronous processing to perform network access because Android does not allow network access in the main thread of a process.
Service
runs in
a separate
process via the
android:process=":process_description"
attribute.
<service android:name="WordService" android:process=":my_process" android:icon="@drawable/icon" android:label="@string/service_name" > </service>
The colon prefix before the name tells Android that the
Service
is private to its declaring application. If the colon is not used the
Service
would be a global process and can be used by other Android
applications.
Running a service in its own process will not block the application in case the service performs long running operations in its main thread. But as the services runs in its own process you need to use some interprocess communication (IPC) to communicate to your service from other parts.
Even if the service runs in its own process you need to use asynchronous processing to perform network access because Android does not allow network access in the main thread of a process.
Running a
service
in its own process gives it its own memory address space and a
garbage collector of the virtual machine in this process does not
affect the application process.
Application rarely need to run a service in its own process. Running a services in its own process make the communication of the other Android components and the service harder to implement.
If you want to make a service to other Android application available, they must run in their own process.
Application rarely need to run a service in its own process. Running a services in its own process make the communication of the other Android components and the service harder to implement.
If you want to make a service to other Android application available, they must run in their own process.
You can also extend the
The
The
IntentService
class for your service implementation.
The
IntentService
is used to perform a certain task in the background. Once
done, the
instance of
IntentService
terminate itself automatically. Examples for its usage would be to
download a certain resources from the Internet.
The
IntentService
class offers the
onHandleIntent()
method which will be asynchronously called by the Android system.
For an introduction into
BroadcastReceiver
please see
Android Broadcast Receiver tutorial
.
An Android component (service, receiver, activity)
can start and
trigger a
service
via the
If the service started the
Once the service is started the method call to start the service triggers
If
Alternatively to
startService(intent)
method. This method call starts the
service
if it is not running.
Intent service = new Intent(context, MyService.class); context.startService(service);
If the service started the
onCreate()
method is called.
Once the service is started the method call to start the service triggers
startService(intent)
method in the
service.
It passes in the
Intent
for the
startService(intent)
call.
If
startService(intent)
is called while the
service
is running, its
onStartCommand()
is also called. Therefore your
service
needs to be prepared that
onStartCommand()
can be called several times. This method is called in the main user
interface thread therefore it cannot be called simultaneously from
two different threads.
Alternatively to
startService(intent)
you can also start a
service
via the
bindService()
method call. This allows you to communicate directly with the
service.
You
stop
a
service
via the
A service can stop itself by calling the
stopService()
method. No matter how frequently you started the
service
with
startService(intent)
a call to
stopService()
stops it.
A service can stop itself by calling the
stopSelf()
method.
If the
activity
wants to
interact with the
service
it can use
the
This method requires as parameter a
This
Afterwards the binding was done the
Services started with
bindService()
method to start the
service.
This method requires as parameter a
ServiceConnection
object which allows to
connect
to the
service. In the
service
the
onBind()
method is called. This method
returns a
IBinder
object to the
ServiceConnection.
This
IBinder
object
can be used
by the
activity
to communicate
with the
service.
Afterwards the binding was done the
onStartCommand()
method is called with the
Intent
data provided by the activity.
startService()
also allows you to provide a flag which determines
the
restart
behavior
of the services.
Service.START_STICKY
is used for
services
which are explicit started or stopped. If these
services are terminated by the Android system, they are restarted if
sufficient resource are available again.
Services started with
Service.START_NOT_STICKY
are not automatically restarted if terminated by the Android system.
As with
activities
the Android system may terminate the process of a service at any
time
to save resources. For this reason you cannot simple use a
For correct scheduling of the
TimerTask
in the service to ensure that it is executed on a regular basis.
For correct scheduling of the
Service
use the
AlarmManager
class. The following code demonstrates how to do this.
Calendar cal = Calendar.getInstance(); Intent intent = new Intent(this, MyService.class); PendingIntent pintent = PendingIntent.getService(this, 0, intent, 0); AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE); // Start every 30 seconds alarm.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 30*1000, pintent);
There are several way for an
activity
to communicate with an
service
and vice versa. This section dicusses the different ways and gives
recommendation which to use.
If the
Service
is started in the same process as the
activity, the
activity
can directly bind to the service. This is a relatively simple and
efficient way to communication.
You can also use dynamically registered receivers for the
communication.
For example your
activity
can dynamically register a
receiver
and the
service
sends outs corresponding events.
To bind to a
service
which runs in a different process you need to use Inter Process
Communication (IPC) as the data needs to be send between different
processes. For this you need to create a AIDL file which looks
similar to an Java interface but ends with the
This approach is required if your service should be provided to other applications, otherwise you should prefer a local service.
.aidl
file extension
and is only allowed to extend other AIDL files.
This approach is required if your service should be provided to other applications, otherwise you should prefer a local service.
The
service
receives data from the starting Android component and can use this data.
If the service should be communicating back to the
activity
it can receive an object of type
A
The following example demonstrates how to use the
Create a new project called de.vogella.android.intentservice.download with a activity called MainActivity.
Create a service "DownloadService" by creating the following class and the entry in
Change the
Change
If you run your example and press the button, the download should be performed by the
http://www.vogella.com/code/index.html
Messenger
via the
Intent
data it receives from the
activity. If the
Messenger
is bound to a
Handler
in the
activity
the
service
can send objects of type
Message
to the
activity.
A
Messenger
is parcelable, which means it can be passed to another process and
you
can use this object to send
Messages
to the
Handler
in the
activity.
Messenger
provides also the method
getBinder()
which allows to pass a
Messenger
to the
activity. The
activity
can therefore send
Messages
to the
service.6. Tutorial: Using IntentService
IntentService
class to download a file from the Internet. Once done the
IntentService
will use an instance of the
Messenger
class to inform the
activity
which started the service about the location of the downloaded file.
Create a new project called de.vogella.android.intentservice.download with a activity called MainActivity.
Create a service "DownloadService" by creating the following class and the entry in
AndroidManifest.xml. Also add the permission to
write to external storage and to access
the Internet to the file.
package de.vogella.android.intentservice.download; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import android.app.Activity; import android.app.IntentService; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Message; import android.os.Messenger; import android.util.Log; public class DownloadService extends IntentService { private int result = Activity.RESULT_CANCELED; public DownloadService() { super("DownloadService"); } // Will be called asynchronously be Android @Override protected void onHandleIntent(Intent intent) { Uri data = intent.getData(); String urlPath = intent.getStringExtra("urlpath"); String fileName = data.getLastPathSegment(); File output = new File(Environment.getExternalStorageDirectory(), fileName); if (output.exists()) { output.delete(); } InputStream stream = null; FileOutputStream fos = null; try { URL url = new URL(urlPath); stream = url.openConnection().getInputStream(); InputStreamReader reader = new InputStreamReader(stream); fos = new FileOutputStream(output.getPath()); int next = -1; while ((next = reader.read()) != -1) { fos.write(next); } // Sucessful finished result = Activity.RESULT_OK; } catch (Exception e) { e.printStackTrace(); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } Bundle extras = intent.getExtras(); if (extras != null) { Messenger messenger = (Messenger) extras.get("MESSENGER"); Message msg = Message.obtain(); msg.arg1 = result; msg.obj = output.getAbsolutePath(); try { messenger.send(msg); } catch (android.os.RemoteException e1) { Log.w(getClass().getName(), "Exception sending message", e1); } } } }
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.vogella.android.intentservice.download" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".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> <service android:name="DownloadService" > </service> </application> </manifest>
Change the
main.xml
layout to the following.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick" android:text="Button" /> </LinearLayout>
Change
MainActivity
to the following.
package de.vogella.android.intentservice.download; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.view.View; import android.widget.Toast; public class MainActivity extends Activity { private Handler handler = new Handler() { public void handleMessage(Message message) { Object path = message.obj; if (message.arg1 == RESULT_OK && path != null) { Toast.makeText(MainActivity.this, "Downloaded" + path.toString(), Toast.LENGTH_LONG) .show(); } else { Toast.makeText(MainActivity.this, "Download failed.", Toast.LENGTH_LONG).show(); } }; }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public void onClick(View view) { Intent intent = new Intent(this, DownloadService.class); // Create a new Messenger for the communication back Messenger messenger = new Messenger(handler); intent.putExtra("MESSENGER", messenger); intent.setData(Uri.parse("http://www.vogella.com/index.html")); intent.putExtra("urlpath", "http://www.vogella.com/index.html"); startService(intent); } }
If you run your example and press the button, the download should be performed by the
Service
and once done the
activity
should show a Toast with the file name.
The following example demonstrates how to use the
Create a new project called de.vogella.android.intentservice.download with a activity called MainActivity.
Create a service "DownloadService" by creating the following class and the entry in
Change the
Change
If you run your example and press the button, the download should be performed by the
IntentService
class to download a file from the Internet. Once done the
IntentService
will use an instance of the
Messenger
class to inform the
activity
which started the service about the location of the downloaded file.
Create a new project called de.vogella.android.intentservice.download with a activity called MainActivity.
Create a service "DownloadService" by creating the following class and the entry in
AndroidManifest.xml. Also add the permission to
write to external storage and to access
the Internet to the file.
package de.vogella.android.intentservice.download; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import android.app.Activity; import android.app.IntentService; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Message; import android.os.Messenger; import android.util.Log; public class DownloadService extends IntentService { private int result = Activity.RESULT_CANCELED; public DownloadService() { super("DownloadService"); } // Will be called asynchronously be Android @Override protected void onHandleIntent(Intent intent) { Uri data = intent.getData(); String urlPath = intent.getStringExtra("urlpath"); String fileName = data.getLastPathSegment(); File output = new File(Environment.getExternalStorageDirectory(), fileName); if (output.exists()) { output.delete(); } InputStream stream = null; FileOutputStream fos = null; try { URL url = new URL(urlPath); stream = url.openConnection().getInputStream(); InputStreamReader reader = new InputStreamReader(stream); fos = new FileOutputStream(output.getPath()); int next = -1; while ((next = reader.read()) != -1) { fos.write(next); } // Sucessful finished result = Activity.RESULT_OK; } catch (Exception e) { e.printStackTrace(); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } Bundle extras = intent.getExtras(); if (extras != null) { Messenger messenger = (Messenger) extras.get("MESSENGER"); Message msg = Message.obtain(); msg.arg1 = result; msg.obj = output.getAbsolutePath(); try { messenger.send(msg); } catch (android.os.RemoteException e1) { Log.w(getClass().getName(), "Exception sending message", e1); } } } }
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.vogella.android.intentservice.download" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".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> <service android:name="DownloadService" > </service> </application> </manifest>
Change the
main.xml
layout to the following.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick" android:text="Button" /> </LinearLayout>
Change
MainActivity
to the following.
package de.vogella.android.intentservice.download; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.view.View; import android.widget.Toast; public class MainActivity extends Activity { private Handler handler = new Handler() { public void handleMessage(Message message) { Object path = message.obj; if (message.arg1 == RESULT_OK && path != null) { Toast.makeText(MainActivity.this, "Downloaded" + path.toString(), Toast.LENGTH_LONG) .show(); } else { Toast.makeText(MainActivity.this, "Download failed.", Toast.LENGTH_LONG).show(); } }; }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public void onClick(View view) { Intent intent = new Intent(this, DownloadService.class); // Create a new Messenger for the communication back Messenger messenger = new Messenger(handler); intent.putExtra("MESSENGER", messenger); intent.setData(Uri.parse("http://www.vogella.com/index.html")); intent.putExtra("urlpath", "http://www.vogella.com/index.html"); startService(intent); } }
If you run your example and press the button, the download should be performed by the
Service
and once done the
activity
should show a Toast with the file name.
No comments:
Post a Comment