知識點列表
1.生命周期
2.啟動方式
3.跨進程通信
4.雙向通信
一 生命周期
Service的生命周期跟啟動方式有關(guān),啟動方式有倆種纷铣,startService和bindService却邓,對應生命周期如下
二 啟動方式
startService()
通過startService啟動后炸宵,service會一直無限期運行下去,只有外部調(diào)用了stopService()或stopSelf()方法時辆憔,該Service才會停止運行并銷毀钉稍。
調(diào)用startService的時候
第一次調(diào)用
構(gòu)造方法——onCreate()——onStartCommand()
當?shù)诙握{(diào)用
直接調(diào)用onStartCommand()
當調(diào)用stopService()的時候
直接調(diào)用onDestory()
bindService()
bindService啟動的服務和調(diào)用者之間是典型的client-server模式配深。調(diào)用者是client,service則是server端嫁盲。service只有一個篓叶,但綁定到service上面的client可以有一個或很多個烈掠,這里所提到的client指的是組件,比如某個Activity缸托。
bindService啟動服務的生命周期與其綁定的client息息相關(guān)左敌,client也可以明確調(diào)用Context的unbindService()方法與Service解除綁定。當所有的client與service解除綁定的時候俐镐,才會調(diào)用onUnbind,然后 Service會自行銷毀矫限。
startService和bindService區(qū)別
client可以通過IBinder接口獲取Service實例,從而實現(xiàn)在client端直接調(diào)用Service中的方法以實現(xiàn)靈活交互佩抹,這在通過startService方法啟動中是無法實現(xiàn)的叼风。
三 跨進程通信
Service跨進程通信,設計到三個東西棍苹,Service无宿,Client,AIDL枢里,client通過bindService的方式可以和Service進行AIDL跨進程通信孽鸡,在Service中定義好AIDL的Stub實現(xiàn)類,通過在Service的onBind回調(diào)中返回IBinder對象栏豺,client綁定時彬碱,獲取到該IBinder對象,即可進行傳遞參數(shù)
AIDL文件
在android studio中奥洼,app/src/main巷疼,下面新建一個aidl文件夾,再在該aidl文件夾下按照包名再新建一個文件夾灵奖,這里面用來寫aidl文件
interface IMyAidlInterface {
int calculate(int x, int y);
}
建好之后嚼沿,build一下,在build/generated/aidl_source_output_dir/debug/out/包名/桑寨,路徑下伏尼,會生成真正用來通訊的文件忿檩,這其實就是AIDL的實際通訊過程尉尾,android為了簡化,只需要我們定義好AIDL接口燥透,自動給我們生成該類沙咏,實際上可以直接編寫該類就可以進行通訊,里面包含一個內(nèi)部靜態(tài)類Stub班套,我們需要再定義個實現(xiàn)類繼承該Stub肢藐,如下
public class MyAidlServiceImpl extends IMyAidlInterface.Stub {
private final String TAG = "MyAidlServiceImpl";
public MyAidlServiceImpl() {
}
@Override
public int calculate(int x, int y) throws RemoteException {
Log.d(TAG, x + y + "");
return x + y;
}
}
Service
Service里面用來通過onBind方法來傳遞該AIDL,實現(xiàn)通信
public class MyService extends Service {
private MyAidlServiceImpl service;
@Override
public void onCreate() {
super.onCreate();
service = new MyAidlServiceImpl();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return service;
}
}
Client
客戶端需要通過bindService來獲取到Service的IMyAidlInterface吱韭,即可調(diào)用對應方法實現(xiàn)通信
public class MainActivity extends Activity {
private final String TAG = "MainActivityInformation";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.method_one).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
connect();
if (aidlInterface == null) {
return;
}
try {
Toast.makeText(MainActivity.this, "計算成功: " + aidlInterface.calculate(10, 20), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
private IMyAidlInterface aidlInterface;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
aidlInterface = IMyAidlInterface.Stub.asInterface(service);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "連接成功", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
aidlInterface = null;
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "連接斷開", Toast.LENGTH_SHORT).show();
}
});
}
};
}
四 雙向通信
單向通信指的是吆豹,數(shù)據(jù)只能從client流向Service鱼的,而雙向通信則是指數(shù)據(jù)可以在client和Service之間互相流通,雙向通信通過inout和回調(diào)監(jiān)聽可以實現(xiàn)雙向通信
提供一個用來傳遞的序列化的類
public class Person implements Parcelable {
public String name;
public int age;
public String describe;
public Person(){
}
...
@Override
public String toString() {
return "我叫" + name + ",今年" + age + "歲," + describe;
}
...
}
然后定義對應的aidl文件
package visual.share.aidlservice;
parcelable Person;
一個用來監(jiān)聽和回調(diào)數(shù)據(jù)的AIDL
interface IMyAidlInterface {
void inOutData(String name, int age, String describe, inout Person data);
void registerCallBack(PersonResultListen listen);
void unregisterCallBack(PersonResultListen listen);
void callBackData();
}
一個回調(diào)監(jiān)聽
interface PersonResultListen {
void result(inout Person person);
}
然后來實現(xiàn)IMyAidlInterface的實現(xiàn)類痘煤,如下
public class MyAidlServiceImpl extends IMyAidlInterface.Stub {
private RemoteCallbackList<PersonResultListen> remoteCallbackList = new RemoteCallbackList<>();
@Override
public void inOutData(String name, int age, String describe, Person data) throws RemoteException {
data.name = name;
data.age = age;
data.describe = describe;
}
@Override
public void registerCallBack(PersonResultListen listen) throws RemoteException {
if (listen != null) {
remoteCallbackList.register(listen);
}
}
@Override
public void unregisterCallBack(PersonResultListen listen) throws RemoteException {
if (listen != null){
remoteCallbackList.unregister(listen);
};
}
@Override
public void callBackData() throws RemoteException {
synchronized (remoteCallbackList){
int count = remoteCallbackList.beginBroadcast();
if (count > 0){
Person person = new Person();
person.name = "監(jiān)聽回調(diào)";
person.age = 20;
person.describe = "監(jiān)聽回調(diào)的描述";
for (int i = 0; i < count; i++) {
remoteCallbackList.getBroadcastItem(i).result(person);
}
}
remoteCallbackList.finishBroadcast();
}
}
}
這里我們的inOutData方法凑阶,里面用到了inout標識,用來實現(xiàn)雙向通信衷快,callBackData方法用來實現(xiàn)接口回調(diào)宙橱,而registerCallBack和unregisterCallBack則是用來注冊和取消注冊這個監(jiān)聽回調(diào),需要注意的時候蘸拔,這里使用的是RemoteCallbackList來存儲回調(diào)隊列师郑,這個類專門用來處理跨進程通信的回調(diào)
定義一個Service,用來獲取我們這個AIDL實現(xiàn)類
public class MyService extends Service {
private MyAidlServiceImpl service;
@Override
public void onCreate() {
super.onCreate();
service = new MyAidlServiceImpl();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return service;
}
}
然后在應用中就可以直接使用
public class MainActivity extends Activity {
private final String TAG = "MainActivity";
private Intent intent;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent = new Intent(MainActivity.this, MyService.class);
bindService();
findViewById(R.id.inout_data).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (aidlInterface != null){
try {
Person person = new Person();
aidlInterface.inOutData("小明", 25, "長得很帥",person);
Toast.makeText(MainActivity.this, person.toString(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}
});
findViewById(R.id.callback_data).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (aidlInterface != null){
try {
aidlInterface.callBackData();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}
});
}
private void bindService(){
boolean flag = bindService(intent, connection,BIND_AUTO_CREATE);
Log.d(TAG, "connect status" + flag);
}
private IMyAidlInterface aidlInterface;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
aidlInterface = IMyAidlInterface.Stub.asInterface(service);
try {
aidlInterface.asBinder().linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
try {
aidlInterface.registerCallBack(personResultListen);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
aidlInterface = null;
}
};
private PersonResultListen.Stub personResultListen = new PersonResultListen.Stub() {
@Override
public void result(Person person) throws RemoteException {
Toast.makeText(MainActivity.this, person.toString(), Toast.LENGTH_SHORT).show();
}
};
private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
try {
aidlInterface.unregisterCallBack(personResultListen);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
if (aidlInterface != null){
aidlInterface.asBinder().unlinkToDeath(deathRecipient, 0);
}
bindService();
}
};
}
打開應用后就綁定服務调窍,這里我們添加監(jiān)聽是在綁定成功之后添加宝冕,并且使用了死亡代理,Binder.DeathRecipient陨晶,當和服務斷開的時候猬仁,再取消注冊的監(jiān)聽,布局如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/inout_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:text="獲取inout數(shù)據(jù)" />
<Button
android:id="@+id/callback_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:text="獲取回調(diào)數(shù)據(jù)" />
</LinearLayout>
這里提供了倆個按鈕先誉,點擊后湿刽,分別獲得通過inout處理后的數(shù)據(jù),和通過回調(diào)返回的數(shù)據(jù)褐耳,實現(xiàn)了雙向通信诈闺。
最后測試demo結(jié)構(gòu)如下
IntentService
Service因為是運行在主線程,不能處理耗時任務铃芦,否則會可能會出現(xiàn)ANR問題雅镊,IntentService 是繼承于 Service 并處理異步請求的一個類,在 IntentService 內(nèi)有一個子線程來處理耗時操作刃滓,啟動 IntentService 的方式和啟動傳統(tǒng) Service 一樣仁烹,同時,當任務執(zhí)行完后咧虎,IntentService 會自動停止卓缰,而不需要我們?nèi)ナ謩涌刂啤?/p>
踩坑
1.binderService和unbinderService需要用使用同一個ServiceConnection
如果不使用同一個,當調(diào)用unbindService時砰诵,會提示Service not registered錯誤
2.onServiceDisconnected不回調(diào)
當bindService后征唬,如果Client和Service連接成功,會調(diào)用ServiceConnection的onServiceConnected方法茁彭,但是調(diào)用unbindService解除綁定時总寒,不會調(diào)用onServiceDisconnected方法,這是因為當Client和Service連接后理肺,只有到Service因為異常原因崩潰摄闸,才會調(diào)用onServiceDisconnected方法