天天看點

Android跨程序通路(AIDL服務)

我将AndroidAIDL的學習知識總結一下和大家共享

在Android開發中,AIDL主要是用來跨程序通路。

Android系統中的程序之間不能共享記憶體,是以,需要提供一些機制在不同程序之間進行資料通信,一般是和Service服務元件一起使用來實作。

1、建立調用AIDL服務

建立AIDL服務的步驟:

第一步:在Eclipse的Android工程的Java源檔案目錄中建立一個擴充名為aidl的檔案,改檔案的文法類似于Java代碼,但稍有不同。

第二步:如果aidl檔案的内容是正确的,ADT會在gen目錄下自動生成一個Java接口檔案。

第三步:建立一個服務類(Service的子類)。

第四步:實作有aidl檔案生成的Java接口。

第五步:在AndroidManifest.xml檔案中配置AIDL服務,尤其要注意的是,<action>标簽中android:name的屬性值就是用戶端要引用該服務的ID,也就是Intent類構造方法的參數值。

下面實作了一個執行個體:

IMyService.aidl:(其實和Java類定義接口檔案形式上有些類似)

package mobile.android.aidl;
interface IMyService
{
    String getValue();
}
           

實作的MyService.java類:

package mobile.android.aidl;
import mobile.android.aidl.IMyService;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class MyService extends Service
{ 
	public class MyServiceImpl extends IMyService.Stub
	{
		@Override
		public String getValue() throws RemoteException
		{
			// TODO Auto-generated method stub
			return "《Android深度探索(卷1):HAL與驅動開發》";
		}	
	}

	@Override
	public IBinder onBind(Intent intent)
	{		
		return new MyServiceImpl();
	}	
}
           

在AndroidManifest.xml的配置:

<service android:name=".MyService" >
<span style="white-space:pre">	</span><intent-filter> 
		<action android:name="mobile.android.aidl.IMyService" />   <!--通路的ID-->
	</intent-filter>
</service>
           

用戶端代碼:

package mobile.android.aidlclient;
import mobile.android.aidl.IMyService;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class Main extends Activity implements OnClickListener
{
	private IMyService myService = null;
	private Button btnInvokeAIDLService;
	private Button btnBindAIDLService;
	private TextView textView;
	private ServiceConnection serviceConnection = new ServiceConnection()
	{
		@Override
		public void onServiceConnected(ComponentName name, IBinder service)
		{
			myService = IMyService.Stub.asInterface(service);  //擷取服務對象
			btnInvokeAIDLService.setEnabled(true);
		}

		@Override
		public void onServiceDisconnected(ComponentName name)
		{
			Toast.makeText(Main.this, "無法連接配接", Toast.LENGTH_SHORT).show();
			btnInvokeAIDLService.setEnabled(true);
		}
	};

	@Override
	public void onClick(View view)
	{
		switch (view.getId())
		{
			case R.id.btnBindAIDLService:
           
<span style="white-space:pre">				</span>//綁定AIDL服務
				if(!bindService(new Intent("mobile.android.aidl.IMyService"),
						serviceConnection, Context.BIND_AUTO_CREATE))
					Toast.makeText(Main.this, "無法連接配接", Toast.LENGTH_SHORT).show();
				break;

			case R.id.btnInvokeAIDLService:
				try
				{
					textView.setText(myService.getValue());
				}
				catch (Exception e)
				{
					textView.setText(e.getMessage());
				}
				break;
		}

	}  

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		btnInvokeAIDLService = (Button) findViewById(R.id.btnInvokeAIDLService);
		btnBindAIDLService = (Button) findViewById(R.id.btnBindAIDLService);
		btnInvokeAIDLService.setEnabled(false);
		textView = (TextView) findViewById(R.id.textview);
		btnInvokeAIDLService.setOnClickListener(this);
		btnBindAIDLService.setOnClickListener(this);
	}
}
           

注意幾點:

1、使用bindService方法綁定AIDL服務。其中需要使用Intent對象指定AIDL服務的ID,也就是聲明服務時<action>标簽中android:name的值。

