如果你對Service還未了解,建議先閱讀我寫的另外一篇文章:
Android四大組件:Service史上最全面解析
多個應用程序共享同一個後臺服務(遠程服務)
即一個遠程Service與多個應用程序的組件(四大組件)進行跨進程通信
為了讓遠程Service與多個應用程序的組件(四大組件)進行跨進程通信(IPC),需要使用AIDL
- IPC:Inter-Process Communication,即跨進程通信
- AIDL:Android Interface Definition Language,即Android接口定義語言;用於讓某個Service與多個應用程序組件之間進行跨進程通信,從而可以實現多個應用程序共享同一個Service的功能。
在多進程通信中,存在兩個進程角色(以最簡單的為例):服務器端和客戶端
以下是兩個進程角色的具體使用步驟:
服務器端(Service)
步驟1:新建定義AIDL文件,並聲明該服務需要向客戶端提供的接口
步驟2:在Service子類中實現AIDL中定義的接口方法,並定義生命週期的方法(onCreat、onBind()、blabla)
步驟3:在AndroidMainfest.xml中註冊服務 & 聲明為遠程服務
客戶端(Client)
步驟1:拷貝服務端的AIDL文件到目錄下
步驟2:使用Stub.asInterface接口獲取服務器的Binder,根據需要調用服務提供的接口方法
步驟3:通過Intent指定服務端的服務名稱和所在包,綁定遠程Service
接下來,我將用一個具體實例來介紹遠程Service的使用
新建一個服務器端的工程:Service - server
先下Demo再看,效果會更好:Github_RemoteService_Server
步驟1. 新建一個AIDL文件
步驟2. 在新建AIDL文件裡定義Service需要與Activity進行通信的內容(方法),並進行編譯(Make Project)
// 在新建的AIDL_Service1.aidl裡聲明需要與Activity進行通信的方法
package scut.carson_ho.demo_service;
interface AIDL_Service1 {
void AIDL_Service();
}
//AIDL中支持以下的數據類型
//1. 基本數據類型
//2. String 和CharSequence
//3. List 和 Map ,List和Map 對象的元素必須是AIDL支持的數據類型;
//4. AIDL自動生成的接口(需要導入-import)
//5. 實現android.os.Parcelable 接口的類(需要導入-import)
步驟3:在Service子類中實現AIDL中定義的接口方法,並定義生命週期的方法(onCreat、onBind()、blabla)
MyService.java
public class MyService extends Service {
<span class="hljs-comment">// 實例化AIDL的Stub類(Binder的子類)</span>
AIDL_Service1.Stub mBinder = <span class="hljs-keyword">new</span> AIDL_Service1.Stub() {
<span class="hljs-comment">//重寫接口裡定義的方法</span>
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">AIDL_Service</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> RemoteException </span>{
System.out.println(<span class="hljs-string">"客戶端通過AIDL與遠程後臺成功通信"</span>);
}
};
//重寫與Service生命週期的相關方法
@Override
public void onCreate() {
super.onCreate();
System.out.println(<span class="hljs-string">"執行了onCreat()"</span>);
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">onStartCommand</span><span class="hljs-params">(Intent intent, <span class="hljs-keyword">int</span> flags, <span class="hljs-keyword">int</span> startId)</span> </span>{
System.out.println(<span class="hljs-string">"執行了onStartCommand()"</span>);
<span class="hljs-function"><span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.<span class="hljs-title">onStartCommand</span><span class="hljs-params">(intent, flags, startId)</span></span>;
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">onDestroy</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">super</span>.onDestroy();
System.out.println(<span class="hljs-string">"執行了onDestory()"</span>);
}
<span class="hljs-meta">@Nullable</span>
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-function">IBinder <span class="hljs-title">onBind</span><span class="hljs-params">(Intent intent)</span> </span>{
System.out.println(<span class="hljs-string">"執行了onBind()"</span>);
<span class="hljs-comment">//在onBind()返回繼承自Binder的Stub類型的Binder,非常重要</span>
<span class="hljs-keyword">return</span> mBinder;
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">boolean</span> <span class="hljs-title">onUnbind</span><span class="hljs-params">(Intent intent)</span> </span>{
System.out.println(<span class="hljs-string">"執行了onUnbind()"</span>);
<span class="hljs-function"><span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.<span class="hljs-title">onUnbind</span><span class="hljs-params">(intent)</span></span>;
}
}
步驟4:在AndroidMainfest.xml中註冊服務 & 聲明為遠程服務
<service
android:name=".MyService"
android:process=":remote" //將本地服務設置成遠程服務
android:exported="true" //設置可被其他進程調用
>
//該Service可以響應帶有scut.carson_ho.service_server.AIDL_Service1這個action的Intent。
//此處Intent的action必須寫成“服務器端包名.aidl文件名”
<intent-filter>
<action android:name="scut.carson_ho.service_server.AIDL_Service1"/>
</intent-filter>
<span class="hljs-tag"></<span class="hljs-name">service</span>></span></span></code></pre>
至此,服務器端(遠程Service)已經完成了。
4.2 客戶端(Client)
新建一個客戶端的工程:Service - Client
先下Demo再看,效果會更好:Github_RemoteService_Client
步驟1:將服務端的AIDL文件所在的包複製到客戶端目錄下(Project/app/src/main),並進行編譯
注:記得要原封不動地複製!!什麼都不要改!
步驟2:在主佈局文件定義“綁定服務”的按鈕
MainActivity.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="scut.carson_ho.service_client.MainActivity">
<span class="hljs-tag"><<span class="hljs-name">Button</span>
<span class="hljs-attr">android:layout_centerInParent</span>=<span class="hljs-string">"true"</span>
<span class="hljs-attr">android:id</span>=<span class="hljs-string">"@+id/bind_service"</span>
<span class="hljs-attr">android:layout_width</span>=<span class="hljs-string">"match_parent"</span>
<span class="hljs-attr">android:layout_height</span>=<span class="hljs-string">"wrap_content"</span>
<span class="hljs-attr">android:text</span>=<span class="hljs-string">"綁定服務"</span>
/></span>
</RelativeLayout>
步驟3:在MainActivity.java裡
MainActivity.java
public class MainActivity extends AppCompatActivity {
<span class="hljs-keyword">private</span> Button bindService;
<span class="hljs-comment">//定義aidl接口變量</span>
<span class="hljs-keyword">private</span> AIDL_Service1 mAIDL_Service;
<span class="hljs-comment">//創建ServiceConnection的匿名類</span>
<span class="hljs-keyword">private</span> ServiceConnection connection = <span class="hljs-keyword">new</span> ServiceConnection() {
<span class="hljs-comment">//重寫onServiceConnected()方法和onServiceDisconnected()方法</span>
<span class="hljs-comment">//在Activity與Service建立關聯和解除關聯的時候調用</span>
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onServiceDisconnected</span><span class="hljs-params">(ComponentName name)</span> </span>{
}
<span class="hljs-comment">//在Activity與Service建立關聯時調用</span>
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onServiceConnected</span><span class="hljs-params">(ComponentName name, IBinder service)</span> </span>{
<span class="hljs-comment">//使用AIDLService1.Stub.asInterface()方法獲取服務器端返回的IBinder對象</span>
<span class="hljs-comment">//將IBinder對象傳換成了mAIDL_Service接口對象</span>
mAIDL_Service = AIDL_Service1.Stub.asInterface(service);
<span class="hljs-keyword">try</span> {
<span class="hljs-comment">//通過該對象調用在MyAIDLService.aidl文件中定義的接口方法,從而實現跨進程通信</span>
mAIDL_Service.AIDL_Service();
} <span class="hljs-keyword">catch</span> (RemoteException e) {
e.printStackTrace();
}
}
};
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span><span class="hljs-params">(Bundle savedInstanceState)</span> </span>{
<span class="hljs-keyword">super</span>.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService = (Button) findViewById(R.id.bind_service);
<span class="hljs-comment">//設置綁定服務的按鈕</span>
bindService.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span><span class="hljs-params">(View v)</span> </span>{
<span class="hljs-comment">//通過Intent指定服務端的服務名稱和所在包,與遠程Service進行綁定</span>
<span class="hljs-comment">//參數與服務器端的action要一致,即"服務器包名.aidl接口文件名"</span>
Intent intent = <span class="hljs-keyword">new</span> Intent(<span class="hljs-string">"scut.carson_ho.service_server.AIDL_Service1"</span>);
<span class="hljs-comment">//Android5.0後無法只通過隱式Intent綁定遠程Service</span>
<span class="hljs-comment">//需要通過setPackage()方法指定包名</span>
intent.setPackage(<span class="hljs-string">"scut.carson_ho.service_server"</span>);
<span class="hljs-comment">//綁定服務,傳入intent和ServiceConnection對象</span>
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
});
}
}</code></pre>
4.3 測試結果
從上面測試結果可以看出:
- 打印的語句分別運行在不同進程(看語句前面的包名);
- 客戶端調用了服務端Service的方法
即客戶端和服務端進行了跨進程通信
4.4 Demo地址
5. 總結
- 本文對Android組件Service中的遠程Service進行了全面介紹
- 如果你還想了解關於Service的其他知識,請瀏覽以下文章:
Android四大組件:Service史上最全面解析
Android:Service生命週期最全面解析
Android:(本地、可通信的、前臺、遠程)Service使用全面介紹
Android:遠程服務Service(含AIDL & IPC講解)
Android多線程全面解析:IntentService用法&源碼
- 接下來會介紹繼續介紹Android開發中的相關知識,有興趣可以繼續關注Carson_Ho的安卓開發筆記
請點贊!因為你們的贊同/鼓勵是我寫作的最大動力!
相關文章閱讀
Android開發:最全面、最易懂的Android屏幕適配解決方案
Android開發:Handler異步通信機制全面解析(包含Looper、Message Queue)
Android開發:頂部Tab導航欄實現(TabLayout+ViewPager+Fragment)
Android開發:底部Tab菜單欄實現(FragmentTabHost+ViewPager)
Android開發:JSON簡介及最全面解析方法!
Android開發:XML簡介及DOM、SAX、PULL解析對比