Android-bindService遠端服務(Aidl)-初步
阿新 • • 發佈:2018-12-08
之前上一篇講解到本地服務,本地服務只能在自身APP中Activity訪問Service,呼叫Service裡面到方法等操作
如果想A應用訪問B應用裡面的方法,屬於跨程序呼叫,如果Android不特供這種跨程序間通訊等API,是不能實現的
Google Android 為了解決 A應用--->B應用 跨程序訪問通訊,提供了一種機制,就是IBInder,這種IBinder機制是Google工程師加入進去的,以前的Linux裡是沒有這個機制的
bindService 會返回IBinder介面,IBinder就是Google工程師在為了解決這種跨應用跨程序通訊,IBinder還需要結合Aidl才能實現遠端服務(跨程序跨應用通訊)
注意:⚠️以前本地服務是通過顯示意圖去繫結,現在遠端服務由於無法拿到另外應用的位元組碼,只能隱士意圖去繫結
Service2,作為服務端,需要把MyService暴露出去
<!-- android:process=":remote",代表在應用程式裡,當需要該service時,會自動建立新的程序。 而如果是android:process="remote",沒有“:”分號的,則建立全域性程序,不同的應用程式共享該程序。 --> <service android:name=".service.MyService" android:exported="true" android:enabled="true" android:process=":remote"> <!-- 允許對外輸出 --> <!-- 想要其他應用程式可以訪問我的訪問,就必須要對外暴漏 --> <intent-filter> <action android:name="liudeli.service2.service.MyService" /> </intent-filter> </service>
Service2,把邏輯都解除安裝MyService
package liudeli.service2.service; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import liudeli.service2.IMusician; import liudeli.service2.service.inter.ISinger; public class MyService extends Service { @Override public IBinder onBind(Intent intent) { // return new MyBinder(); 以前這種方式只適合本地服務(自身APP) return new MyBinderAidl(); } /** * 以前本地服務的方式 */ /*class MyBinder extends Binder implements ISinger { *//** * 通過ID查詢歌手姓名 * * @param id * @return *//* @Override public String querySingerNameByID(int id) { String result = null; switch (id) { case 1: result = "黃家駒"; break; case 2: result = "王傑"; break; case 3: result = "陳百強"; break; default: result = "張雨生"; break; } return result; } }*/ /** * AIDL的方式,只有這種方式才能完成遠端服務 */ class MyBinderAidl extends IMusician.Stub { @Override public String querySingerNameByID(int id) throws RemoteException { String result = null; int result2 = 0; switch (id) { case 1: result = "黃家駒"; result2 = 210000; break; case 2: result = "王傑"; result2 = 180000; break; case 3: result = "陳百強"; result2 = 200000; break; default: result = "張雨生"; result2 = 200090; break; } return result; } } }
Service2,以前本地服務介面的定義:
package liudeli.service2.service.inter; import java.lang.String; public interface ISinger { /** * 通過ID查詢歌手姓名 * @param id * @return */ public String querySingerNameByID(int id); }
Service2,現在遠端服務的AIdl語言介面的定義:
// IMusician.aidl package liudeli.service2; // Declare any non-default types here with import statements interface IMusician { /** * 通過ID查詢歌手姓名 * @param id * @return */ String querySingerNameByID(int id); }
----------------------------- 分割線 下面程式碼是 Service1 訪問者相關的了
Service1,⚠️要把Service2 aidl 帶包一起copy到Service1,保持aidl相關一模一樣
Service1,繫結遠端服務相關程式碼
package liudeli.service1; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.view.View; import android.widget.Toast; import liudeli.service2.IMusician; public class RemoteActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_remote); bindServiceAction(); } /** * Activity初始化的時候繫結服務 */ private void bindServiceAction() { Intent intent = new Intent(); intent.setAction("liudeli.service2.service.MyService"); // android 5.0以後直設定action不能啟動相應的服務,需要設定packageName或者Component intent.setPackage("liudeli.service2"); bindService(intent, connection, BIND_AUTO_CREATE); } private IMusician iMusician; /** * 定義服務連線物件,用於連線遠端服務 */ private ServiceConnection connection = new ServiceConnection() { /** * 服務連線成功 * @param name 可以獲取到服務到完整資訊 * @param service 服務那邊返回到IBinder介面 */ @Override public void onServiceConnected(ComponentName name, IBinder service) { // 這裡和本地服務不同,本地服務是強制型別轉換,遠端服務直接使用代理完成 iMusician = IMusician.Stub.asInterface(service); } /** * 服務斷開連線 * @param name */ @Override public void onServiceDisconnected(ComponentName name) { } }; /** * 查詢遠端服務裡面的資料 * @param view */ public void queryRemoteService(View view) { if (null == iMusician) { Toast.makeText(this, "繫結遠端服務失敗,無法查詢", Toast.LENGTH_LONG).show(); return; } try { String result = iMusician.querySingerNameByID(1); Toast.makeText(this, "查詢成功:" + result, Toast.LENGTH_LONG).show(); } catch (RemoteException e) { e.printStackTrace(); Toast.makeText(this, "遠端服務失敗 遠端異常", Toast.LENGTH_LONG).show(); } } /** * Activity結束時,記得一定要解綁服務,否則會報連線資源異常 */ @Override protected void onDestroy() { super.onDestroy(); unbindService(connection); } }
Layout相關程式碼:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="queryRemoteService" android:text="查詢遠端服務" /> </LinearLayout>
結果: