Android處理序間通訊

來源:互聯網
上載者:User

標籤:

如果想要進行IPC通訊,一般寫一個AIDL介面,再寫一個Service子類,然後實現AIDL介面 當做IBinder返回給Activity介面層。
如果不想寫AIDL介面檔案,只是單線程中與Service進行通訊 我們可以用Android寫好的Messenger類來處理,一樣能將訊息傳遞給Service進行通訊。
什麼是aidl:
aidl是 Android Interface definition language的縮寫,它是一種android內部進程通訊介面的描述語言,通過它我們可以定義進程間的通訊介面
icp:interprocess communication :內部進程通訊。
在 Android中, 每個應用程式都有自己的進程,當需要在不同的進程之間傳遞對象時, Java中是不支援跨進程記憶體共用的。因此要傳遞對象, 需要把對象解析成作業系統能夠理解的資料格式, 以達到跨界對象訪問的目的。在JavaEE中,採用RMI通過序列化傳遞對象。在Android中, 則採用AIDL(Android Interface Definition Language:介面定義語言)方式實現。
1.在Android工程的Java包目錄中建立一個副檔名為aidl的檔案。該檔案的文法類似於Java代碼,但會稍有不同。
2.如果aidl檔案的內容是正確的,ADT會自動產生一個Java介面檔案(*.java)。
3.建立一個服務類(Service的子類)。
4.實現由aidl檔案產生的Java介面。
5.在AndroidManifest.xml檔案中配置AIDL服務,尤其要注意的是,<action>標籤中android:name的屬性值就是用戶端要引用該服務的ID,也就是Intent類的參數值。 

AIDL 是一種介面定義語言,用於約束兩個進程間的通訊規則,供編譯器產生代碼,實現Android裝置上的兩個處理序間通訊(IPC)。進程之間的通訊資訊,首先會被轉換成AIDL協議訊息,然後發送給對方,對方收到AIDL協議訊息後再轉換成相應的對 象。由於進程之間的通訊資訊需要雙向轉換,所以android採用代理類在背後實現了資訊的雙向轉換,代理類由android編譯器產生,對開發人員來說 是透明的。
實現進程通訊,一般需要下面四個步驟:
假設A應用需要與B應用進行通訊,調用B應用中的download(String path)方法,B應用以Service方式向A應用提供服務。需要下面四個步驟:
一、 在B應用中建立*.aidl檔案,aidl檔案的定義和介面的定義很相類,如:在com.robert.aidl包下建立IDownloadService.aidl檔案,內容如下:
package com.robert.aidl;
interface IDownloadService {
   void download(String path);
}
AIDL使用簡單的文法來聲明介面,描述其方法以及方法的參數和返回值。這些參數和返回值可以是任何類型,甚至是其他AIDL產生的介面。其中對於Java程式設計語言的基礎資料型別 (Elementary Data Type) (int, long, char, boolean等),String和CharSequence,集合介面類型List和Map,不需要import 語句。 而如果需要在AIDL中使用其他AIDL介面類型,需要import,即使是在相同包結構下。AIDL允許傳遞實現Parcelable介面的類,需要import.需要特別注意的是, 對於非基礎資料型別 (Elementary Data Type),也不是String和CharSequence類型的,需要有方向指示,包括in、out和inout,in表示由用戶端設定,out表示由服務端設定,inout是兩者均可設定AIDL只支援介面方法,不能公開static變數。
當完成aidl檔案建立後,項目的gen目錄中同步產生IDownloadService.java介面檔案。介面檔案中產生一個 Stub的抽象類別,裡麵包括aidl定義的方法,還包括一些其它輔助方法。值得關注的是asInterface(IBinder iBinder),它返回介面類型的執行個體,對於遠程服務調用,遠程服務返回給用戶端的對象為代理對象,用戶端在 onServiceConnected(ComponentName name, IBinder service)方法引用該對象時不能直接強轉成介面類型的執行個體,而應該使用asInterface(IBinder iBinder)進行類型轉換。
編寫Aidl檔案時,需要注意下面幾點:
1.介面名和aidl檔案名稱相同。
2.介面和方法前不用加存取權限修飾符public,private,protected等,也不能用final,static。
3.Aidl預設支援的類型包話java基本類型(int、long、boolean等)和(String、List、Map、 CharSequence),使用這些類型時不需要import聲明。對於List和Map中的元素類型必須是Aidl支援的類型。如果使用自訂類型作 為參數或返回值,自訂類型必須實現Parcelable介面。
4.自訂類型和AIDL產生的其它介面類型在aidl描述檔案中,應該顯式import,即便在該類和定義的包在同一個包中。
5.在aidl檔案中所有非Java基本型別參數必須加上in、out、inout標記,以指明參數是輸入參數、輸出參數還是輸入輸出參數。
6.Java原始類型預設的標記為in,不能為其它標記。
二、在B應用中實現aidl檔案產生的介面(本例是IDownloadService),但並非直接實現介面,而是通過繼承介面的Stub來實現,並且實現介面方法的代碼。內容如下:
public class ServiceBinder extends IDownloadService.Stub {
   @Override
   public void download(String path) throws RemoteException {
      Log.i("DownloadService", path);
   }    
}
三、 在B應用中建立一個Service(服務),在服務的onBind(Intent intent)方法中返回實現了aidl介面的對象)。內容如下:
public class DownloadService extends Service {
   private ServiceBinder serviceBinder = new ServiceBinder();
   @Override
   public IBinder onBind(Intent intent) {
      return serviceBinder;
   }
   public class ServiceBinder extends IDownloadService.Stub {
      @Override
      public void download(String path) throws RemoteException {
        Log.i("DownloadService", path);
      }    
   }
}
其他應用可以通過隱式意圖訪問服務,意圖的動作可以自訂,AndroidManifest.xml配置代碼如下:
<service android:name=".DownloadService" >
   <intent-filter>
      <action android:name="com.robert.process.aidl.DownloadService" />
   </intent-filter>
