編輯:關於Android編程
為使應用程序之間能夠彼此通信,Android提供了IPC (Inter Process Communication,進程間通信)的一種獨特實現: AIDL (Android Interface Definition Language, Android接口定義語言)。
在Android中,每個應用(Application)執行在它自己的進程中,無法直接調用到其他應用的資源,這也符合“沙箱”的理念。因此,在Android中,當一個應用被執行時,一些操作是被限制的,比如訪問內存,訪問傳感器,等等。這樣做可以最大化地保護系統,免得應用程序“為所欲為”。那我們有時需要在應用間交互,怎麼辦呢?於是,Android需要實現IPC協議。然而,這個協議還是有點復雜,主要因為需要實現數據管理系統(在進程或線程間傳遞數據)。為了暫時減緩這個麻煩,Android為我們實現了自己的IPC,也就是是AIDL 。
AIDL(Android Interface Definition Language)是一種接口定義語言,編譯器通過*.aidl文件的描述信息生成符合通信協議的Java代碼,我們無需自己去寫這段繁雜的代碼,只需要在需要的時候調用即可,通過這種方式我們就可以完成進程間的通信工作。
AIDL是IPC的一個輕量級實現,用了對於Java開發者來說很熟悉的語法。Android也提供了一個工具,可以自動創建Stub(類構架,類骨架)。當我們需要在應用間通信時,我們需要按以下幾步走:
1. 定義一個AIDL接口
2. 為遠程服務(Service)實現對應Stub
3. 將服務“暴露”給客戶程序使用
4. 客戶端調用
接下來,我就演示一個操作AIDL的最基本的流程。
一、創建工程
首先,我們需要建立一個服務端的工程,如圖所以:
在com.scott.aidl這個包中,新建一個普通文件(New->File),取名為IPerson.aidl。在這個文件中定義了一個“問候”的方法:
package com.scott.aidl; interface IPerson { String greet(String someone); }一旦文件被保存,Android的AIDL工具會在gen/com/scott/aidl這個文件夾裡自動生成對應的IPerson.java這個文件。因為是自動生成的,所以無需改動。這個文件裡就包含了Stub,我們接下來要為我們的遠程服務實現這個Stub。格式化後的代碼:
package com.scott.aidl; public interface IPerson extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.scott.aidl.IPerson { private static final java.lang.String DESCRIPTOR = "com.scott.aidl.IPerson"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.scott.aidl.IPerson interface, * generating a proxy if needed. */ public static com.scott.aidl.IPerson asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = (android.os.IInterface) obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.scott.aidl.IPerson))) { return ((com.scott.aidl.IPerson) iin); } return new com.scott.aidl.IPerson.Stub.Proxy(obj); } public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_greet: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); java.lang.String _result = this.greet(_arg0); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.scott.aidl.IPerson { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } public java.lang.String greet(java.lang.String someone) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(someone); mRemote.transact(Stub.TRANSACTION_greet, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_greet = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public java.lang.String greet(java.lang.String someone) throws android.os.RemoteException; }
該文件的大綱視圖如下:
IPerson接口中的抽象內部類Stub繼承android.os.Binder類並實現aidl文件中的IPerson接口,比較重要的方法是asInterface(IBinder)方法,該方法會將IBinder類型的對象轉換成IPerson類型,必要的時候生成一個代理對象返回結果。
二、實現遠程服務
首先我們在com.scott.aidl這個包中新建一個類,取名叫AIDLService .java。
package com.scott.server; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import com.scott.aidl.IPerson; public class AIDLService extends Service { private static final String TAG = "AIDLService"; IPerson.Stub stub = new IPerson.Stub() { @Override public String greet(String someone) throws RemoteException { Log.i(TAG, "greet() called"); return "hello, " + someone; } }; @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind() called"); return stub; } @Override public boolean onUnbind(Intent intent) { Log.i(TAG, "onUnbind() called"); return true; } @Override public void onDestroy() { super.onDestroy(); Log.i(TAG, "onDestroy() called"); } }為了實現我們的服務,我們需要讓這個類中的onBind方法返回一個IBinder類的對象(stub的實例),這樣一來調用方獲取的IPerson.Stub就是我們的這個實例。這個IBinder類的對象就代表了遠程服務的實現。為了實現這個服務,我們要用到自動生成的子類IPerson.Stub。在其中,我們也必須實現我們之前在AIDL文件中定義的greet方法。
當然,要想讓Service生效,我們還需要在AndroidManifest.xml中做一些配置工作,以便其他進程訪問:
至此服務端已經完成。
三、客戶端調用
接下來我們就該完成客戶端的工作了。我已經建好了一個客戶端工程,如圖:
我們只需要把IPerson.aidl文件拷到相應的目錄中即可,編譯器同樣會生成相對應的IPerson.java文件,這一部分和服務端沒什麼區別。這樣一來,服務端和客戶端就在通信協議上達到了統一。我們主要工作在MainActivity中完成。
package com.scott.client; 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.os.RemoteException; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.scott.aidl.IPerson; public class MainActivity extends Activity { private Button bindBtn; private Button greetBtn; private Button unbindBtn; private IPerson person; private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i("ServiceConnection", "onServiceConnected() called"); person = IPerson.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { //This is called when the connection with the service has been unexpectedly disconnected, //that is, its process crashed. Because it is running in our same process, we should never see this happen. Log.i("ServiceConnection", "onServiceDisconnected() called"); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); bindBtn = (Button) findViewById(R.id.bindBtn); bindBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("android.intent.action.AIDLService"); bindService(intent, conn, Context.BIND_AUTO_CREATE); bindBtn.setEnabled(false); greetBtn.setEnabled(true); unbindBtn.setEnabled(true); } }); greetBtn = (Button) findViewById(R.id.greetBtn); greetBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { String retVal = person.greet("scott"); Toast.makeText(MainActivity.this, retVal, Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show(); } } }); unbindBtn = (Button) findViewById(R.id.unbindBtn); unbindBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { unbindService(conn); bindBtn.setEnabled(true); greetBtn.setEnabled(false); unbindBtn.setEnabled(false); } }); } }
到現在我們就剩下最後一個步驟了,這個環節也是最為關鍵的,就是綁定我們需要的服務。我們通過服務端Service定義的“android.intent.action.AIDLService”這個標識符來綁定其服務,這樣客戶端和服務端就實現了通信的連接,我們就可以調用IPerson中的“問候”方法了。
四、結果展示
最後,貼幾張客戶端演示過程圖。
按照順序分別是:初始界面;點擊bindService後界面;點擊greet後界面;點擊unbindService後界面。
操作過程中的日志如下:
五、總結
AIDL支持的數據類型與Java接口支持的數據類型有些不同:
1. 所有基礎類型(int, char, 等)
2. String,List,Map,CharSequence等類
3. 其他AIDL接口類型
4. 所有Parcelable的類
AIDL的最終效果就是讓 IPC的通訊就像調用函數那樣簡單。自動的幫你完成了參數序列化發送以及解析返回數據的那一系列麻煩。而你所需要做的就是寫上一個接口文件,然後利用aidl工具轉化一下得到另一個java文件,這個文件在服務和客戶端程序各放一份。服務程序繼承IxxxxService.Stub 然後將函數接口裡面的邏輯代碼實現一下。
書接上回,我們已經了解了一些關於適配的一些相關概念,接下來我們會了解一下,在設置布局時我們應該注意的地方。盡量不去設定具體的尺寸值。為了確保布局適應各種尺寸的屏幕,在保證
幫忙給朋友手機root ,是電信的定制機,試了很多軟件都沒有成,後來才發基帶版本是FB24,是不能root的,需要刷成EK21才能root,就在網上找刷機的
Android 側滑菜單的實現,參考網上的代碼,實現側滑菜單。最重要的是這個動畫類UgcAnimations,如何使用動畫類來側滑的封裝FlipperLayout。1、實
這是<是時候來了解android7>系列的第三篇文章了, 前面兩篇分別介紹了多窗口模式和shortcut功能, 今天我們來點簡單的, 說一說通知直接回復功能.