1. 程式人生 > >Android 自定義AIDL的實現與通訊原理

Android 自定義AIDL的實現與通訊原理

以前使用到AIDL的時候感覺操作是蠻簡單的,原理好像一點看不懂,後來才發現,原來原理這麼複雜,怪不得光看程式碼看不懂。。。。。。

沒辦法,UML圖畫的太醜。。

一、先來講講Android程序之間的通訊

差不多就是這樣的一個圖,程序間通訊都得通過一個單一IBinder介面,Android框架在Client端放了一個BinderProxy,在服務端放了一個Binder,Binder實現了IBinder介面,而BinderProxy又是Binder的子類。其中Binder實現類中有4個比較重要的方法提下:

1.init():在類初始化的時候呼叫,本地方法,用來與底層的c/c++溝通,傳遞物件的地址(指標)

2.transact():java層使用的程式碼,主要內容是呼叫onTransact()

3.execTransact():c/c++使用的程式碼,也是呼叫onTransact()

4.onTransact():這個方法可以由Binder的子類來重寫,在用到的時候會自動回撥。


在Android中每個程序都是在不同的dalvikvm上執行的,所以不同的程序間是無法靠java程式碼通訊的。這就要用到底層的c程式碼,Linux驅動,這裡只要先記住MyActivity端的ServiceConnection 類中方法的IBinder物件其實是一個BinderProxy物件(可以用service.getClass().getName()打印出來看看),在服務端也有個自定義的MyBinder物件(準確的說是Service中onBInd()方法中返回的那個物件),當呼叫到BinderProxy的transact方法時,通過底層的程式碼,最終會呼叫到MyBinder中的onTransact方法。

二、AIDL是幹嘛的呢?

AIDL其實就是為了方便,將上面的東西給封裝起來了,AP開發者不用知道有BinderProxy這個東西,也不用知道transact這個方法,只要呼叫這邊介面的方法,那邊實現類的方法就會執行了。


還記得AIDL的Stub中有個anInterface方法,這個方法就是將IBinder物件放入到了他的Proxy中,為什麼呢,因為他的Proxy不管做什麼事,都要用到transact方法,而這個方法當然得有傳回來的這個Ibinder物件來執行,所以結構就應該是Proxy中有個IBinder屬性。

再來看看上圖。IFakerAIDL用來模擬aidl檔案,都是一個介面,圖中的FakeBinder與FakeProxy實現了這個介面,其中FakeBinder是一個抽象類,具體的方法由使用者自定義。當AP開發者呼叫了FakeProxy中的doSomeThing的時候,框架就自動去呼叫了IBinder中的transact方法(2,3),然後又會呼叫到onTransact方法(4),還記得之前說過,先記住框架的底層會幫助我們,最終呼叫到程序另外一端的onTransact方法。然後在FakeBinder類中,onTransact方法被呼叫,這個方法自然也去呼叫doSomeThind()方法(6),這個方法是個抽象方法,這樣就會再去呼叫Service中返回的那個Ibinder物件的自定義方法。

上面的原理,加上AIDL檔案的程式碼自動生成,就形成了AIDL。

三、貼上程式碼,模擬AIDL

1.清單檔案:

首先,由於用到服務,在清單檔案配上:

 <service
            android:name="com.aii.imitateaidl.MyService"
            android:process=":remote" >
 </service>
其中process屬性指定這個服務是獨立於這個程序的,也就是說這個APP會有2個程序。後面的引數是程序名

2.佈局檔案:

2個按鈕,一個繫結,一個傳送,一個文字輸入框,接收要傳遞的資料。

<LinearLayout 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:orientation="vertical" >

    <Button
        android:id="@+id/button_bind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="bindService" />

    <EditText
        android:id="@+id/textView_content"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入要傳遞的資訊" />

    <Button
        android:id="@+id/button_send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="sendMessage" />

</LinearLayout>

3.java程式碼部分:

主要有5個類,在AIDL中用的是內部類我這裡拆到外面來了。

MainActivity:

MyService:自定義服務(模擬遠端)

IFakeAIDL:介面類(模擬AIDL檔案)

FakeStub:遠端的BInder子類

FakeProxy:Client代理類

MainActivity:

package com.aii.imitateaidl;