</service>
四、把B應用中aidl檔案所在package連同aidl檔案一起拷貝到用戶端A應用,A應用的gen目錄中為aidl檔案同步產生IDownloadService.java介面檔案,接下來就可以在A應用中實現與B應用通訊,代碼如下:
public class ClientActivity extends Activity {
   private IDownloadService downloadService;
   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
     this.bindService(new Intent("com.robert.process.aidl.DownloadService"), this.serviceConnection, BIND_AUTO_CREATE);//綁定到服務
   }
   @Override
   protected void onDestroy() {
      super.onDestroy();
      this.unbindService(serviceConnection);//解除服務
   } 
   private ServiceConnection serviceConnection = new ServiceConnection() {
      @Override
      public void onServiceConnected(ComponentName name, IBinder service) {
        downloadService = IDownloadService.Stub.asInterface(service);
        try {
           downloadService.download("http://www.itcast.cn");
        } catch (RemoteException e) {
           Log.e("ClientActivity", e.toString());
        }
      }
      @Override
      public void onServiceDisconnected(ComponentName name) {
        downloadService = null;
      }
   };
}
Aidl預設支援的類型包話java基本類型(int、long、boolean等)和(String、List、Map、CharSequence),如果要傳遞自訂的類型該實現方式如下
1. 建立自訂類型,並實現Parcelable介面,使其支援parcelable協議。如:在com.robert.domain包下建立Person.java:
package com.robert.domain;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable
   private Integer id;
   private String name;
   public Person(){}
   public Person(Integer id, String name) {
      this.id = id;
      this.name = name;
   }
   public Integer getId() {
      return id;
   }
   public void setId(Integer id) {
      this.id = id;
   }
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   } 
   @Override
   public int describeContents() {
      return 0;
   }
   @Override
   public void writeToParcel(Parcel dest, int flags) {//把javanbean中的資料寫到Parcel
      dest.writeInt(this.id);
      dest.writeString(this.name);
   }
//添加一個靜態成員,名為CREATOR,該對象實現了Parcelable.Creator介面
   public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>(){
      @Override
      public Person createFromParcel(Parcel source) {//從Parcel中讀取資料,返回person對象
        return new Person(source.readInt(), source.readString());
      }
      @Override
      public Person[] newArray(int size) {
        return new Person[size];
      }
   };
}
2. 在自訂類型所在包下建立一個aidl檔案對自訂類型進行聲明,檔案的名稱與自訂類型同名。
package com.robert.domain;
parcelable Person;
3. 在介面aidl檔案中使用自訂類型,需要使用import顯式匯入,本例在com.robert.aidl包下建立IPersonService.aidl檔案,內容如下:
package com.robert.aidl;
import com.robert.domain.Person;
interface IPersonService {
      void save(in Person person);
}
4. 在實現aidl檔案產生的介面(本例是IPersonService),但並非直接實現介面,而是通過繼承介面的Stub來實現,並且實現介面方法的代碼。內容如下:
public class ServiceBinder extends IPersonService.Stub {
       @Override
       public void save(Person person) throws RemoteException {
   Log.i("PersonService", person.getId()+"="+ person.getName());
       }   
}
5. 建立一個Service(服務),在服務的onBind(Intent intent)方法中返回實現了aidl介面的對象。內容如下:
public class PersonService extends Service {
   private ServiceBinder serviceBinder = new ServiceBinder();
   @Override
   public IBinder onBind(Intent intent) {
      return serviceBinder;
   }
public class ServiceBinder extends IPersonService.Stub {
       @Override
       public void save(Person person) throws RemoteException {
   Log.i("PersonService", person.getId()+"="+ person.getName());
       }
}
}
其他應用可以通過隱式意圖訪問服務,意圖的動作可以自訂,AndroidManifest.xml配置代碼如下:
<service android:name=".PersonService" >
   <intent-filter>
      <action android:name="com.robert.process.aidl.PersonService " />
   </intent-filter>
