Android Service Tutorial

Lars Vogel


Version 2.6


Copyright © 2011, 2012, 2013 Lars Vogel

22.05.2013

Revision History
Revision 0.1 07.03.2011 Lars
Vogel
created
Revision 0.2 - 2.6 08.03.2011 - 22.05.2013 Lars
Vogel
bug fixed and enhancements

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.

Table of Contents

1. Android Services 1.1. Service 1.2. Android Platform Service 1.3. Defining new services 2. Defining services 2.1. Declaring own services 2.2. Running a Services in its own process 2.3. When to run a service in a separate process? 2.4. Intent Services 3. Broadcast receiver 4. Starting services 4.1. Start a service 4.2. Stopping a service 4.3. Binding between activities and local services 4.4. Starting services regularly via AlarmManager 5. Communicating with Services 5.1. Activity binding to local service 5.2. Using receiver 5.3. AIDL for services in a different process 5.4. Sending Intent data and bundle to the services 5.5. Handler and Messenger 6. Tutorial: Using IntentService 7. Tutorial: Define and consume local service 8. Thank you 9. Questions and Discussion 10. Links and Literature 10.1. Source Code 10.2. Android Resources 10.3. vogella Resources

1. Android Services

1.1. Service

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.

1.2. Android Platform Service

The Android platform provides pre-defined services, usually exposed via a specific Manager class. Access to them can be gained via thegetSystemService()method.

1.3. Defining new services

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.

2. Defining services

2.1. Declaring own services

A service needs to be declared in theAndroidManifest.xmland the implementing class must extend theServiceclass 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.

Serviceswhich run in the process of the application are sometimes called localservices.

2.2. Running a Services in its own process

You can also specify that yourServiceruns in a separate process via theandroid: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 theServiceis private to its declaring application. If the colon is not used theServicewould 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.

2.3. When to run a service in a separate 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.

2.4. Intent Services

You can also extend theIntentServiceclass for your service implementation.

TheIntentServiceis used to perform a certain task in the background. Once done, the instance ofIntentServiceterminate itself automatically. Examples for its usage would be to download a certain resources from the Internet.

TheIntentServiceclass offers theonHandleIntent()method which will be asynchronously called by the Android system.

3. Broadcast receiver

For an introduction intoBroadcastReceiverplease see Android Broadcast Receiver tutorial .

4. Starting services

4.1. Start a service

An Android component (service, receiver, activity) can start and trigger a service via thestartService(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 theonCreate()method is called.

Once the service is started the method call to start the service triggersstartService(intent)method in the service. It passes in theIntentfor thestartService(intent)call.

IfstartService(intent)is called while the service is running, itsonStartCommand()is also called. Therefore your service needs to be prepared thatonStartCommand()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 tostartService(intent)you can also start a service via thebindService()method call. This allows you to communicate directly with the service.

4.2. Stopping a service

You stop a service via thestopService()method. No matter how frequently you started the service withstartService(intent)a call tostopService()stops it.

A service can stop itself by calling thestopSelf()method.

4.3. Binding between activities and local services

If the activity wants to interact with the service it can use thebindService()method to start the service.

This method requires as parameter aServiceConnectionobject which allows to connect to the service. In the service theonBind()method is called. This method returns aIBinderobject to theServiceConnection.

ThisIBinderobject can be used by the activity to communicate with the service.

Afterwards the binding was done theonStartCommand()method is called with theIntentdata provided by the activity.

startService()also allows you to provide a flag which determines the restart behavior of the services.Service.START_STICKYis 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 withService.START_NOT_STICKYare not automatically restarted if terminated by the Android system.

4.4. Starting services regularly via AlarmManager

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 aTimerTaskin the service to ensure that it is executed on a regular basis.

For correct scheduling of theServiceuse theAlarmManagerclass. 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);


5. Communicating with Services

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.

5.1. Activity binding to local service

If theServiceis 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.

5.2. Using receiver

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.

5.3. AIDL for services in a different process

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.aidlfile 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.

5.4. Sending Intent data and bundle to the services

The service receives data from the starting Android component and can use this data.

5.5. Handler and Messenger

If the service should be communicating back to the activity it can receive an object of typeMessengervia theIntentdata it receives from the activity. If theMessengeris bound to aHandlerin the activity theservicecan send objects of typeMessageto the activity.

AMessengeris parcelable, which means it can be passed to another process and you can use this object to sendMessagesto theHandlerin the activity.

Messengerprovides also the methodgetBinder()which allows to pass aMessengerto the activity. The activity can therefore sendMessagesto the service.

6. Tutorial: Using IntentService

The following example demonstrates how to use theIntentServiceclass to download a file from the Internet. Once done theIntentServicewill use an instance of theMessengerclass 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 inAndroidManifest.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 themain.xmllayout 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>


ChangeMainActivityto 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 theServiceand once done the activity should show a Toast with the file name.

7. Tutorial: Define and consume local service

