1.先来看看What is Service?

A Service is not a separate process And A Service is not a thread. 一个服务不是一个独立的进程,也不是一个线程。

那Service是什么呢?

后台运行 处理耗时较长的工作

Service是一个应用程序组件

Service没有图形化界面

Service通常用来处理一些耗时比较长的操作

可以使用Service更新ContentProvider,发送Intent以及启动系统的通知等等

2. 接着来看看Service的整个生命周期:

Service的活动生命周期是在onStart()之后,这个方法会处理通过startServices()方法传递来的Intent对象。音乐Service可以通过开打intent对象来找到要播放的音乐,然后开始后台播放。

Service停止时没有相应的回调方法,即没有onStop()方法。onCreate()方法和onDestroy()方法是针对所有的Services,无论它们是否启动。通过Context.startService()和Context.bindService()方法。然而,只有通过startService()方法启动的Service才会被调用onStart()方法。如果一个Service允许别人绑定,那么需要实现以下额外的方法:

IBinder onBind(Intent intent)

boolean onUnbind(Intent intent)

void onRebind(Intent intent)

onBind()回调方法会继续传递通过bindService()传递来的intent对像。onUnbind()会处理传递给unbindService()的intent对象。如果Service允许绑定,onBind()会返回客户端与服务互相联系的通信频道。如果建立了一个新的客户端与服务的链接,onUnbind()方法可以请求调用onRebind()方法。

下面的图表介绍了Service的回调方法,然而,它把通过startService()方法建立的服务从通过bindService()方法建立的服务分离开。记住任何服务,无果它怎样建立,都默认客户端可以链接,所以任何的Service能够接收onBind()和onUnbind()方法。


3. Service组件的通信方式一共有三种:(1)通过startService来启动的Service;(2)通过bindService来启动的Service;(3)使用AIDL方式的Service,下面我们来看看它们的具体使用方法,和它们之间有什么区别。

(1)首先来说说通过startService来启动的Service(后台处理工作)

startService(Intent service),通过intent值来指定启动哪个Service,可以直接指定目标Service的名,也可以通过Intent的action属性来启动设置了相应action属性的Service

,使用这种方式启动的Service,当启动它的Activity被销毁时,是不会影响到它的运行的,这时它仍然继续在后台运行它的工作。直至调用StopService(Intent service)方法时

时或者是当系统资源非常紧缺时,这个服务才会调用onDestory()方法停止运行。所以这种Service一般可以用做,处理一些耗时的工作。可能有人会问,如果Service不是独立的

一个进程的话,为什么当Activity退出时,Service仍然可以进行运行呢?其实是这样的,sdk上说了activity和service默认是运行在应用进程的主线程中,四大组件默认都是和

activity运行在同一个主线程中的,那就是说activity通过startservice方法启动一个服务后,被启动的服务和activity都是在同一个线程中的。所以当我主动销毁了这个activity,但是

他所在的线程还是存在的,只不过是这个activity他所占用的资源被释放掉了,这个activity所在的主线程只有当android内存不足才会被杀死掉,否则一般的情况下这个activity所

在的应用程序的线程始终存在,也就是这个activity所启动的服务也会一直运行下去。

还有一点需要注意的是,如果Service要处理一些比较耗时的工作时,因为Service和Activity默认情况都在同一个主线程中的缘故,所以要操作这些耗时的工作一般是在

Service里另起一个新线程来处理。这样可以避免主线程的阻塞,影响用户体验性。

(2)然后来说说通过bindService来启动的Service(在本地同进程内与Activity交互)

这里举一个实例来讲:

首选,创建一个接口,IService

package yy.android.service
public interface IService {
String getName();
}

接着,创建一个服务LocalService

package yy.android.service;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class LocalService extends Service{
private static final String TAG="LocalService";
private MyBind myBind=new MyBind();
public IBinder onBind(Intent intent) {
Log.d(TAG, "localService onBind");
return myBind;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "localService onCreate");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "localService onDestroy");
}
@Override
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
Log.d(TAG, "localService onStart");
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
Log.d(TAG, "localService onUnBind");
return super.onUnbind(intent);
}
public class MyBind extends Binder implements IService{
public String getName() {
// TODO Auto-generated method stub
return "YUZHIBOYI";
}
}
}

最后就是实现ServiceActivity了:

package yy.android.service;

import android.app.Activity;
import android.os.Bundle;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;


