編輯:關於Android編程
Android系統中的進程之間不能共享內存,因此,需要提供一些機制在不同進程之間進行數據通信。
在前一篇文章(關於Android中的四大組件(Service的開啟與關閉))中介紹了開發人員如何定制自己的服務,但這些
服務並不能被其它的應用程序訪問,為了使其它的應用程序也可以訪問本應用程序提供的服務,Android系統采用了
遠程過程調用(Remote Procedure Call,RPC)方式來實現。與很多其它的基於RPC的解決方案一樣,Android使用一種
接口定義語言(Interface Definition Language,IDL)來公開服務的接口,因此,可以將這種跨進程訪問的服務稱為
(Android Interface Definition Language)AIDL服務。
建立AIDL服務比普通的服務要復雜一些,具體步驟如下。
(1)在Eclipse Android工程的Java源文件目錄中建立一個擴展名為aidl的文件,該文件的語法類似於Java代
碼,但會稍不同。
(2)如果aidl文件的內容是正確的,ADT會自動生成一個Java接口文件(*.java)。
(3)建立一個服務類(Service的子類)。
(4)實現由aidl文件生成的Java接口。
(5)在AndroidManifest.xml文件中配置AIDL服務,注意的是標簽中android:name的屬性值就是客戶端
要引用該服務的ID,也就是Intent類構造方法的參數值。
以下程序是服務端創建了一個簡單的AIDL服務,這個服務有兩個get方法,分別獲取姓名和年齡,創建此AIDL服務的步驟
如下。
(1)建立一個aidl文件,如下圖中的IPerson.aidl文件。
IPerson.aidl文件的內容如下:
package com.aidl; interface IPerson{ String getName(); int getAge(); }
IPerson.aidl文件的內容與Java代碼非常相似,但要注意,不能加修飾符,比如:public、private,同時AIDL服
務不支持的數據類型例如:InputStream、OutputStream等內容。
(2)如果IPerson.adil文件中內容正確,ADT會自動生成一個IPerson.java文件。
(3)編寫一個MyService類,該類繼承自Service,在MyService類中定義了一個內聯類PersonImpl,該類繼承自
IPerson.Stub,MyService類的代碼如下:
public class MyService extends Service{ public class PersonImpl extends IPerson.Stub{ @Override public String getName() throws RemoteException { return bill; } @Override public int getAge() throws RemoteException { return 25; } } @Override public IBinder onBind(Intent intent) { return new PersonImpl(); } }
(4)在AndroidManifest.xml文件中配置MyService類,代碼如下:
至此服務端的AIDL服務編寫完成,接下來進行客戶端代碼的編寫,新建工程,將服務端的自動生成的
IPerson.java文件連同包目錄一起復制到客戶端工程中,如下圖:
以下程序實現了綁定AIDL服務,以及獲取服務端數據:
public class MainActivity extends Activity implements OnClickListener{ private Button btn_aidl; private Button btn_get; private TextView tv_show; private Intent mIpItent; private IPerson mIpIPerson=null; private ServiceConnection conn=new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { mIpIPerson=IPerson.Stub.asInterface(service); btn_get.setEnabled(true); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); initView(); initEvent(); } private void initData(){ mIpItent=new Intent(com.aidl.IPerson); } private void initView(){ btn_aidl=(Button) this.findViewById(R.id.btn_aidl); btn_get=(Button) this.findViewById(R.id.btn_get); tv_show=(TextView) this.findViewById(R.id.tv_show); btn_get.setEnabled(false); } private void initEvent(){ btn_aidl.setOnClickListener(this); btn_get.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_aidl://綁定AIDL服務 bindService(mIpItent, conn, Context.BIND_AUTO_CREATE); break; case R.id.btn_get://獲取服務端數據 try { tv_show.setText(姓名:+mIpIPerson.getName()+ 年齡:+mIpIPerson.getAge()); } catch (RemoteException e) { e.printStackTrace(); } default: break; } } }
以上代碼使用bindService方法來綁定AIDL服務,其中需要指定AIDL服務的ID,也就是標簽中的
android:name屬性值。在綁定時需要一個ServiceConnection對象,當綁定成功,系統調用
ServiceConnection.onServiceConnected方法,通過此方法的service參數獲取AIDL服務對象。最後先運行服務端程
序,再運行客戶端程序。
效果如下:
AIDL服務只支持有限的數據類型,因此,如果用AIDL服務傳遞一些復雜的數據需要做更一步處理,AIDL服務支
持的數據類型如下。
(1)java的簡單類型(int、char、boolean等),不需要導入。
(2)String和CharSequence,不需要導入。
(3)List和Map,List和Map對象的元素類型必須是AIDL服務支持的數據類型,不需要導入。
(4)AIDL自動生成的接口,需要導入。
(5)實現android.os.Parcelable接口的類,需要導入。
以下程序傳遞的數據類型是Person類,服務端代碼如下:
(1)新建Person實現Parcelable接口
public class Person implements Parcelable{ private String name; private int age; public static final Parcelable.CreatorCREATOR=new Creator () { @Override public Person[] newArray(int size) { return new Person[size]; } @Override public Person createFromParcel(Parcel source) { return new Person(source.readInt(),source.readString()); } }; public Person(){ } public Person(int age,String name){ this.name=name; this.age=age; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(age); dest.writeString(name); } 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; } }
Person實現android.os.Parcelable這個接口,該接口用於序列化對象,在Person類中必須有一個靜態常量,常量
名必須是CREATOR,而且CREATOR常量的數據類型必須是Parcelable.Creator。在writeToParcel方法中需要將序列
化的值寫入Parcel對象,注意,讀取的順序必須和寫入的順序保持一致。
(2)接著建立一個IPerson.aidl文件,代碼如下:
package com.aidl; import com.aidl.Person; interface IPerson{ Person getPerson(); }
(3)建立一個Person.aidl文件,代碼如下:
package com.aidl; parcelable Person;
(4)創建一個MyService類,代碼如下:
public class MyService extends Service { public class PersonImpl extends IPerson.Stub { @Override public Person getPerson() throws RemoteException { Person person = new Person(); person.setName(bill); person.setAge(25); return person; } } @Override public IBinder onBind(Intent intent) { return new PersonImpl(); } }
最後服務端的工程目錄結構如下圖:
OK,服務端的AIDL服務編寫完畢,接著編寫客戶端程序,將服務端的Person.java與IPerson.aidl連同包一起復
制到客戶端工程中,如下圖:
以下代碼實現了獲取服務端的Person對象中的數據:
public class MainActivity extends Activity implements OnClickListener{ private Button btn_aidl; private Button btn_get; private TextView tv_show; private Intent mIpItent; private IPerson mIpIPerson=null; private ServiceConnection conn=new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { mIpIPerson=IPerson.Stub.asInterface(service); btn_get.setEnabled(true); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); initView(); initEvent(); } private void initData(){ mIpItent=new Intent(com.bill.aidl.IPerson); } private void initView(){ btn_aidl=(Button) this.findViewById(R.id.btn_aidl); btn_get=(Button) this.findViewById(R.id.btn_get); tv_show=(TextView) this.findViewById(R.id.tv_show); btn_get.setEnabled(false); } private void initEvent(){ btn_aidl.setOnClickListener(this); btn_get.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_aidl://綁定AIDL服務 bindService(mIpItent, conn, Context.BIND_AUTO_CREATE); break; case R.id.btn_get://獲取服務端數據 try { Person person=mIpIPerson.getPerson(); tv_show.setText(姓名:+person.getName()+ 年齡:+person.getAge()); } catch (RemoteException e) { e.printStackTrace(); } default: break; } } }
先運行服務端程序,在運行客戶端程序,運行效果如下:
Android3.0提供了drag/drop框架,利用此框架可以實現使用拖放手勢將一個view拖放到當前布局中的另外一個view中。本文將介紹如何使用拖放框架。 一、實
shape和selector是Android UI設計中經常用到的,比如我們要自定義一個圓角Button,點擊Button有些效果的變化,就要用到shape和select
工廠模式是一種創建者模式,在任何生成復雜對象的地方都可以使用工廠模式。理論來說在任何使用A a = new A()的方式都可以使用工廠模式,雖然使用工廠模式可能需要多做一
最近有一段時間沒寫博客了,一方面是工作比較忙,一方面也著實本人水平有限,沒有太多能與大家分享的東西,也就是在最近公司要做一個搶紅包的功能,老板發話了咋們就開干呗,本人就開