2、在綁定時需要一個ServiceConnection對象。建立ServiceConnection對象的過程中如果綁定成功,系統會調用ServiceConnection.OnServiceConnected方法,

通過該方法的service參數值可獲得AIDL服務對象。

2、傳遞複雜的資料的AIDL服務(自定義類型的資料)

首先,了解一下AIDL服務隻支援有限的資料類型,是以,如果用AIDL服務傳遞一些複雜的資料就需要做更一步處理。

AIDL的支援資料類型如下:

1、Java的簡單類型(int 、char、boolean),不需要Import導入。

2、String和CharSequence。不需要Import導入。

3、List和map。但是要注意,List和Map對象元素類型必須是AIDL服務支援的資料類型。不需要Import導入。

4、AIDL自動生成的接口。需要import導入。l

5、實作android.os.Parcelable接口的類。需要import導入。

在傳遞不需要Import導入的和上面說的一樣,傳遞需要import導入的(實作android.os.Parcelable接口的類)。除了建立一個實作android.os.Parcelable的類

還要為這個類單獨建立一個aidl檔案,并使用Parcelable關鍵字進行定義。

看這個執行個體:

在AndroidManifest.xml的注冊配置:

<service android:name=".MyService" >
	<intent-filter> 
		<action android:name="mobile.android.complex.type.aidl.IMyService" />
	</intent-filter>
</service>
           

IMyService.aidl:

package mobile.android.complex.type.aidl;
import mobile.android.complex.type.aidl.Product;
interface IMyService  
{  
    Map getMap(in String country, in Product product);
    Product getProduct();     
}          
           

Product.java類:

package mobile.android.complex.type.aidl;
import android.os.Parcel;
import android.os.Parcelable;

public class Product implements Parcelable
{
	private int id;
	private String name;
	private float price;
	public static final Parcelable.Creator<Product> CREATOR = new Parcelable.Creator<Product>()
	{
		public Product createFromParcel(Parcel in)
		{
			return new Product(in);
		}

		public Product[] newArray(int size)
		{
			return new Product[size]; 
		}
	};
    public Product()
    { 	
    }
	private Product(Parcel in)
	{
		readFromParcel(in);
	}

	@Override
	public int describeContents()
	{
		// TODO Auto-generated method stub
		return 0;
	}

	public void readFromParcel(Parcel in)
	{
		id = in.readInt();
		name = in.readString();
		price = in.readFloat();
	}

	@Override
	public void writeToParcel(Parcel dest, int flags)
	{
		dest.writeInt(id);
		dest.writeString(name);
		dest.writeFloat(price);
	}

	public int getId()
	{
		return id;
	}

	public void setId(int id)
	{
		this.id = id;
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public float getPrice()
	{
		return price;
	}

	public void setPrice(float price)
	{
		this.price = price;
	}
}
           

Product.aidl:(主要是在aidl中聲明上面那個自定義的類型,才能在IMyService中使用)

parcelable Product; 
           

MyService.java類:

package mobile.android.complex.type.aidl;
import java.util.HashMap;
import java.util.Map;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
 
public class MyService extends Service
{ 
	public class MyServiceImpl extends IMyService.Stub
	{

		@Override
		public Product getProduct() throws RemoteException
		{
			Product product = new Product();
			product.setId(1234);
			product.setName("汽車");
			product.setPrice(31000); 
			return product;
		}

		@Override
		public Map getMap(String country, Product product)
				throws RemoteException
		{
			Map map = new HashMap<String, String>();
			map.put("country", country);
			map.put("id", product.getId());
			map.put("name", product.getName());
			map.put("price", product.getPrice());
			map.put("product", product);
			return map;
		}
	}

