標籤:aidl 通訊
首先,為什麼要用aidl
---------------------------------------------------------------------------------------------------------------------------
aidl其實就是進程間的通訊官方文檔特別提醒我們何時使用AIDL是必要的:只有你允許用戶端從不同的應用程式為了進程間的通訊而去訪問你的service,以及想在你的service處理多線程。如果不需要進行不同應用程式間的並發通訊(IPC),you should create your interface by implementing a Binder;或者你想進行IPC,但不需要處理多線程的,則implement your interface using a Messenger
簡單說就是,1、你某個service想被別人使用;2、你想使用其它進程的service
然後,怎麼用aidl(一下是一個最簡單的使用,從服務端擷取一個字串)-------------------------------------------------------------------------
步驟:服務端1、在服務程式端建立xxx.aidl檔案,裡面放介面(這個介面要與檔案名稱一樣),寫法跟寫Java程式一樣2、建立一個Service,這個Service裡面實現之前那個aidl檔案裡面的介面3、manifest.xml檔案註冊Serviceclient端1、copy之前的那個aidl檔案過來(要獨立一個跟服務端檔案夾相同名字的檔案夾,否則會報錯最後面的那個錯誤)2、在activity裡面初始化ServiceConnection(用來bind Service的),和
獲得服務端的service服務端的service = 介面.Stub.asInterface(service);//在ServiceConnection的onServiceConnected裡面實現3、bindService
實現代碼:先看一下目錄結構
ExecuteMyAidlService.aidl檔案(注意介面名稱要與檔案名稱前邊相同)
package aid;interface ExecuteMyAidlService { String sayHello();}MyAidlService.java檔案
package aidlservice;import aid.ExecuteMyAidlService;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;public class MyAidlService extends Service{private static final String TAG = "MyAidlService"; private ExecuteMyAidlService.Stub mBinder = new ExecuteMyAidlService.Stub() {@Overridepublic String sayHello() throws RemoteException {// TODO Auto-generated method stubreturn "hello ";} };@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn mBinder;}private void Log(String str) { android.util.Log.d(TAG, "------ " + str + "------"); } @Override public void onCreate() { Log("service create"); } @Override public void onStart(Intent intent, int startId) { Log("service start id=" + startId); } }activity裡面沒有東西就不貼了
用戶端的目錄結構
ClientActivity的代碼:
package com.example.aidlclienttest;import aid.ExecuteMyAidlService;import android.support.v7.app.ActionBarActivity;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.Menu;import android.view.MenuItem;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;public class ClientActivity extends ActionBarActivity { private ExecuteMyAidlService mIaidlServerService = null; private TextView mTextView = null; private Button mButton = null; private ServiceConnection mConnection = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { mIaidlServerService = null; } public void onServiceConnected(ComponentName name, IBinder service) { mIaidlServerService = ExecuteMyAidlService.Stub.asInterface(service); //aidl通訊 try { String mText = "Say hello: " + mIaidlServerService.sayHello(); mTextView.setText(mText); } catch (RemoteException e) { e.printStackTrace(); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_client); mTextView = (TextView)findViewById(R.id.helloword); mButton = (Button)findViewById(R.id.getServiceFromAidl); mButton.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View v) {// TODO Auto-generated method stubIntent service = new Intent("aidl.ExecuteMyAidlService");service.setAction("aidl.ExecuteMyAidlService"); bindService(service, mConnection,BIND_AUTO_CREATE); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.client, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); }}
這裡就實現了最簡單的aidl通訊的過程看一下效果先開啟服務端
然後再啟動client端,點擊bindservice之後
進階aidl
---------------------------------------------------------------------------------------------1、如果我想從service擷取對象呢?(最要應用)將對象實現parcelable 介面 為這個對象寫個aidl檔案[裡面只需要定義一下這個對象就行了如:]
parcelable Object
還是看一下代碼吧先看一下這個結構增加一個Person的類,這個類要實現parcelable 介面代碼
package aid;import android.os.Parcel;import android.os.Parcelable;import android.os.Parcelable.Creator;public class Person implements Parcelable{private String name;private int age; private String place; public Person(Parcel source) {// TODO Auto-generated constructor stubname = source.readString(); age = source.readInt(); place = source.readString();}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + ", place=" + place+ "]";}public Person() {// TODO Auto-generated constructor stub}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getPlace() {return place;}public void setPlace(String place) {this.place = place;}@Overridepublic int describeContents() {// TODO Auto-generated method stubreturn 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {// TODO Auto-generated method stubdest.writeString(name); dest.writeInt(age); dest.writeString(place);}public static final Parcelable.Creator<Person> CREATOR = new Creator<Person>(){@Overridepublic Person createFromParcel(Parcel source) {// TODO Auto-generated method stubPerson person = new Person(source); return person;}@Overridepublic Person[] newArray(int size) {// TODO Auto-generated method stubreturn new Person[size];}};}同樣也要寫一個aidl檔案
想之前的那個介面裡面增加一個擷取對象的空函數
在service裡面實現,步驟跟之前的類似不多敘述了
這樣在用戶端那邊就可以擷取到這個對象了2、如果activity要往service裡面傳遞資料呢?【如果是對象一樣是要parcelable,(因為client和server的aidl檔案是一樣的)】要在介面函數那裡加上in基本類型(int,long,char,boolean等),String,CharSequence,List,Map,其他類型必須使用import匯入,即使它們可能在同一個包裡,比如上面的Person,儘管它和IMyService在同一個包中,但是還是需要顯示的import進來。
另外,介面中的參數除了aidl支援的類型,其他類型必須標識其方向:到底是輸入還是輸出抑或兩者兼之,用in,out或者inout來表示,一般in標記,因為大多數情況下輸入型參數。
這個也很簡單:過程是某個client這邊向serverSevice傳入一個對象,service對這個對象處理完,通知或者返回client
貼一下關鍵的代碼
client端
private ServiceConnection mConnection = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { mIaidlServerService = null; } public void onServiceConnected(ComponentName name, IBinder service) { mIaidlServerService = ExecuteMyAidlService.Stub.asInterface(service); //aidl通訊 try { String mText = "Say hello: " + mIaidlServerService.sayHello(); Person mPerson = mIaidlServerService.getDefaultPerson(); System.out.println(mPerson); mPerson.setAge(mPerson.getAge()+1);
<span style="white-space:pre"></span>//這裡向service傳入對象 mIaidlServerService.tellMeHowOldAmI(mPerson,mClientCallBack); mTextView.setText(mText); } catch (RemoteException e) { e.printStackTrace(); } } };
這邊要實現callback
ClientCallBack.Stub mClientCallBack = new ClientCallBack.Stub() {<span style="white-space:pre"></span><span style="white-space:pre"></span>@Override<span style="white-space:pre"></span>public void handleByServer(String param) throws RemoteException {<span style="white-space:pre"></span>// TODO Auto-generated method stub<span style="white-space:pre"></span>Toast.makeText(getApplicationContext(), param, Toast.LENGTH_LONG).show();<span style="white-space:pre"></span>}<span style="white-space:pre"></span>};
服務端
@Overridepublic String tellMeHowOldAmI(Person person,ClientCallBack mClientCallBack) throws RemoteException {// TODO Auto-generated method stubmCallBack = mClientCallBack;mCallBack.handleByServer("your age is "+String.valueOf(person.getAge()));return String.valueOf(person.getAge());} };
過程中碰到的錯誤------------------------------------------------------------------------------------------------------------------------------------------------java.lang.SecurityException: Binder invocation to an incorrect interface
在service端和client端都有.aidl檔案,一定要放在單獨的包中,因為兩個app中的MainActivity的包名不相同,會導致兩個.aidl檔案內容不同。單獨一個包,只放.aidl檔案,就能保證包名和檔案內容都是相同的。
最簡單的Android Aidl 使用