public class ServiceActivity extends Activity {

private static final String TAG="ServiceActivity";
private IService iService=null;
private EditText edit;
private Button mBind;
ServiceConnection connection=new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG,"DisConnection");
System.out.println("DisConnection!!!");
}
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
Log.d(TAG,"Connection");
System.out.println("Connection!!!");
iService=(IService)service;
edit.setText(iService.getName());
// text.setText(iService.getName());
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Log.d(TAG,"Start");
edit =(EditText)findViewById(R.id.edit);
mBind = (Button)findViewById(R.id.Connection);
mBind.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent=new Intent(ServiceActivity.this,LocalService.class);
bindService(intent,connection, BIND_AUTO_CREATE);
}
});
}
}

AndroidManifest.xml注册LocalService

<service android:name = ".LocalService">
<intent-filter>
</intent-filter>
</service>

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<EditText
android:id="@+id/edit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
></EditText>
<Button
android:id="@+id/Connection"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/Bind"/>
</LinearLayout>
当运行程序是,日志信息是这样的:05-10 13:46:28.353: D/LocalService(1328): localService onCreate

05-10 13:46:28.353: D/LocalService(1328): localService onBind

05-10 13:46:28.385: D/ServiceActivity(1328): Connection

当点击模拟器的back按钮时,日志信息: 05-10 13:46:57.953: D/LocalService(1328): localService onUnBind

05-10 13:46:57.953: D/LocalService(1328): localService onDestroy

从日志信息可以分析出,整个程序的运行过程是这样的:点用户点击绑定服务按钮时Activity调用bindService()方法,然后系统就去调用onCreate创建服务,然后系统继续调用onBind()方法向Activity传递一个IBinder类型的对象, 是传递给Activity里的ServiceConnection里的onServiceConnected(ComponentName name, IBinder service)的第二个参数,然后通过这个参数可以获得IService的方法。进行本地的Activity和Service交互。(在同一个进程里进行的),当用户点击back建时,系统就调用onUnbind()再接着调用onDestory()方法销毁服务。总结可以理解成:bindSevice()->onCreate()->onBind()->onServiceConnected();这里需要注意的一点是,启动的LocalService是和ServiceActivity在同一个进程里的,因为在注册服务时,没有配置它的android:process = "xxxx" 属性。具体android:process的用法可以自己去看其他资料。

(3)最后要说的是使用AIDL方式的Service(进行跨进程的通信)

AIDL(Android Interface Definition Language) IPC机制是面向对象的,轻量级的。通过AIDL定义的接口可以实现服务器端与客户端的IPC通信。在Android上,一个进程不能简单的像访问本进程内存一样访问其他进程的内存。所以,进程间想要对话,需要将对象拆解为操作系统可以理解的基本数据单元,并且有序的通过进程边界。通过代码来实现这个数据传输过程是冗长乏味的,所幸的是android提供了AIDL工具来帮我们完成了此项工作。

注意:仅仅在你需要A应用程序的客户端访问B应用程序的服务器端来实现 IPC通信,并且在服务器端需要处理多线程(客户端)访问的情况下使用AIDL。如果不需要使用到进程间的IPC通信,那么通过Binder接口实现将更为合适,如果需要实现进程间的IPC通信,但不需要处理多线程(多客户端),通过Messager接口来实现将更为合适。不管怎样,在使用AIDL之前,应先确保已理解了Bound Service。

AIDL接口的调用采用的是直接的函数调用方式,但你无法预知哪个进程(或线程)将调用该接口。同进程的线程调用和其他进程调用该接口之间是有所区别的:

  • 在同进程中调用AIDL接口,AIDL接口代码的执行将在调用该AIDL接口的线程中完成,如果在主UI线程中调用AIDL接口,那么AIDL接口代码的执行将会在这个主UI线程中完成。如果是其他线程,AIDL接口代码的执行将在service中完成。因此,如果仅仅是本进程中的线程访问该服务,你完全可以控制哪些线程将访问这个服务(但是如果是这样,那就完全没必要使用AIDL了,而采取Binder接口的方式更为合适)。

  • 远程进程(其他线程)调用AIDL接口时,将会在AIDL所属的进程的线程池中分派一个线程来执行该AIDL代码,所以编写AIDL时,你必须准备好可能有未知线程访问、同一时间可能有多个调用发生(多个线程的访问),所以ADIL接口的实现必须是线程安全的。

  • 可以用关键字oneway来标明远程调用的行为属性,如果使用了该关键字,那么远程调用将仅仅是调用所需的数据传输过来并立即返回,而不会等待结果的返回,也即是说不会阻塞远程线程的运行。AIDL接口将最终将获得一个从Binder线程池中产生的调用(和普通的远程调用类似)。如果关键字oneway在本地调用中被使用,将不会对函数调用有任何影响。