import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.ActionBarActivity;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity implements OnClickListener {
	private EditText textView_content;
	private Button button_bind, button_send;
	private BroadcastReceiver receiver;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		textView_content = (EditText) findViewById(R.id.textView_content);
		button_bind = (Button) findViewById(R.id.button_bind);
		button_send = (Button) findViewById(R.id.button_send);

		button_bind.setOnClickListener(this);
		button_send.setOnClickListener(this);

		receiver = new BroadcastReceiver() {

			@Override
			public void onReceive(Context context, Intent intent) {

				String content = intent.getStringExtra("content");
				if (content != null) {
					Toast.makeText(MainActivity.this, content,
							Toast.LENGTH_SHORT).show();
				}

			}
		};
		IntentFilter filter = new IntentFilter("com.aii.broadcast");
		registerReceiver(receiver, filter);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.button_bind:
			// 繫結服務
			Intent intent = new Intent(this, MyService.class);
			bindService(intent, conn, Context.BIND_AUTO_CREATE);
			break;
		case R.id.button_send:
			// 先判斷文字框內容
			String content = textView_content.getText().toString().trim();
			if (TextUtils.isEmpty(content)) {
				Toast.makeText(this, "請輸入要傳遞的內容", Toast.LENGTH_SHORT).show();
				return;
			}
			if (binder != null) {
				try {
					// 呼叫介面方法
					binder.doLog(content);
				} catch (RemoteException e) {
					e.printStackTrace();
				}
			}
			break;
		}

	}

	private IFakeAIDL binder;
	private ServiceConnection conn = new ServiceConnection() {

		@Override
		public void onServiceDisconnected(ComponentName name) {
		}

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// 將service裝入自定義的FakeProxy中
			binder = FakeStub.asInterface(service);
		}
	};

	@Override
	protected void onDestroy() {
		unbindService(conn);
		unregisterReceiver(receiver);
		super.onDestroy();
	}
}

MyService:
package com.aii.imitateaidl;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;

public class MyService extends Service {

	public static final int MESSAGE_LOG = 1;
	public static Handler handler;

	@Override
	public IBinder onBind(Intent intent) {
		return new MyBinder();
	}
	//列印Toast資訊,遠端發生廣播給主執行緒,如果不需要Toast,可無視此部分
	@Override
	public void onCreate() {
		super.onCreate();
		new Thread(new Runnable() {

			@Override
			public void run() {
				Looper.prepare();

				handler = new Handler() {
					@Override
					public void handleMessage(Message msg) {
						switch (msg.what) {
						case MESSAGE_LOG:
							Intent intent = new Intent("com.aii.broadcast");
							intent.putExtra("content", (String) msg.obj);
							sendBroadcast(intent);
							break;

						default:
							break;
						}
					}
				};

				Looper.loop();
			}
		}).start();
	}

	class MyBinder extends FakeStub {

		@Override
		public void doLog(String content) throws RemoteException {
			Log.i("MainActivity", content);
			
			//列印Toast資訊,遠端發生廣播給主執行緒,如果不需要Toast,可無視此部分
			Message msg = Message.obtain();
			msg.what = MESSAGE_LOG;
			msg.obj = content;
			handler.sendMessage(msg);
		}

	}

}
IFakeAIDL:
package com.aii.imitateaidl;

import android.os.RemoteException;

interface IFakeAIDL {
	
	int DO_LOG=0;
	void doLog(String content) throws RemoteException;
}


FakeStub:
package com.aii.imitateaidl;

import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;

abstract class FakeStub extends Binder implements IFakeAIDL {

	public static IFakeAIDL asInterface(IBinder binder) {
		//簡化版。。。
		if (binder != null) {
			return new FakeProxy(binder);
		}
		return null;
	}

	@Override
	protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
			throws RemoteException {
		switch (code) {		
		case IFakeAIDL.DO_LOG:
			//code與Proxy中的對應,對應的code傳遞不同的資訊,可執行不同的方法
			doLog(data.readString());
			return true;

		default:
			break;
		}

		return super.onTransact(code, data, reply, flags);
	}

}

FakeProxy:

package com.aii.imitateaidl;

import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;

public class FakeProxy extends Binder implements IFakeAIDL {
	private IBinder binder;

	public FakeProxy(IBinder binder) {
		this.binder = binder;
	}

	@Override
	public void doLog(String content) throws RemoteException {
		Parcel data = Parcel.obtain();
		Parcel reply = Parcel.obtain();
		data.writeString(content);
		try {
			//表面上是doLog方法,其實是封裝了transact方法來實現
			binder.transact(IFakeAIDL.DO_LOG, data, reply, 0);
		} finally {
			data.recycle();
			reply.recycle();
		}
	}
	


}
程式碼地址:http://download.csdn.net/detail/q291611265/8607549