Service全解析(一)
==1.Service基本介绍==
==2.启动模式下的Service==
==3.绑定模式下的Service==
==4.与Remote Service数据交互==
首先看一下Service的生命周期,启动模式(startService()/stopService)下的生命周期为:
onCreate() —-> onStartCommand() —-> onDestroy() ;
绑定模式下:onCreate() —-> onBind() —->onBind() —-> onDestroy() ;
启动模式下不会执行onStartCommand()方法,所以逻辑处理放在onCreate()中。
需要注意的一点是5.0版本以上的隐式启动,Intent需要setPackage,否则会报错;
绑定模式下Service的onBind()方法会返回一个 Binder对象,在调用bindService()方法的地方得到这个Binder对象,从而实现与Service的交互。
Binder 继承 IBinder。
代码分析(省略了部分无关方法)
public class CountSumService extends Service {
private ServiceBinder serviceBinder = new ServiceBinder();
@Override
public IBinder onBind(Intent intent) {
Log.v("out","onBind()");
return serviceBinder;
}
public void test(){
Log.v("out","service test demo");
}
public class ServiceBinder extends Binder{
public CountSumService getService(){
return CountSumService.this;
}
}
}
Service内部创建了一个 了Binder的类,然后在onBind()方法中返回了这个类的实例,这个实例在创建ServiceConnection的onServiceConnected()方法中可以拿到。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
setContentView(R.layout.activity_service_demo);
conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.v("out",name.toString());
IBinder temp = service;
if(service!=null) {
countSumService = ((CountSumService.ServiceBinder) service).getService();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
countSumService = null;
}
};
mContext.bindService(new Intent(ServiceDemoActivity.this,CountSumService.class) ,conn,BIND_AUTO_CREATE);
//countSumService.test();
Log.v("out","断点");
new Thread(new Runnable() {
@Override
public void run() {
while (countSumService==null){
if(countSumService!=null){
countSumService.test();
}
}
}
}).start();
}
创建ServiceConnection的onServiceConnected()方法得到Service中返回的Binder对象。
这里有一个坑:得到这个Binder对象是异步的不是同步的,所以你bindService()后不能直接得到binder对象,一般都是在onServiceConnected()方法中使用;如果非得在外面使用,可以像我这样开一个线程来获取。
Remote Service
上面讲的都是与本地Service交互,那么远程Service呢,与Remote Service交互涉及到IPC, 通常有这3种方式实现:
==1.广播==
==2.AIDL==
==3.Message==
这里主要讲后两张方法。
先看AIDL方式的实现:
(1)创建一个Remote Service,AndroidManifest.xml文件配置Service属性:两种方式,第一:android:process = “remote”,这种方式创建的Service会运行在一个新进程中,但是这一个新的进程是公有的,也就是说其他的应用也可以运行在这个进程中;第二:android:process=”:任意字符”,(注意:号)这样创建的是一个私有新程。
(2)与本地Service不同的地方在于,本地Service我们自己创建一个继承与Binder的类,而远程Service需要通过创建.aidl文件来自动为我们生成一个Binder类。
(3)下面介绍在AS中创建aidl文件:
a.切换到Project状态
b.右键c/in/java目录,new —-> AIDL文件,生成的aidl文件会自动放到与java同级的AIDL文件夹下。
import com.wangliang160616.androidtest.model.student;
interface IMyAidlInterface {
void setStuInfo(in student stu);
void setAge(int age);
}
注意:import需要手动写。
你以为这就完了,too yang too simple
传递基本数据类型和String,CharSequence,List,Map这些是ok的,但是如果是自己定义的Model,比如我码中的student类,要跨进程传递,应该这样做:
(1)自定义的类继承Parcelable
(2)在你放这个model的包上右键new aidl,先随便起一个名字,等他自动生成后将名字改成与你这个Model同名的名字。
(3)Build –> make project ,AS自动生成java文件,这个java 文件在哪呢,资料上都说在gen目录下,但是在AS下面根本没有gen目录,在app/build/generate/source/aidl,题外话,R文件app/build/generate/source/r下面
(4)回到Service,onBind()方法返回根据AIDL文件自动生成的java类中的Stup对象即可。看代码:
public class RemoteService extends Service {
private student data;
private int age;
private IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub(){
@Override
public void setStuInfo(student stu) throws RemoteException {
if(stu!=null){
data = stu;
}
Log.v("out","当前Service pid:"+android.os.Process.myPid());
Log.v("out","姓名:"+data.getStuName()+"/id:"+data.getStuId());
}
@Override
public void setAge(int age) throws RemoteException {
data.setStuId(age);
Log.v("out","姓名:"+data.getStuName()+"/id:"+data.getStuId());
}
};
public RemoteService() {
}
@Override
public IBinder onBind(Intent intent) throws UnsupportedOperationException{
return mBinder;
}
}
(5)在activity中这样
IMyAidlInterface Service = IMyAidlInterface.Stub.asInterface(service);
得到Binder对象。