Service与AIDL学习小结

本文详细介绍了Android Service的两种形式:通过startService()启动的独立Service和通过bindService()启动的绑定Service。探讨了它们的生命周期、创建过程及如何正确使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个Service,从本质上来说,可以有两种形式。
1、 通过startService()启动。这种情况下,该Service与启动它的组件是完全独立的,即使组件被销毁,Service仍会继续,不会自动结束,而且通常情况下不返回任何结果。因此,应在Service执行完毕后调用stopSelf(int)(最好用带参数的,防止一个Service被多个组件请求的情况下,把还在执行的请求结束了)或其他组件调用stopService关闭Service。
2、 通过bindService()启动。其生命周期与绑定该Service的组件有关,可以多个组件绑定一个Service,但当所有组件都解绑后,该Service将被销毁。但是当有组件绑定一个Service时,该Service无法以stopService或stopSelf的方式终止。

默认情况下,任何组件都可以访问Service,但是我们可以在AndroidManifest.xml中将其声明为私有的,从而拒绝其他应用中的组件访问本应用的Service。通过设置intent-filter,可以使得Service能够被隐式调用。
同时,一个Service默认情况下运行在它的宿主进程的主线程(除非在AndroidManifest.xml中另行设置),因此,若要在一个Service中执行一些复杂的操作,最好在Service的执行代码中新建一个线程,在里面运行。这样能够降低系统出现Application Not Responding (ANR)错误的风险。
若只想在Activity运行时在后台执行某些操作,可以在Activity的onCreate中新建一个线程,在onStart中启动,在onStop中停止。而不是采用Service的方式。

创建过程:继承Service类,实现生命周期方法。与Activity不同,Service不需要调用父类方法。
1、 onStartCommand:当该Service被其他组件以startService的方式调用后,执行该方法。不同的返回值可以设置当系统内存不足时,对该Service的处理方式。START_NOT_STICKY:销毁后不自动创建。START_STICKY:销毁后重新创建Service,并执行onStartCommand,但传入的intent是空的。START_REDELIVER_INTENT:在START_STICKY基础上,传入了最后传入的那个intent。
2、 onBind:当该Service被其他组件以bindService的方式调用后,执行该方法。绑定后,该Service的生命周期就与绑定它的组件相关联。这种情况下,不用在代码里停止Service。要支持Service绑定,需要返回一个IBinder接口的实例(使用内部类继承Binder类定义并创建,Binder类实现了IBinder接口),在里面定义一些自己需要的方法,以便Service的绑定者与该Service之间的交互。

class MyBinder extends Binder{
	// 比如说,定义一个停止Service的方法
	public void stopService(){
		MyService.this.stopSelf();
	}
	// 甚至可以定义一个返回该Service对象的方法
	public MyService getService(){
		return MyService.this;
	}
}
@Override
public IBinder onBind(Intent arg0) {
	Log.d(TAG, "onBind");
	return new MyBinder();
}

 
然后就可以在Service的绑定者(比如说Activity)的代码里对其进行操作

MyService myService;
MyService.MyBinder localBinder;
// 定义ServiceConnection对象并在Service连接时获取相关对象
private ServiceConnection mConnection = new ServiceConnection() {
	@Override
	public void onServiceConnected(ComponentName className,IBinder binder)
{
		localBinder = (MyService.MyBinder) binder;
		myService = localBinder.getService();
	}
	@Override
	public void onServiceDisconnected(ComponentName arg0) {
	}
};
// 然后进行绑定
Intent intent2=new Intent();
intent2.setClass(context, MyService.class);
context.bindService(intent2, mConnection, Context.BIND_AUTO_CREATE);

 
3、 onCreate:只在Service第一次被创建时调用,其调用在onStartCommand与onBind之前。
4、 onDestroy:销毁时调用。

对于onStartCommand和onBind,可以只实现其中一种。
不能从BroadcastReceiver中调用bindService。但如果是用registerReceiver的方式注册过的BroadcastReceiver,由于其生命周期与Activity绑定,则可以。
IntentService是Service的子类,其中采用了一个工作者线程来处理所有的Intent请求(串行);当请求执行完毕后自动调用stopSelf方法;提供返回null的onBind的实现;提供onStartCommand的默认实现,并把请求转发至onHandleIntent方法(需自行实现)。若不需要所有请求同时处理,建议通过继承该类来实现Service。

前台Service:可以在通知栏中看到的Service。
当一个Service处于前台模式下时,系统不会在内存不足时销毁它。同时,前台Service需要在通知栏上显示一个图标及相应信息。并支持从通知栏启动相应的Activity。可以在Service的onStartCommand方法中根据传入的intent中的内容的不同,修改状态。