	@Override
	public IBinder onBind(Intent intent)
	{		
		return new MyServiceImpl();
	}
}
           

在用戶端實作AIDL服務:

package mobile.android.complex.type.aidlclient;
import mobile.android.complex.type.aidl.IMyService;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class Main extends Activity implements OnClickListener
{
	private IMyService myService = null;
	private Button btnInvokeAIDLService;
	private Button btnBindAIDLService;
	private TextView textView;
	private ServiceConnection serviceConnection = new ServiceConnection()
	{ 

		@Override
		public void onServiceConnected(ComponentName name, IBinder service)
		{
			myService = IMyService.Stub.asInterface(service);
			btnInvokeAIDLService.setEnabled(true);
		}

		@Override
		public void onServiceDisconnected(ComponentName name)
		{
			// TODO Auto-generated method stub
		}
	};

	@Override
	public void onClick(View view)
	{
		switch (view.getId())
		{
			case R.id.btnBindAIDLService:
				bindService(new Intent("mobile.android.complex.type.aidl.IMyService"),
						serviceConnection, Context.BIND_AUTO_CREATE);
				break;

			case R.id.btnInvokeAIDLService:
				try
				{
					String s = "";
					s = "Product.id = " + myService.getProduct().getId() + "\n";
					s += "Product.name = " + myService.getProduct().getName()
							+ "\n";
					s += "Product.price = " + myService.getProduct().getPrice()
							+ "\n";
					
					s += myService.getMap("China", myService.getProduct()).toString();
					textView.setText(s);
				}
				catch (Exception e)
				{
				}
				break;
		}
	}

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		btnInvokeAIDLService = (Button) findViewById(R.id.btnInvokeAIDLService);
		btnBindAIDLService = (Button) findViewById(R.id.btnBindAIDLService);
		btnInvokeAIDLService.setEnabled(false);
		textView = (TextView) findViewById(R.id.textview);
		btnInvokeAIDLService.setOnClickListener(this);
		btnBindAIDLService.setOnClickListener(this);
	}
}
           

這邊在實作一個擴充的:AIDL與來去電自動挂斷

雖然可以同Activity Action來撥打電話,但是使用正常的方法無法挂斷電話。不過使用Java反射技術是實作一種通過程式挂斷電話的方法。

在Android SDK源生提供了如下接口:

com,android.internal,telephony.ITelephony,外部無法直接通路,這個接口提供了一個ITelephony.endCall的方法:

盡管不能直接通路ITelephony對象。不過在TelephonyManager類提供一個getITelepgony的方法可以傳回一個ITelephony的對象。不過這個方法是私有的,但

我們可以通過Java的反射技術來調用該方法。在利用getITelephony獲得ITelephony對象之前,先将Android SDK 目錄下找到NeighboringCellInfo.aidl和ITelephony.aidl檔案

複制到你的工程目錄下。

ADT會根據ITelephony.aidl檔案自動生成ITelephony.java檔案。

下面是一個廣播接收來電是先自動挂斷指定電話的來電:

package mobile.android.call.aidl;
import java.lang.reflect.Method;
import com.android.internal.telephony.ITelephony;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.widget.Toast;

public class InCallReceiver extends BroadcastReceiver
{
	@Override
	public void onReceive(Context context, Intent intent)
	{
		TelephonyManager tm = (TelephonyManager) context
				.getSystemService(Service.TELEPHONY_SERVICE);
		switch (tm.getCallState())
		{
			case TelephonyManager.CALL_STATE_RINGING: // 響鈴
				// 獲得來電的電話号
				String incomingNumber = intent
						.getStringExtra("incoming_number");
				if ("12345678".equals(incomingNumber))
				{
					try
					{		
						TelephonyManager telephonyManager = (TelephonyManager) context
								.getSystemService(Service.TELEPHONY_SERVICE);
						Class<TelephonyManager> telephonyManagerClass = TelephonyManager.class;
						Method telephonyMethod = telephonyManagerClass
								.getDeclaredMethod("getITelephony",
										(Class[]) null);
						telephonyMethod.setAccessible(true);
						ITelephony telephony = (com.android.internal.telephony.ITelephony) telephonyMethod
								.invoke(telephonyManager, (Object[]) null);
						telephony.endCall();
					}
					catch (Exception e)
					{
						Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
					}
				}
				break;
		}
	}
}
           

 以上是對AIDL服務的了解總結,希望對大家有用,歡迎共同學習和指導。

繼續閱讀