定义AIDL接口

AIDL接口使用后缀名位.aidl的文件来定义,.aidl文件使用java语法编写,并且将该.aidl文件保存在 src/目录下(无论是服务端还是客户端都得保存同样的一份拷贝,也就是说只要是需要使用到该AIDL接口的应用程序都得在其src目录下拥有一份.aidl文件的拷贝)。

编译时,Android sdk 工具将会为 src/目录下的.aidl文件在 gen/ 目录下产生一个IBinder接口。服务端必须相应的实现该IBinder接口。客户端可以绑定该服务、调用其中的方法实现IPC通信。


创建一个用AIDL实现的服务端,需要以下几个步骤:
1. 创建.aidl文件:

该文件(YourInterface.aidl)定义了客户端可用的方法和数据的接口

2. 实现这个接口:

Android SDK将会根据你的.aidl文件产生AIDL接口。生成的接口包含一个名为Stub的抽象内部类,该类声明了所有.aidl中描述的方法,你必须在代码里继承该Stub类并且实现.aidl中定义的方法。

3.向客户端公开服务端的接口:

实现一个Service,并且在onBinder方法中返回第2步中实现的那个Stub类的子类(实现类)。

注意:

服务端AIDL的任何修改都必须的同步到所有的客户端,否则客户端调用服务端得接口可能会导致程序异常(因为此时客户端此时可能会调用到服务端已不再支持的接口

1. 创建.aidl文件

AIDL使用简单的语法来声明接口,描述其方法以及方法的参数和返回值。这些参数和返回值可以是任何类型,甚至是其他AIDL生成的接口。重要的是必须导入所有非内置类型,哪怕是这些类型是在与接口相同的包中。

默认的AIDL支持一下的数据类型(这些类型不需要通过import导入):

  • java语言的原始数据类型(包括 int, long, char, boolen 等等)

  • String

  • CharSequence:该类是被TextView和其他控件对象使用的字符序列

  • List:列表中的所有元素必须是在此列出的类型,包括其他AIDL生成的接口和可打包类型。List可以像一般的类(例如List<String>)那样使用,另一边接收的具体类一般是一个ArrayList,这些方法会使用List接口

  • Map:Map中的所有元素必须是在此列出的类型,包括其他AIDL生成的接口和可打包类型。一般的maps(例如Map<String,Integer>)不被支持,另一边接收的具体类一般是一个HashMap,这些方法会使用Map接口。

对于其他的类型,在aidl中必须使用import导入,即使该类型和aidl处于同一包内。

定义一个服务端接口时,注意一下几点:

  • 方法可以有0个或多个参数,可以使空返回值也可以返回所需的数据。

  • 所有非原始数据类型的参数必须指定参数方向(是传入参数,还是传出参数),传入参数使用in关键字标记,传出参数使用out,传入传出参数使用inout。如果没有显示的指定,那么将缺省使用in。

  • 在aidl文件中所有的注释都将会包含在生成的IBinder接口中(在Import和pacakge语句之上的注释除外)。

  • aidl中只支持成员方法,不支持成员变量。

我们通过一个例子来说明: (服务端)YAIDLService工程里的包yy.service.aidl有以下三个文件 IAIDLService.aidl package yy.service.aidl;
interface IAIDLService {
String getName();
}

将该.aidl文件保存在工程目录中的 src/目录下,当编译生成apk时,sdk 工具将会在 gen/ 目录下生成一个对应的IBiner接口的.java文件。

如果使用eclipse编写app,那么这个IBinder接口文件将会瞬间生成。

生成的接口包含一个名为Stub的抽象的内部类,该类声明了所有.aidl中描述的方法,

注意:

Stub还定义了少量的辅助方法,尤其是asInterface(),通过它或以获得IBinder(当applicationContext.bindService()成功调用时传递到客户端的 onServiceConnected())并且返回用于调用IPC方法的接口实例

YAIDLService.java package yy.service.aidl;
import yy.service.aidl.IAIDLService.Stub;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class YAIDLService extends Service{
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return mBinder;
}
private IAIDLService.Stub mBinder = new Stub() {

public String getName() throws RemoteException {
return "YUZHIBOYI";
}
};
} 在服务端里的服务程序里实现接口。这样,mBinder就是一个Stub类得对象,该对象为service提供了IPC接口,并将会向客户端公开,这样客户端就可以通过该对象与该service进行交互了。现在,如果客户端(比如一个Activity)调用bindService()来连接该服务端(YAIDLService) ,客户端的onServiceConnected()回调函数将会获得从服务端(YAIDLService )的onBind()返回的mBinder对象

