1、Android服务
服务(Service)是Android系统中4大组件之一。服务主要用于两个目的:后台运行和跨进程访问。通过启动一个服务,可以在不显示界面的前提下在后台运行指定的任务,这样可以不影响用户做其他事情。通过AIDL服务可以实现不同进程之间的通信,这也是服务的重要用途之一
Service并没有实际界面,而是一直在Android系统的后台运行。一般使用Service为应用程序提供一些服务,或不需要界面的功能,例如,从网上下载文件、控制Video播放器等。本节主要介绍Service的启动和结束过程(Service的生命周期)以及启动Service的各种方法
1.1 Service的生命周期
Service和Activity一样,也有从启动到销毁的过程,但Service的过程比Activity简单的多: 创建->开始->销毁
public void onCreate(); // 创建服务
public void onStartCommand(Intent intent, int startId); // 开始服务
public void onDestroy(); // 销毁服务
一个服务只会创建一次,销毁一次,但可以开始多次,因此,onCreate和onDestroy方法只会被调用一次,而onStartCommand方法会被调用多次
// MyService是一个服务类,该类必须从android.app.Service类继承
public class MyService extends Service
{
@Override
public IBinder onBind(Intent intent)
{
return null;
}
// 当服务第1次创建时调用该方法
@Override
public void onCreate()
{
Log.d("MyService", "onCreate");
super.onCreate();
}
// 当服务销毁时调用该方法
@Override
public void onDestroy()
{
Log.d("MyService", "onDestroy");
super.onDestroy();
}
// 当开始服务时调用该方法
@Override
public void onStartCommand(Intent intent, int startId)
{
Log.d("MyService", "onStartCommand");
super.onStart(intent, startId);
}
}
在MyService中实现了Service类中3个生命周期方法,并在这些方法中输出了相应的日志信息,以便更容易地观察事件方法的调用情况。
写Android的应用组件时要注意,不管是编写什么组件(例如,Activity、Service等),都需要在AndroidManifest.xml文件中进行配置。MyService类也不例子。配置这个服务类很简单,只需要在AndroidManifest.xml文件的<application>标签中添加如下代码即可
<service android:enabled="true" android:name=".MyService" />
其中android:enabled属性的值为true,表示MyService服务处于激活状态。虽然目前MyService是激活的,但系统仍然不会启动MyService,要想启动这个服务。必须显式地调用startService方法。如果想停止服务,需要显式地调用stopService方法,代码如下
Intent serviceIntent = new Intent(this, MyService.class);
public void onClick(View view)
{
switch (view.getId())
{
case R.id.btnStartService:
startService(serviceIntent);
// 单击【Start Service】按钮启动服务
break;
case R.id.btnStopService:
stopService(serviceIntent);
// 单击【Stop Service】按钮停止服务
break;
}
}
启动后会看到:
onCreate
onStartCommand
停止后会看到:
onDestroy
1.2 绑定Activity和Service
public class MyService extends Service
{
private MyBinder myBinder = new MyBinder();
// 成功绑定后调用该方法
@Override
public IBinder onBind(Intent intent)
{
Log.d("MyService", "onBind");
return myBinder;
}
// 重新绑定时调用该方法
@Override
public void onRebind(Intent intent)
{
Log.d("MyService", "onRebind");
super.onRebind(intent);
}
// 解除绑定时调用该方法
@Override
public boolean onUnbind(Intent intent)
{
Log.d("MyService", "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onCreate()
{
Log.d("MyService", "onCreate");
super.onCreate();
}
@Override
public void onDestroy()
{
Log.d("MyService", "onDestroy");
super.onDestroy();
}
@Override
public void onStartCommand(Intent intent, int startId)
{
Log.d("MyService", "onStart");
super.onStart(intent, startId);
}
public class MyBinder extends Binder
{
MyService getService()
{
return MyService.this;
}
}
}
现在定义一个MyService变量,和一个ServiceConnection变量:
private MyService myService;
private ServiceConnection serviceConnection = new ServiceConnection() {
// 连接服务失败后,该方法被调用
@Override
public void onServiceDisconnected(ComponentName name)
{
myService = null;
Toast.makeText(Main.this, "Service Failed.", Toast.LENGTH_LONG).show();
}
// 成功连接服务后,该方法被调用。在该方法中可以获得MyService对象
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
// 获得MyService对象
myService = ((MyService.MyBinder) service).getService();
Toast.makeText(Main.this, "Service Connected.", Toast.LENGTH_LONG).show();
}
};
最后使用bindService来绑定Activity和Service
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
unbindService(serviceConnection);
1.3 在BroadcastReceiver中启动Service
public class StartupReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 启动一个Service
Intent serviceIntent = new Intent(context, MyService.class);
context.startService(serviceIntent);
Intent activityIntent = new Intent(context, MessageActivity.class);
// 要想在Service中启动Activity,必须设置如下标志
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(activityIntent);
}
}
2、什么事AIDL服务
2.1 跨进程
Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。Activity和Broadcast都可以跨进程通信,除此之外,还可以使用Content Provider进行跨进程通信。现在我们已经了解了4个Android应用程序组件中的3个(Activity、Broadcast和Content Provider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。这就是本节要介绍的AIDL服务
2.2 什么是AIDL服务
前面的部分介绍了开发人员如何定制自己的服务,但这些服务并不能被其他的应用程序访问。为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务
2.3 AIDL的语法十分简单,与Java语言基本保持一致, AIDL支持的数据类型
- AIDL文件以 .aidl 为后缀名
- AIDL支持的数据类型分为如下几种:
- 八种基本数据类型:byte、char、short、int、long、float、double、boolean
- String,CharSequence
- 实现了Parcelable接口的数据类型
- List 类型。List承载的数据必须是AIDL支持的类型
- Map类型。Map承载的数据必须是AIDL支持的类型
2.3建立AIDL服务的步骤
(1)在java文件目录上点击右键,新建AIDL -> AIDL File, 创建好以后系统会自动创建一个aidl目录,所有aidl文件都在这个目录中
我们新建了IMyService.aidl,里面有一个接口IMyService,重新编译代码,会自动生成一个IMyService.java文件,里面东西只关心IMyService.Stub这个类,这里面是我们封装的方法
(2)在Java目录中创建Service,并创建aidl文件里生成的IMyService.Stub实例,然后onBind返回(注意:这一步必须先重新编译代码,不然找不到接口)
(3)如果要加方法,就在IMyService.aidl文件中加,加完以后需要重新编译,这时候stub实例里的回调方法就会报错,提示你实现接口
(4)在AndroidManifest.xml文件中配置AIDL服务,android:exported=true,这个属性用于指示该服务是否能够被其他应用程序组件调用或跟它交互。如果设置为true,则能够被调用或交互,否则不能
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"/>
(5)启动Service
Intent it = new Intent(this, MyService.class);
startService(it);
在服务端工程中必须启动Service,否则客户端连不上
(6)编写客户端代码获取数据(首先要把aidl目录完全复制到客户端代码中,不需要任何变动)
当服务端添加了方法,必须把aidl目录全部重新复制过来
(7)客户端绑定服务
public class MainActivity extends AppCompatActivity {
IMyService myService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myService = IMyService.Stub.asInterface(service);
try {
Toast.makeText(MainActivity.this, myService.getValue(), Toast.LENGTH_LONG).show();
}
catch (Exception e) {
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Intent it = new Intent();
it.setComponent(new ComponentName("com.bank.myaapplication", "com.bank.myaapplication.MyService"));
bindService(it, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
启动成功后就可以通过myService变量来调用服务端提供的方法。