// service内执行
// 此处的title是一个通知生成时,在通知栏显示的提示消息
Notification notification = new Notification(R.drawable.icon, "title",System.currentTimeMillis());
// 为了能在通知栏拉下来后,点击通知能够打开Activity,使用了PendingIntent
Intent notificationIntent = new Intent(this, BroadcastActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
// 此处的title2是把通知栏拉下来后,显示的标题。content即文本内容。
notification.setLatestEventInfo(this, "title2","content", pendingIntent);
// 设置为前台显示
startForeground(id, notification);

 
不想在前台显示时,在Service中执行stopForeground(true);即可。

只有Activity,Service,ContentProvider能够绑定Service,BroadcastReceiver不可以。
如果想要在每次绑定时都执行一遍onBind,要在onUnbind中返回true。否则,当下一次有组件绑定该服务时,将运行onRebind,而不是onBind。如下图:


 

进程间通信
当要在Service中支持IPC(进程间通信)时,可以使用Messenger(不需要多线程)或AIDL(需要支持多线程访问服务)。
Messenger:
1、 在Service中实现一个Handler,在它的handleMessage方法中处理包含不同Message的请求。
2、 在Service中创建Messenger对象(公共,参数是自定义Handler对象),在onBind时,使用Messenger的getBinder方法返回绑定的IBinder对象。
3、 调用时,在ServiceConnection类的onServiceConnected方法中,利用IBinder对象创建Messenger对象。然后在想要调用服务的地方使用它的send方法发送Message对象给Service。
AIDL
注:对AIDL的访问相当于函数调用,无法保证其运行在哪个线程。
对AIDL的调用是同步调用,调用完毕函数才返回。如果是比较耗时的工作,注意不要在主线程中调用,防止界面卡死(ANR)。
实现过程:
1、 定义AIDL文件,定义完后,Eclipse会自动在gen的相应目录生成一个.java文件。

interface MyAidl {
    int getPid();
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);
    void stopService();
}

 
2、 在自己实现的Service中,创建一个Stub类(Binder的子类,在.java中自动生成的)对象,给出相应方法的实现。

public class AidlBinder extends MyAidl.Stub{
	@Override
public void basicTypes(int anInt, long aLong, boolean 
aBoolean,float aFloat, double aDouble, String aString)
throws RemoteException
{
		Log.d(TAG, "basicTypes");
	}
	@Override
	public int getPid() throws RemoteException {
		Log.d(TAG, "getPid");
		return 0;
	}
	@Override
	public void stopService() throws RemoteException {
		MyService.this.stopSelf();
	}
}

 
3、 将Stub对象通过onBind传给调用者

@Override
	public IBinder onBind(Intent arg0) {
		return myAidlBinder;
	}

 
4、 在调用者的代码中,利用asInterface方法获取接口对象(若处于不同应用,调用者应能获取aidl文件,以便自动生成接口类)。

MyAidl aidl;
private ServiceConnection mConnection = new ServiceConnection() {
	@Override
	public void onServiceConnected(ComponentName 
	className,IBinder binder) {
		aidl=MyAidl.Stub.asInterface(binder);
	}
	@Override
	public void onServiceDisconnected(ComponentName arg0) {
	}
};

 

若要在AIDL中使用基本类型、String、CharSequence以外的数据类型,需要使用Parcelable,并指明方向。过程如下:
1、 定义一个类继承Parcelable,实现writeToParcel,以便将该类写入Parcel中(类似一个输出流)
2、 添加一个实现了Parcelable.Creator接口的名为CREATOR的静态变量

public class MyParcelable implements Parcelable {
	int num;
	double n2;
	String string;
	public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {
		public MyParcelable createFromParcel(Parcel in) {
			return new MyParcelable(in);
		}
		public MyParcelable[] newArray(int size) {
			return new MyParcelable[size];
		}
	};
	public MyParcelable() {
	}
	private MyParcelable(Parcel in) {
		num = in.readInt();
		n2 = in.readDouble();
		string = in.readString();
	}
	@Override
	public int describeContents() {
		return 0;
	}
	@Override
	public void writeToParcel(Parcel out, int flag) {
		out.writeInt(num);
		out.writeDouble(n2);
		out.writeString(string);
	}
}

 
3、 创建一个MyParcelable.aidl文件,声明该类

package com.learn.aidl;
parcelable MyParcelable;

 
4、 在使用该类作为参数的aidl文件中import

import com.learn.aidl.MyParcelable;
interface MyAidl {
	MyParcelable getParcelable();
void setParcelable(in MyParcelable myParcelable);
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值