Service运行在主线程。所以不可以执行耗时操作。
而后台做的事,都是一些需要一直连接,一直做出请求的事,难道就不担心主线程阻塞?
解决的办法就是,就在服务中创建子线程开始耗时操作。
Service和Thread一些比较:
在Activity里子线程执行耗时操作和在Service里子线程执行耗时操作具体有什么区别?
Activity被销毁后,虽然子线程还在执行,但是有一个麻烦就是,怎么去拿到在销毁了Activity的子线程实例,来对其进行操作。
而Service,无论在哪一个Activity,都可以与Service进行关联,然后很方便的操作Service里的方法。
绑定服务和开启服务的一点区别:
绑定的服务,当开启它的Activity被销毁了。绑定的服务会销毁。
绑定的服务,主要的作用是调用服务里的方法。
开启的服务,只要进程没被杀死,服务一直在后台运行。
开启的服务,却无法调用服务里的方法。
如果既要长期运行在后台,又要调用服务里的方法,先开启服务,再绑定服务。
在服务的OnCreate方法中,进行耗时的操作。的确打开应用就会出现“应用失去响应”。
这是因为服务运行在主线程,做了耗时的操作。
如果把服务替换为远程服务,就不会出现ANR。原因就是此时的服务的进程和主线程所处的进程已经不是同一个进程。
但是远程服务有一个问题,就是当绑定服务,需要返回IBinder对象时,由于不在一个进程内,此时的Activity无法与另一个进程的Service进行通信,返回的必然是null。
那怎么解决和远程(不在一个进程)服务进行通信呢?
解决的方法是:Android interface Definition Language安卓接口定义语言
AIDL解决进程间(跨进程)的通信问题。
在android官网中是这么介绍AIDL的:
AIDL可能类似于你使用的其他接口定义语言。
它允许你定义客户端和服务端达成一致的编程接口,来进行IPC(进程间通信)。
在android中一个进程无法访问另一个进程的内存,所以,需要把对象分解成操作系统可以理解的原始类型。
这个过程是复杂的,所以android定义了AIDL帮你解决。
如果你的面临的需求是:不同的应用程序跨进程的访问你的服务和处理多线程服务,使用AIDL。
如果你的面临的需求是:需要处理不同的应用程序跨进程的访问,但不用考虑多线程的问题,使用Binder。
如果你面临的需求是:需要考虑IPC,不需要处理多线程。使用Messager。
通过上面的翻译,我自己的理解是,多进程之间不可以访问内存,是由于每一个android应用都拥有独立的虚拟机,但是android应用都是运行在android系统上,可以利用android系统的底层来进行通信。
一个应用和另一个应用使用相同的接口(.aidl文件一致),在底层就完成某种契约呢。(自己的理解)
AIDL是基于Service来进行跨进程通信。
IPC的例子:
一个应用(服务端)已经写好了某个功能(比如进行加法运算),现在为了节省开发成本,想在我们的应用(客户端)中直接使用这个功能。
1,在服务端创建.aidl文件
.aidl文件必须要是一个接口,接口中定义了方法的内容。
①创建AIDL文件夹
②创建.aidl文件
interface IMyAddInterface {
//添加一个用于进行加法运算的方法
int add(int num1,int num2);
}
在eclipse中会自动的进行编译,把.aidl编译为我们都认识的java语言。在android studio中,需要手动的进行编译。
编译完后,会生成和以.aidl文件名命名的java接口文件。
这个java接口文件包含一个Stub的静态内部类,这个Stub继承自android.os.Binder,实现父类的接口。
public interface IMyAddInterface extends android.os.IInterface{
public static abstract class Stub extends Binder implements IMyAddInterface{
..........
}
}
Stub中有一个方法asInterface,它把服务端的Binder对象转化为客户端所需的AIDL接口类型的对象。
public static com.asule.cn.IMyAddInterface asInterface(android.os.IBinder obj){
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.asule.cn.IMyAddInterface))) {
return ((com.asule.cn.IMyAddInterface)iin);
}
return new com.asule.cn.IMyAddInterface.Stub.Proxy(obj);
}
2,实现接口,暴露接口给客户端
//实现接口
private final IMyAddInterface.Stub mBinder = new IMyAddInterface.Stub() {
@Override
public int add(int num1, int num2) throws RemoteException {
//实现加法运算
Log.e(LOG_TAG, "num1:"+num1+"num2:"+num2);
return (num1+num2);
}
};
//暴露接口给客户端
@Override
public IBinder onBind(Intent intent) {
Log.e(LOG_TAG, "onBind");
return mBinder;
}
要保证跨进程通信,客户端和服务端都必须定义标准的aidl语言,客户端写aidl时必须保证和服务端的接口一致,为了防止出错。直接拷贝服务端的aidl文件。
此时客户端就可以通过绑定服务的方式,获取到Binder对象。
通过asInterface方法把IBinder对象转化为抽象内部类Stub实例。而Stub的实例实现了加法功能。
接口指向接口实现对象,动态绑定,会调用句柄指向的实例里的内容。
客户端的代码:
//绑定服务
//android5.0之后不可以再通过隐式意图去开启或绑定服务,
//所以在service定义action,category,data等过滤规则是没有用的
Intent intent=new Intent();
//指定service的包名和类名(要全称)
ComponentName com = new ComponentName("asule.hope","asule.hope.MyService");
intent.setComponent(com);
bindService(intent,connn,BIND_AUTO_CREATE);
//调用远程服务
private ServiceConnection connn=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IMyAddInterface stub=IMyAddInterface.Stub.asInterface(service);
try {
System.out.println(stub.add(1,2)+"");
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
一个坑:
在服务端注册服务时,需要指定android:exported=”true”,而默认是false。
这个参数为true表示该服务是否可以和其他应用的组件调用或交互。所以要写为true。否则调用不起来。
支持的数据类型:
在aidl的官方介绍中,表示Java的基本类型都支持,但是在写aidl文件时,把参数写为short类型会编译不通过。
同样也支持List,Map集合,但是List和Map的元素必须是一个aidl支持的数据类型,或者是声明的parceables。
比如写List<short>就会挂。
封包(大数据到小数据)和打包(小数据到大数据)是很耗费内存的操作,在定义时还得指明List集合是需要打包还是封包。
in List<String> list 只要打包
out List<String> list 只要封包