首先,让我们确认下什么是service?
service就是android系统中的服务,它有这么几个特点:它无法与用户直接进行交互、它必须由用户或者其他程序显式的启动、它的优先级比较高,它比处于前台的应用优先级低,但是比后台的其他应用优先级高,这就决定了当系统因为缺少内存而销毁某些没被利用的资源时,它被销毁的概率很小哦。
那么,什么时候,我们需要使用service呢?
我们知道,service是运行在后台的应用,对于用户来说失去了被关注的焦点。这就跟我们打开了音乐播放之后,便想去看看图片,这时候我们还不想音乐停止,这里就会用到service;又例如,我们打开了一个下载链接之后,我们肯定不想瞪着眼睛等他下载完再去做别的事情,对吧?这时候如果我们想手机一边在后台下载,一边可以让我去看看新闻啥的,就要用到service。
service分类:
一般我们认为service分为两类,本地service和远程service。
本地service顾名思义,那就是和当前应用在同一个进程中的service,彼此之间拥有共同的内存区域,所以对于某些数据的共享特别的方便和简单;
远程service:主要牵扯到不同进程间的service访问。因为android的系统安全的原因导致了我们在不同的进程间无法使用一般的方式共享数据。在这里android为我们提供了一个AIDL工具。(android interface description language)android接口描述语言。在后边我们将会对其进行详细的介绍。
service生命周期:
和Activity相比,service的生命周期已经简单的不能再简单了,只有onCreate()->onStart()->onDestroy()三个方法。
Activity中和service有关的方法:
startService(Intent intent):启动一个service
stopService(Intent intent) :停止一个service
如果我们想使用service中的一些数据或者访问其中的一些方法,那么我们就要通过下面的方法:
public boolean bindService(Intent intent, ServiceConnection conn, int flags) ;
public void unbindService(ServiceConnection conn);
intent是跳转到service的intent,如 Intent intent = new Intent(); intent.setClass(this,MyService.class);
conn则是一个代表与service连接状态的类,当我们连接service成功或失败时,会主动触发其内部的onServiceConnected或onServiceDisconnected方法。如果我们想要访问service中的数据,可以在onServiceConnected()方法中进行实现,例如
- /**
- * 链接到service时触发。
- * name 链接到service组件的名称
- * service 在service中调用onBund时返回的IBinder,主要用来进行信息的交流
- */
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.i("通知", "链接成功!");
- MyBinder binder = (MyBinder)service;
- MyService myService = binder.getMyService();
- int count = myService.getCount();
- Log.i("通知", "count="+count);
- }
-------------------------------------------------------------------
使用service的步骤:
第一步:我们要继承service类,实现自己的service。
如果想要访问service中的某些值,我们通常会提供一个继承了Binder的内部类,通过onBund()方法返回给service请求。这里实际上巧妙的利用了内部类能够访问外部类属性的特点。
第二步:在androidManifest.xml中进行注册,如:
<!--
service配置开始
-->
<service android:name="MyService"></service>
<!--
service配置结束
-->
第三步:在activity中进行启动、绑定、解绑或者停止service。
(很多书上说,service与用户是不能交互的,其实这话很不正确,我们完全可以通过activity与service进行交互嘛!我觉得,确切的说法应该是service与用户不能进行直接的交互)。
------------------------------------------------------
下边提供一个调用service听音乐的例子:
activity代码:
- package cn.com.chenzheng_java;
- import cn.com.chenzheng_java.MyService.MyBinder;
- import android.app.Activity;
- import android.content.ComponentName;
- 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;
- /**
- * @description 对service进行简单的应用
- * @author chenzheng_java
- * @since 2011/03/18
- *
- */
- public class ServiceActivity extends Activity implements OnClickListener{
- private Button button_start ;
- private Button button_bind ;
- private Button button_destroy ;
- private Button button_unbind;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.service);
- button_start = (Button) findViewById(R.id.button1_service);
- button_bind = (Button) findViewById(R.id.button2_service);
- button_destroy = (Button) findViewById(R.id.button3_service);
- button_unbind = (Button) findViewById(R.id.button4_service);
- button_start.setOnClickListener(this);
- button_bind.setOnClickListener(this);
- button_destroy.setOnClickListener(this);
- button_unbind.setOnClickListener(this);
- }
- private class MyServiceConnection implements ServiceConnection{
- /**
- * 链接到service时触发。
- * name 链接到service组件的名称
- * service 在service中调用onBund时返回的IBinder,主要用来进行信息的交流
- */
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.i("通知", "链接成功!");
- MyBinder binder = (MyBinder)service;
- MyService myService = binder.getMyService();
- int count = myService.getCount();
- Log.i("通知", "count="+count);
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.i("通知", "链接未成功!");
- }
- }
- private MyServiceConnection serviceConnection = new MyServiceConnection();
- @Override
- public void onClick(View v) {
- if(v == button_start){
- Intent intent = new Intent();
- intent.setClass(getApplicationContext(), MyService.class);
- startService(intent);
- }
- if(v == button_bind){
- Intent intent = new Intent();
- intent.setClass(getApplicationContext(), MyService.class);
- bindService(intent,serviceConnection , BIND_AUTO_CREATE);
- }
- if(v==button_destroy){
- Intent intent = new Intent();
- intent.setClass(getApplicationContext(), MyService.class);
- stopService(intent);
- }
- if(v==button_unbind){
- unbindService(serviceConnection);
- }
- }
- }
继承service的类:
- package cn.com.chenzheng_java;
- import android.app.Service;
- import android.content.Intent;
- import android.media.MediaPlayer;
- import android.os.Binder;
- import android.os.IBinder;
- import android.util.Log;
- /**
- * @description 实现自己的service
- * @author chenzheng_java
- * @since 2011/03/18
- */
- public class MyService extends Service {
- MediaPlayer mediaPlayer;
- /**
- * 当用户调用bindService方法时会触发该方法 返回一个IBinder对象,我们可以通过该对象,对service中 的某些数据进行访问
- */
- @Override
- public IBinder onBind(Intent intent) {
- Log.i("通知", "service绑定成功!");
- return new MyBinder();
- }
- @Override
- public void onCreate() {
- Log.i("通知", "service创建成功!");
- mediaPlayer = MediaPlayer.create(this, R.raw.aiweier);
- mediaPlayer.setLooping(false);
- super.onCreate();
- }
- @Override
- public void onDestroy() {
- mediaPlayer.stop();
- Log.i("通知", "service销毁成功!");
- super.onDestroy();
- }
- @Override
- public void onRebind(Intent intent) {
- Log.i("通知", "service重新绑定成功!");
- super.onRebind(intent);
- }
- @Override
- public void onStart(Intent intent, int startId) {
- mediaPlayer.start();
- Log.i("通知", "service start成功!");
- super.onStart(intent, startId);
- }
- @Override
- public boolean onUnbind(Intent intent) {
- mediaPlayer.stop();
- Log.i("通知", "service解绑成功!");
- return super.onUnbind(intent);
- }
- private int count = 100;
- public int getCount() {
- return count;
- }
- public void setCount(int count) {
- this.count = count;
- }
- public class MyBinder extends Binder {
- /**
- * @return 返回一个个人的service对象
- */
- MyService getMyService() {
- return MyService.this;
- }
- }
- }
service.xml代码:
- <?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">
- <Button android:text="启动" android:id="@+id/button1_service" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
- <Button android:text="绑定" android:id="@+id/button2_service" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
- <Button android:text="销毁" android:id="@+id/button3_service" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
- <Button android:text="解绑" android:id="@+id/button4_service" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
- </LinearLayout>
androidManifest.xml
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="cn.com.chenzheng_java"
- android:versionCode="1"
- android:versionName="1.0">
- <uses-sdk android:minSdkVersion="8" />
- <application android:icon="@drawable/icon" android:label="@string/app_name">
- <activity android:name="ServiceActivity"
- 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配置开始
- -->
- <service android:name="MyService"></service>
- <!--
- service配置结束
- -->
- </application>
- </manifest>
最终效果图:
---------------------------------------------------------------------
首先描述下我们想要实现的内容,我们希望在一个应用中通过点击按钮,去操作另一个进程中应用的音乐播放功能。
如图,我们点击“播放”时,系统就会去远程调用我们提供的一个service(与当前service不是同一个应用哦),然后操作service中的音乐播放,点击“停止”则会终止播放。想要重新播放的话,必须先点“销毁service”,再点播放按钮哦。(至于这里为什么要先点销毁按钮才能播放,完全是为了给大家展示下,远程调用service时,怎么去解绑service)。
在这个例子中,我们用到了一个非常重要的概念,AIDL。
AIDL(android interface definition language)android接口描述语言,其主要的作用就是允许我们在不同的进程间进行通信。
我们都知道每个应用程序都运行在各自的进程中,并且android平台是不允许不同进程间进行直接的对象数据等传递的。如果我们非要进行进程间的通讯,那么我们就必须将我们的数据对象分解成一个个微小的、可以被操作系统理解的数据单元,然后有序的通过进程边界的检查。
如果让我们自己实现,那么这个过程都愁死一批批的程序人了。幸好android,为我们解决了这个问题,这就是AIDL出现的原因了。
AIDL (Android Interface Definition Language)是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。
AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。
注意:AIDL只支持方法,不能定义静态成员,并且方法也不能有类似public等的修饰符;AIDL运行方法有任何类型的参数和返回值,在java的类型中,以下的类型使用时不需要导入包(import),基本数据类型、String、Map、List.当然为了避免出错,建议只要使用了,就导入包。
-------------------------------------------------------------------------------
使用AIDL的步骤:
服务端(提供服务):
第一步:定义一个*.aidl文件,该文件里是符合aidl语言规范的接口定义,里面定义了外部应用可以访问的方法。当我们保存该文件的时候,eclipse会自动为我们在gen文件夹下生成一个相应的java接口文件。例如RemoteServiceInterface.java
第二步:定义一个自己的service, 并将其注册到androidManifest.xml文件中,例如:
<service android:name="MyRemoteService">
<intent-filter>
<action android:name="cn.com.chenzheng_java.remote"/>
</intent-filter>
</service>
注意这里一定要提供一个intent-filter,我们的客户端进程就是通过该action访问到服务端进程的哦。
我们都知道,在实现自己的service时,为了其他应用可以通过bindService来和我们的service进行交互,我们都要实现service中的onBind()方法,并且返回一个继承了Binder的内部类;在这里,eclipse自动为我们生成的RemoteServiceInterface.java中有一个实现了Binder的内部类,RemoteServiceInterface.Stub。AIDL要求我们,在这里不能再直接去实现Binder类了,而是去实现,AIDL提供给我们的Stub类。 实现stub类的同时,AIDL还要求我们同时实现我们在接口中定义的各种服务的具体实现。至此为止,我们的服务端已经和我们的aidl文件绑定到一起了哦。
客户端:
第一步:客户端要想使用该服务,肯定要先知道我们的服务在aidl文件中到底对外提供了什么服务,对吧?所以,第一步,我们要做的就是,将aidl文件拷贝一份到客户端的程序中(这里一定要注意,包路径要和服务端的保持一致哦,例如服务端为cn.com.chenzheng_java.remote.a.aidl,那么在客户端这边也应该是这个路径)。
第二步:我们都知道,想要和service交互,我们要通过bindService方法,该方法中有一个ServiceConnection类型的参数。而我们的主要代码便是在该接口的实现中。
第三步:在ServiceConnection实现类的onServiceConnected(ComponentName name, IBinder service)方法中通过类似remoteServiceInterface = RemoteServiceInterface.Stub.asInterface(service);方式就可以获得远程服务端提供的服务的实例,然后我们就可以通过remoteServiceInterface 对象调用接口中提供的方法进行交互了。(这里的关键是通过*.Stub.asInterface(service);方法获取一个aidl接口的实例哦)
我们前面在服务端中说过了,必须提供一个intent-filter来匹配请求是否合法,所以我们在客户端访问服务的时候,还必须传递包含了匹配action的Intent哦。
--------------------------------------------------------------------------------------
下边整体是代码:
远程服务端:
RemoteServiceInterface.aidl
- package cn.com.chenzheng_java.remote;
- /**AIDL的语法和Interface的语法稍微有些不同,
- *它里面只能有方法,并且java中的那些修饰词如public等,在这里是不支持的.
- *当我们在eclipse中添加一个以.aidl结尾的AIDL文件时,如果你的格式正确,那么
- *在gen目录下,你就会看到系统根据你提供AIDL文件自动为你生成的相应的java类
- *@author chenzheng_java
- */
- interface RemoteServiceInterface {
- void startMusic();
- void stopMusic();
- }
MyRemoteService.java
- package cn.com.chenzheng_java.remote;
- import java.io.IOException;
- import android.app.Service;
- import android.content.Intent;
- import android.media.MediaPlayer;
- import android.os.IBinder;
- import android.os.RemoteException;
- /**
- * @description 远程service
- * @author chenzheng_java
- *
- */
- public class MyRemoteService extends Service {
- MediaPlayer mediaPlayer;
- @Override
- public IBinder onBind(Intent intent) {
- return new MyBinder();
- }
- @Override
- public void onCreate() {
- /*
- * MediaPlayer.create方法第一个参数实际上为context对象,这里我们直接传递service给它,
- * 是因为service本身也是继承了context的。
- */
- mediaPlayer = MediaPlayer.create(MyRemoteService.this, R.raw.aiweier);
- super.onCreate();
- }
- /**
- * @description 这里一定要注意,继承的不再是Binder,而是系统自动为我们生成的
- * Binder的一个内部类,叫做Stub。我们通过继承Stub类,然后实现AIDL
- * 中定义的方法,便等于对接口的方法进行了具体的实现。
- * @author chenzheng_java
- *
- */
- private class MyBinder extends RemoteServiceInterface.Stub{
- public void startMusic() throws RemoteException {
- mediaPlayer.start();
- }
- public void stopMusic() throws RemoteException {
- mediaPlayer.stop();
- try {
- mediaPlayer.prepare();
- } catch (IllegalStateException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
RemoteServiceActivity.java
- package cn.com.chenzheng_java.remote;
- import android.app.Activity;
- import android.os.Bundle;
- /**
- * 很多人不理解,这里面基本上什么也没实现,为什么还要提供呢?
- * 其实原因是这样的,service代码我们写好了,也在配置文件中注册了,
- * 这样,手机系统就会识别了吗?不是的哦,你至少得将该service所在的
- * 应用运行一次才可以哦。要不手机怎么知道你添加了一个service啊,对吧!
- * @author chenzheng_Java
- *
- */
- public class RemoteServiceActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- }
- }
androidManifest.xml
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="cn.com.chenzheng_java.remote"
- android:versionCode="1"
- android:versionName="1.0">
- <uses-sdk android:minSdkVersion="8" />
- <application android:icon="@drawable/icon" android:label="@string/app_name">
- <activity android:name=".RemoteServiceActivity"
- 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="MyRemoteService">
- <intent-filter>
- <action android:name="cn.com.chenzheng_java.remote"/>
- </intent-filter>
- </service>
- </application>
- </manifest>
客户端:
activity代码:
- package cn.com.chenzheng_java;
- 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.os.RemoteException;
- import android.util.Log;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import cn.com.chenzheng_java.remote.RemoteServiceInterface;
- /***
- * @author chenzheng_java
- * @description 通过当前activity去调用不同进程中的远程service
- */
- public class LocalServiceActivity extends Activity implements OnClickListener {
- String ACTION_NAME = "cn.com.chenzheng_java.remote";
- boolean flag = false;
- Button button_start ;
- Button button_stop ;
- Button button_destroy ;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.music);
- button_start = (Button) findViewById(R.id.button1);
- button_stop = (Button) findViewById(R.id.button2);
- button_destroy = (Button) findViewById(R.id.button3);
- button_start.setOnClickListener(this);
- button_stop.setOnClickListener(this);
- button_destroy.setOnClickListener(this);
- }
- RemoteServiceInterface remoteServiceInterface ;
- private class MyServiceConnection implements ServiceConnection{
- public void onServiceConnected(ComponentName name, IBinder service) {
- remoteServiceInterface = RemoteServiceInterface.Stub.asInterface(service);
- try {
- Log.i("flag", flag+"");
- if(flag){
- Log.i("通知", "已经开始唱歌");
- remoteServiceInterface.startMusic();
- }else{
- Log.i("通知", "已经停止唱歌");
- remoteServiceInterface.stopMusic();
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- public void onServiceDisconnected(ComponentName name) {
- }
- }
- private MyServiceConnection serviceConnection = new MyServiceConnection();
- public void onClick(View v) {
- if(v == button_start){
- flag = true;
- Intent intent = new Intent(ACTION_NAME);
- /**
- * Context.BIND_AUTO_CREATE 当绑定service时,如果发现尚未create,那么就先create一个,然后绑定
- */
- bindService(intent, serviceConnection ,Context.BIND_AUTO_CREATE);
- }
- if(v == button_stop){
- Log.i("通知", "已经点击了停止按钮");
- flag = false;
- Intent intent = new Intent(ACTION_NAME);
- bindService(intent, serviceConnection ,Context.BIND_AUTO_CREATE);
- try {
- remoteServiceInterface.stopMusic();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- if(v == button_destroy){
- flag = false;
- Intent intent = new Intent(ACTION_NAME);
- bindService(intent, serviceConnection ,Context.BIND_AUTO_CREATE);
- unbindService(serviceConnection);
- }
- }
- }
music.xml
- <?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">
- <Button android:text="播放" android:id="@+id/button1"
- android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
- <Button android:text="停止" android:id="@+id/button2"
- android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
- <Button android:text="销毁service" android:id="@+id/button3"
- android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
- </LinearLayout>
其他没有粘贴出来的代码都是由系统默认生成的。
以上转自: http://blog.youkuaiyun.com/chenzheng_java/article/details/6259429