</service>
6. 把應用中的aidl檔案和所在package一起拷貝到用戶端應用的src目錄下,用戶端應用的gen目錄中為aidl檔案同步生 成IPersonService.java介面檔案,接下來再把自訂類型檔案和型別宣告aidl檔案及所在package一起拷貝到用戶端應用的src 目錄下。
最後就可以在用戶端應用中實現與遠程服務的通訊,代碼如下:
public class ClientActivity extends Activity {
   private IPersonService personService;
   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
      this.bindService(new Intent("com.robert.process.aidl.PersonService"), this.serviceConnection, BIND_AUTO_CREATE);//綁定到服務
   }
@Override
   protected void onDestroy() {
      super.onDestroy();
      this.unbindService(serviceConnection);//解除服務
   } 
   private ServiceConnection serviceConnection = new ServiceConnection() {
      @Override
      public void onServiceConnected(ComponentName name, IBinder service) {
        personService = IPersonService.Stub.asInterface(service);
        try {
           personService.save(new Person(56,"liming"));
        } catch (RemoteException e) {
           Log.e("ClientActivity", e.toString());
        }
      }
      @Override
      public void onServiceDisconnected(ComponentName name) {
        personService = null;
      }
   };
}
本地服務和 AIDL服務的區別:
本地服務不支援onBind(),它從onBind()返回null,這種類型的服務只能由承載服務的應用程式組件訪問。可以調用 startService()來調用本地服務。AIDL服務可以同時供 同一進程內的組件和其他應用程式的組件使用。這種類型的服務在AIDL 檔案中為自身與其用戶端定義一個契約。服務實現 AIDL契約,而用戶端綁定到 AIDL定義。服務通過從 onBind()方法 返回AIDL介面的實現,來實現契約。用戶端通過調用 bindService()來綁定到AIDL服務,並調用 unBindService()來從服務斷開。

我們使用Handler都是在一個進程中使用的,我們可以使用另外一種方式,
android系統的android.os.Messenger可以很方便的跨進程使用Handler。
服務端:
public class MessengerTestService extends Service {
  
    protected static final String TAG = "MessengerTestService";
      
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 1:
                Log.d(TAG, "收到訊息");
                 //擷取用戶端message中的Messenger,用於回調
                final Messenger callback = msg.replyTo;
                try {
                    // 回調
                    callback.send(Message.obtain(null, 0));
                } catch (RemoteException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                break;
            }
        }
    };
      
    @Override
    public IBinder onBind(Intent intent) {
        return new Messenger(mHandler).getBinder();
    }
  
}

用戶端
public class MainActivity extends Activity {
  
    protected static final String TAG = "MainActivity";
    Messenger messenger;
    Messenger reply;
      
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        reply = new Messenger(handler);
        Intent intent = new Intent();
        intent.setClassName("test.messenger", "test.messenger.MessengerTestService");
          
        // 綁定服務
        bindService(intent, new ServiceConnection() {
              
            @Override
            public void onServiceDisconnected(ComponentName name) {
                  
            }
              
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Toast.makeText(MainActivity.this, "bind success", 0).show();
                messenger = new Messenger(service);
            }
        }, Context.BIND_AUTO_CREATE);
          
    }
    public void sendMessage(View v) {
        Message msg = Message.obtain(null, 1);
        // 設定回調用的Messenger
        msg.replyTo = reply;
        try {
            messenger.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    private Handler handler = new Handler() {
  
        @Override
        public void handleMessage(Message msg) {
            Log.d(TAG, "回調成功");
        }
          
    };
}

用戶端綁定服務端,擷取遠程Messenger的binder對象。調用Messenger的send函數,就可以把Message發送至服務端的Handler。
同時,如果需要服務端回調用戶端,則可以在send的Message中設定replyTo,服務端就可以往用戶端發送訊息了。
下面我們看下Messenger的源碼
建構函式
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();  
    }
Handler.getIMessenger()返回的是一個IMessenger的binder對象,它的send方法將會調用Handler的sendMessage方法。
    public void send(Message message) throws RemoteException {
        mTarget.send(message);

    }







Android處理序間通訊

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.