实现ADIL接口时需要注意一下几点:

  • 不能保证所有对aidl接口的调用都在主线程中执行,所以必须考虑多线程调用的情况,也就是必须考虑线程安全。

  • 默认IPC调用是同步的。如果已知IPC服务端会花费很多毫秒才能完成,那就不要在Activity或View线程中调用,否则会引起应用程序挂起(Android可能会显示“应用程序未响应”对话框),可以试着在独立的线程中调用。

  • 不会将异常返回给调用方

YAIDLServiceActivity.java(这个文件是创建Activity自动生成的可以不用)
(客户端)YAIDLClient工程里的yy.client.aidl包里有 文件YAIDLClientActivity.java package yy.client.aidl;
import yy.service.aidl.IAIDLService;
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.os.RemoteException;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class YAIDLClientActivity extends Activity {
private IAIDLService mAIDLService;
private TextView mName;
private Button mMessage;
private Button mPerson;
private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
mAIDLService = IAIDLService.Stub.asInterface(service);
System.out.println("OnService!!!");
}
public void onServiceDisconnected(ComponentName name) {
mAIDLService = null;
System.out.println("DisService!!!");
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findViewById(R.id.connection).setOnClickListener(new OnClickListener() {
public void onClick(View view) {
/**
* 第一步,单击"连接"按钮后用mServiceConnection去bind服务器端创建的Service。
*/
Intent service = new Intent("yy.service.aidl.IAIDLService");
bindService(service, mServiceConnection, BIND_AUTO_CREATE);
}
});
mName = (TextView)findViewById(R.id.name);
mMessage = (Button) findViewById(R.id.message);
mMessage.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
/**
* 第二步,从服务器端获取字符串。
*/
try {
mName.setText(mAIDLService.getName());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
} 在包yy.service.aidl包里有文件 IAIDLService.aidl package yy.service.aidl;

interface IAIDLService {
String getName();
}

客户端同样得访问该接口类(这里指YAIDLService),所以,如果服务端和客户端不在同一进程(应用程序)中,那么客户端也必须在 src/ 目录下拥有和服务端同样的一份.aidl文件的拷贝(同样是指,包名、类名、内容完全一模一样),客户端将会通过这个.aidl文件生成android.os.Binder接口――以此来实现客户端访问AIDL中的方法。当客户端在onServiceConnected()回调方法中获得IBinder对象后,必须通过调用YourServiceInterface.Stub.asInterface(service)将其转化成为YourServiceInterface类型如上程序。


服务端的AndroidMainfest.xml里注册Service <service android:name = ".YAIDLService" android:process = ":remote">
<intent-filter>
<action android:name = "yy.service.aidl.IAIDLService" />
</intent-filter>
</service>

总结:这里给出调用远端AIDL接口的步骤:

1. 在 src/ 目录下包含.adil文件。

2. 声明一个IBinder接口(通过.aidl文件生成的)的实例。

3. 实现ServiceConnection.

4. 调用Context.bindService()绑定你的ServiceConnection实现类的对象(也就是远程服务端)。

5. 在onServiceConnected()方法中会接收到IBinder对象(也就是服务端),调用YourInterfaceName.Stub.asInterface((IBinder)service)将返回值转换为YourInterface类型。

6. 进行跨进程交互。

好了,Service大概就介绍完了,欢迎大家拍砖,一起讨论!!!


更多相关文章

  1. [Android]Service和Activity双向通信的两种方式
  2. [Android]更改Configuration来读取不同的Apk Resource
  3. Android利用Soap读取WebService并且解析XML的DataSet数据
  4. 横竖屏切换不重启activity的方法
  5. 【Android】CalledFromWrongThreadException 深入源码分析
  6. Android调用 .Net Core WebApi 返回数据,用FastJSON解析一直报错
  7. Fragment加载轮换add,show,hide,replace方法
  8. Android(安卓)使用Socket实现服务器与手机客户端的长连接四:使用
  9. Android(安卓)NDK c创建java对象

随机推荐

  1. Android(安卓)ListView的子项的随意拖动
  2. Android(安卓)解析excel文件
  3. Android(安卓)P(api28) 不支持 http 协议
  4. Android(安卓)Contacts(一)—— 读取联系人
  5. android 安卓创建文件夹
  6. android(3)(android五大布局)
  7. android activity 单元测试总结
  8. Android常用代码
  9. Android(安卓)MediaPlayer 简单综合应用
  10. Android自动补全(二)