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對象。