小菜毛毛技術(shù)分享

          與大家共同成長(zhǎng)

            BlogJava :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
            164 Posts :: 141 Stories :: 94 Comments :: 0 Trackbacks
          http://blog.chinaunix.net/u3/90876/showart_2200991.html
          在Android中, 每個(gè)應(yīng)用程序都可以有自己的進(jìn)程. 在寫UI應(yīng)用的時(shí)候, 經(jīng)常要用到Service. 在不同的進(jìn)程中, 怎樣傳遞對(duì)象呢? 顯然, Java中不允許跨進(jìn)程內(nèi)存共享. 因此傳遞對(duì)象, 只能把對(duì)象拆分成操作系統(tǒng)能理解的簡(jiǎn)單形式, 以達(dá)到跨界對(duì)象訪問的目的. 在J2EE中,采用RMI的方式, 可以通過序列化傳遞對(duì)象. 在Android中, 則采用AIDL的方式. 理論上AIDL可以傳遞Bundle,實(shí)際上做起來卻比較麻煩. 

          AIDL(AndRoid接口描述語(yǔ)言)是一種借口描述語(yǔ)言; 編譯器可以通過aidl文件生成一段代碼,通過預(yù)先定義的接口達(dá)到兩個(gè)進(jìn)程內(nèi)部通信進(jìn)程的目的. 如果需要在一個(gè)Activity中, 訪問另一個(gè)Service中的某個(gè)對(duì)象, 需要先將對(duì)象轉(zhuǎn)化成AIDL可識(shí)別的參數(shù)(可能是多個(gè)參數(shù)), 然后使用AIDL來傳遞這些參數(shù), 在消息的接收端, 使用這些參數(shù)組裝成自己需要的對(duì)象. 

          AIDL的IPC的機(jī)制和COM或CORBA類似, 是基于接口的,但它是輕量級(jí)的。它使用代理類在客戶端和實(shí)現(xiàn)層間傳遞值. 如果要使用AIDL, 需要完成2件事情: 1. 引入AIDL的相關(guān)類.; 2. 調(diào)用aidl產(chǎn)生的class. 

          具體實(shí)現(xiàn)步驟如下: 

          1、創(chuàng)建AIDL文件, 在這個(gè)文件里面定義接口, 該接口定義了可供客戶端訪問的方法和屬性。 如: ITaskBinder.adil 

          package com.cmcc.demo; 

          import com.cmcc.demo.ITaskCallback; 

          interface ITaskBinder { 

          boolean isTaskRunning(); 
          void stopRunningTask(); 
          void registerCallback(ITaskCallback cb); 
          void unregisterCallback(ITaskCallback cb); }


          其中: ITaskCallback在文件ITaskCallback.aidl中定義: 

          package com.cmcc.demo; 

          interface ITaskCallback { 
          void actionPerformed(int actionId); }


          注意: 理論上, 參數(shù)可以傳遞基本數(shù)據(jù)類型和String, 還有就是Bundle的派生類, 不過在Eclipse中,目前的ADT不支持Bundle做為參數(shù), 據(jù)說用Ant編譯可以, 我沒做嘗試. 

          2、編譯AIDL文件, 用Ant的話, 可能需要手動(dòng), 使用Eclipse plugin的話,可以根據(jù)adil文件自動(dòng)生產(chǎn)java文件并編譯, 不需要人為介入. 

          3、在Java文件中, 實(shí)現(xiàn)AIDL中定義的接口. 編譯器會(huì)根據(jù)AIDL接口, 產(chǎn)生一個(gè)JAVA接口。這個(gè)接口有一個(gè)名為Stub的內(nèi)部抽象類,它繼承擴(kuò)展了接口并實(shí)現(xiàn)了遠(yuǎn)程調(diào)用需要的幾個(gè)方法。接下來就需要自己去實(shí)現(xiàn)自定義的幾個(gè)接口了. 
          ITaskBinder.aidl中接口的實(shí)現(xiàn), 在MyService.java中接口以內(nèi)嵌類的方式實(shí)現(xiàn): 

          private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() { 
          public void stopRunningTask() { //@TODO } 

          public boolean isTaskRunning() { //@TODO return false; } 

          public void registerCallback(ITaskCallback cb) { if (cb != null) mCallbacks.register(cb); } public void unregisterCallback(ITaskCallback cb) { if(cb != null) mCallbacks.unregister(cb); } }; 


          在MyActivity.java中ITaskCallback.aidl接口實(shí)現(xiàn): 

          private ITaskCallback mCallback = new ITaskCallback.Stub() { 
          public void actionPerformed(int id) { //TODO printf("callback id=" + id); } }; 


          4、向客戶端提供接口ITaskBinder, 如果寫的是service,擴(kuò)展該Service并重載onBind ()方法來返回一個(gè)實(shí)現(xiàn)上述接口的類的實(shí)例。這個(gè)地方返回的mBinder,就是上面通過內(nèi)嵌了定義的那個(gè). (MyService.java) 
          public IBinder onBind(Intent t) { printf("service on bind"); return mBinder; } 
          在Activity中, 可以通過Binder定義的接口, 來進(jìn)行遠(yuǎn)程調(diào)用. 

          5、在服務(wù)器端回調(diào)客戶端的函數(shù). 前提是當(dāng)客戶端獲取的IBinder接口的時(shí)候,要去注冊(cè)回調(diào)函數(shù), 只有這樣, 服務(wù)器端才知道該調(diào)用那些函數(shù)在:MyService.java中: 

          void callback(int val) {

          final int N = mCallbacks.beginBroadcast();

          for (int i=0; i<N; i++) {

          try {

          mCallbacks.getBroadcastItem(i).actionPerformed(val);

          } catch (RemoteException e) {

          // The RemoteCallbackList will take care of removing

          // the dead object for us.

          }

          }

          mCallbacks.finishBroadcast();

          }



          AIDL的創(chuàng)建方法: 
          AIDL語(yǔ)法很簡(jiǎn)單,可以用來聲明一個(gè)帶一個(gè)或多個(gè)方法的接口,也可以傳遞參數(shù)和返回值。由于遠(yuǎn)程調(diào)用的需要, 這些參數(shù)和返回值并不是任何類型.下面是些AIDL支持的數(shù)據(jù)類型: 
          1. 不需要import聲明的簡(jiǎn)單Java編程語(yǔ)言類型(int,boolean等) 
          2. String, CharSequence不需要特殊聲明 

          3. List, Map和Parcelables類型, 這些類型內(nèi)所包含的數(shù)據(jù)成員也只能是簡(jiǎn)單數(shù)據(jù)類型, String等其他比支持的類型. 
          ( 
          (另外: 我沒嘗試Parcelables, 在Eclipse+ADT下編譯不過, 或許以后會(huì)有所支持). 
          下面是AIDL語(yǔ)法: 
          // 文件名: SomeClass.aidl // 文件可以有注釋, 跟java的一樣 // 在package以前的注釋, 將會(huì)被忽略. // 函數(shù)和變量以前的注釋, 都會(huì)被加入到生產(chǎn)java代碼中. package com.cmcc.demo; 
          // import 引入語(yǔ)句 import com.cmcc.demo.ITaskCallback; 

          interface ITaskBinder { 
          //函數(shù)跟java一樣, 可以有0到多個(gè)參數(shù) ,可以有一個(gè)返回值 boolean isTaskRunning(); 
          void stopRunningTask(); //參數(shù)可以是另外的一個(gè)aidl定義的接口 void registerCallback(ITaskCallback cb); 
          void unregisterCallback(ITaskCallback cb); 
          //參數(shù)可以是String, 可以用in表入輸入類型, out表示輸出類型. 
          int getCustomerList(in String branch, out String customerList); 
          } 

          實(shí)現(xiàn)接口時(shí)有幾個(gè)原則: 
          .拋出的異常不要返回給調(diào)用者. 跨進(jìn)程拋異常處理是不可取的. 
          .IPC調(diào)用是同步的。如果你知道一個(gè)IPC服務(wù)需要超過幾毫秒的時(shí)間才能完成地話,你應(yīng)該避免在Activity的主線程中調(diào)用。 也就是IPC調(diào)用會(huì)掛起應(yīng)用程序?qū)е陆缑媸ロ憫?yīng). 這種情況應(yīng)該考慮單起一個(gè)線程來處理. 
          .不能在AIDL接口中聲明靜態(tài)屬性。 

          IPC的調(diào)用步驟: 
          1. 聲明一個(gè)接口類型的變量,該接口類型在.aidl文件中定義。 
          2. 實(shí)現(xiàn)ServiceConnection。 
          3. 調(diào)用ApplicationContext.bindService(),并在ServiceConnection實(shí)現(xiàn)中進(jìn)行傳遞. 
          4. 在ServiceConnection.onServiceConnected()實(shí)現(xiàn)中,你會(huì)接收一個(gè)IBinder實(shí)例(被調(diào)用的Service). 調(diào)用 YourInterfaceName.Stub.asInterface((IBinder)service)將參數(shù)轉(zhuǎn)換為YourInterface類型。 
          5. 調(diào)用接口中定義的方法。 你總要檢測(cè)到DeadObjectException異常,該異常在連接斷開時(shí)被拋出。它只會(huì)被遠(yuǎn)程方法拋出。 
          6. 斷開連接,調(diào)用接口實(shí)例中的ApplicationContext.unbindService() 


          整個(gè)源碼程序如下所示: 
          1. ITaskCallback.aidl 

          package com.cmcc.demo; 

          interface ITaskCallback { 
          void actionPerformed(int actionId); } 


          2. ITaskBinder.aidl 

          package com.cmcc.demo; 

          import com.cmcc.demo.ITaskCallback; 

          interface ITaskBinder { 

          boolean isTaskRunning(); 
          void stopRunningTask(); 
          void registerCallback(ITaskCallback cb); 
          void unregisterCallback(ITaskCallback cb); }


          3. MyService.java 

          package com.cmcc.demo; 

          import android.app.Service; 
          import android.content.Intent; 
          import android.os.IBinder; 
          import android.os.RemoteCallbackList; 
          import android.os.RemoteException; 
          import android.util.Log; 

          public class MyService extends Service { 

          @Override public void onCreate() { printf("service create"); } 
          @Override public void onStart(Intent intent, int startId) { printf("service start id=" + startId); callback(startId); } 
          @Override public IBinder onBind(Intent t) { printf("service on bind"); return mBinder; } 
          @Override public void onDestroy() { printf("service on destroy"); super.onDestroy(); } 
          @Override public boolean onUnbind(Intent intent) { printf("service on unbind"); return super.onUnbind(intent); } 
          public void onRebind(Intent intent) { printf("service on rebind"); super.onRebind(intent); } 
          private void printf(String str) { Log.e("TAG", "###################------ " + str + "------"); } 
          void callback(int val) { final int N = mCallbacks.beginBroadcast(); for (int i=0; i<N; i++) { try {mCallbacks.getBroadcastItem(i).actionPerformed(val); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing // the dead object for us. } } mCallbacks.finishBroadcast(); } 

          private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() { public void stopRunningTask() { 
          } 
          public boolean isTaskRunning() { return false; } 
          public void registerCallback(ITaskCallback cb) { if (cb != null) mCallbacks.register(cb); } public void unregisterCallback(ITaskCallback cb) { if(cb != null) mCallbacks.unregister(cb); } }; 
          final RemoteCallbackList
          <itaskcallback>mCallbacks = new RemoteCallbackList
          <itaskcallback>(); } 


          4. MyActivity.java 

          package com.cmcc.demo; 

          import android.app.Activity; 
          import android.content.ComponentName; 
          import android.content.Context; 
          import android.content.Intent; 
          import android.content.ServiceConnection; 
          import android.graphics.Color; 
          import android.os.Bundle; 
          import android.os.IBinder; 
          import android.os.RemoteException; 
          import android.util.Log; 
          import android.view.View; 
          import android.view.ViewGroup; 
          import android.view.View.OnClickListener; 
          import android.widget.AbsoluteLayout; 
          import android.widget.Button; 
          import android.widget.LinearLayout; 
          import android.widget.RelativeLayout; 
          import android.widget.TextView; 

          import java.io.BufferedReader; 
          import java.io.File; 
          import java.io.FileOutputStream; 
          import java.io.FileReader; 
          import java.io.PrintWriter; 

          public class MyActivity extends Activity { 

          private Button btnOk; private Button btnCancel; 
          @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); 
          setContentView(R.layout.test_service); 
          btnOk = (Button)findViewById(R.id.btn_ok); btnCancel = (Button)findViewById(R.id.btn_cancel); 
          btnOk.setText("Start Service"); btnCancel.setTag("Stop Service"); 
          btnOk.setOnClickListener(new OnClickListener() { public void onClick(View v) { onOkClick(); } }); 
          btnCancel.setOnClickListener(new OnClickListener() { public void onClick(View v) { onCancelClick(); } }); } 
          void onOkClick() { Bundle args = new Bundle(); 
          Intent intent = new Intent(this, MyService.class); intent.putExtras(args); 
          //printf("send intent to start"); 

          //startService(intent); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); startService(intent); } 

          void onCancelClick() { Intent intent = new Intent(this, MyService.class); //printf("send intent to stop"); 

          unbindService(mConnection); //stopService(intent); } 

          private void printf(String str) { Log.e("TAG", "###################------ " + str + "------"); } 
          ITaskBinder mService; 
          private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) {mService = ITaskBinder.Stub.asInterface(service); try { mService.registerCallback(mCallback); } catch (RemoteException e) { } 
          } 
          public void onServiceDisconnected(ComponentName className) { mService = null; } }; 
          private ITaskCallback mCallback = new ITaskCallback.Stub() { public void actionPerformed(int id) { printf("callback id=" + id); } }; } 


          5. xml文件(略) 
          posted on 2010-11-19 15:41 小菜毛毛 閱讀(4091) 評(píng)論(0)  編輯  收藏 所屬分類: andriod
          主站蜘蛛池模板: 龙游县| 仁怀市| 龙南县| 潜山县| 中宁县| 温泉县| 敦煌市| 江油市| 永清县| 德格县| 灵武市| 大兴区| 大丰市| 东山县| 建始县| 柳州市| 微山县| 利辛县| 萝北县| 五原县| 武邑县| 改则县| 云梦县| 吴桥县| 隆德县| 柘荣县| 泾川县| 五莲县| 太原市| 宜春市| 遵义县| 宝应县| 永仁县| 桂平市| 静宁县| 交城县| 普宁市| 攀枝花市| 龙江县| 阜新市| 南靖县|