The following chapter demonstrates how to create and consume a service from an activity. The service startes at boot time and periodically fetch data. Theactivitybinds itself to the service and use connection to communicate with the service.

Create a new project called de.vogella.android.ownservice.local with an activity called MainActivity.

Create theLocalWordServiceclass.


package de.vogella.android.ownservice.local; import java.util.ArrayList; import java.util.List; import java.util.Random; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; public class LocalWordService extends Service { private final IBinder mBinder = new MyBinder(); private ArrayList<String> list = new ArrayList<String>(); @Override public int onStartCommand(Intent intent, int flags, int startId) {

    Random random = new Random(); if (random.nextBoolean()) {
      list.add("Linux");
    } if (random.nextBoolean()) {
      list.add("Android");
    } if (random.nextBoolean()) {
      list.add("iPhone");
    } if (random.nextBoolean()) {
      list.add("Windows7");
    } if (list.size() >= 20) {
      list.remove(0);
    } return Service.START_NOT_STICKY;
  } @Override public IBinder onBind(Intent arg0) { return mBinder;
  } public class MyBinder extends Binder {
    LocalWordService getService() { return LocalWordService.this;
    }
  } public List<String> getWordList() { return list;
  }

}


Create the following two classes, which will be registered asBroadcastReceivers.


package de.vogella.android.ownservice.local; import java.util.Calendar; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class MyScheduleReceiver extends BroadcastReceiver { // Restart service every 30 seconds private static final long REPEAT_TIME = 1000 * 30; @Override public void onReceive(Context context, Intent intent) {
    AlarmManager service = (AlarmManager) context
        .getSystemService(Context.ALARM_SERVICE);
    Intent i = new Intent(context, MyStartServiceReceiver.class);
    PendingIntent pending = PendingIntent.getBroadcast(context, 0, i,
        PendingIntent.FLAG_CANCEL_CURRENT);
    Calendar cal = Calendar.getInstance(); // Start 30 seconds after boot completed cal.add(Calendar.SECOND, 30); // // Fetch every 30 seconds // InexactRepeating allows Android to optimize the energy consumption service.setInexactRepeating(AlarmManager.RTC_WAKEUP,
        cal.getTimeInMillis(), REPEAT_TIME, pending); // service.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), // REPEAT_TIME, pending); }
}



package de.vogella.android.ownservice.local; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class MyStartServiceReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) {
    Intent service = new Intent(context, LocalWordService.class);
    context.startService(service);
  }
}



<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.vogella.android.ownservice.local" android:versionCode="1" android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:icon="@drawable/icon" 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=".LocalWordService" android:icon="@drawable/icon" android:label="@string/service_name" >
        </service>

        <receiver android:name="MyScheduleReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
        <receiver android:name="MyStartServiceReceiver" >
        </receiver>
    </application>

</manifest>


Change the layout file of the activity similar to the following example.


<?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" >

    <TextView
        android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello" />

    <Button
        android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="showServiceData" android:text="Button" >
    </Button>

    <ListView
        android:id="@id/android:list" android:layout_width="match_parent" android:layout_height="wrap_content" >
    </ListView>

</LinearLayout>


Change your activity class to the following code.


package de.vogella.android.ownservice.local; import java.util.ArrayList; import java.util.List; import android.app.ListActivity; 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.view.View; import android.widget.ArrayAdapter; import android.widget.Toast; public class MainActivity extends ListActivity { private LocalWordService s;

  
  
  
  

  

  

   
   
/** Called when the activity is first created. */
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); wordList = new ArrayList<String>(); adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, wordList); setListAdapter(adapter); doBindService(); } private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder binder) { s = ((LocalWordService.MyBinder) binder).getService(); Toast.makeText(MainActivity.this, "Connected", Toast.LENGTH_SHORT).show(); } public void onServiceDisconnected(ComponentName className) { s = null; } }; private ArrayAdapter<String> adapter; private List<String> wordList; void doBindService() { bindService(new Intent(this, LocalWordService.class), mConnection, Context.BIND_AUTO_CREATE); } public void showServiceData(View view) { if (s != null) { Toast.makeText(this, "Number of elements" + s.getWordList().size(), Toast.LENGTH_SHORT).show(); wordList.clear(); wordList.addAll(s.getWordList()); adapter.notifyDataSetChanged(); } } }


8. Thank you


Please help me to support this article:

Flattr this


9. Questions and Discussion

Before posting questions, please see the vogella FAQ. If you have questions or find an error in this article please use the www.vogella.com Google Group. I have created a short list how to create good questions which might also help you.

10. Links and Literature

10.1. Source Code

Source Code of Examples

10.3. vogella Resources

vogella Training Android and Eclipse Training from the vogella team

Android Tutorial Introduction to Android Programming

GWT Tutorial Program in Java and compile to JavaScript and HTML

Eclipse RCP Tutorial Create native applications in Java

JUnit Tutorial Test your application

Git Tutorial Put everything you have under distributed version control system

转载于:https://my.oschina.net/zhuzihasablog/blog/135818

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值