在Android中, 每個應用程式都可以有自己的進程. 在寫UI應用的時候, 經常要用到Service.
在不同的進程中, 怎樣傳遞物件呢?  顯然, Java中不允許跨進程記憶體共用. 因此傳遞物件, 只能把物件拆分成作業系統能理解的簡單形式,以達到跨界物件訪問的目的. 
在J2EE中,採用RMI的方式, 可以通過序列化傳遞物件. 在Android中, 則採用AIDL的方式.


AIDL(Android Interface Definition Language)是一種接口描述語言; 編譯器可以通過aidl檔生成一段代碼,通過預先定義的介面達到兩個進程內部通信進程的目的. 
如果需要在一個Activity中, 訪問另一個Service中的某個物件, 需要先將物件轉化成AIDL可識別的參數(可能是多個參數), 然後使用AIDL來傳遞這些參數, 在消息的接收端, 使用這些參數組裝成自己需要的物件.


AIDL的跨進程(IPC)的遠距溝通機制和COM或CORBA類似, 是基於介面的,但它是輕量級的。
它使用代理類在用戶端和實現層間傳遞值.
如果要使用AIDL, 需要完成2件事情:
1. 引入AIDL的相關類.
2. 調用aidl產生的class.


具體實現步驟如下:
1、創建AIDL檔, 在這個檔裡面定義介面, 該介面定義了可供用戶端訪問的方法和屬性。 如: 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);
}


注意: 理論上, 參數可以傳遞基底資料型別和String, 還有就是Bundle的派生類, 不過在Eclipse中,目前的ADT不支援Bundle做為參數.
2、編譯AIDL檔,使用Eclipse plugin的話,可以根據adil檔自動生產java檔並編譯.
3、在Java檔中, 實現AIDL中定義的介面. 編譯器會根據AIDL介面, 產生一個JAVA介面。
這個介面有一個名為Stub的內部抽象類別,它繼承擴展了介面並實現了遠端調用需要的幾個方法。接下來就需要自己去實現自訂的幾個介面了.
ITaskBinder.aidl中介面的實現, 在MyService.java中介面以內嵌類的方式實現:
private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() {
//需在此實作所有在ITaskbinder(ITaskbinder.aidl)介面所定義的方法
        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介面實現:
private ITaskCallback mCallback = new ITaskCallback.Stub() {
//需在此實作所有在ITaskCallback(ITaskCallback.aidl)介面所定義的方法,主要是讓MyService可以呼叫MyActivity
        public void actionPerformed(int id) {
           //TODO
            printf("callback id=" + id);
        }
};
4、向用戶端提供介面ITaskBinder, 如果寫的是service,擴展該Service並重載onBind ()方法來返回一個實現上述介面的類的實例。
這個地方返回的mBinder,就是上面通過內嵌了定義的那個. (MyService.java)
    public IBinder onBind(Intent t) {
        printf("service on bind");
        return mBinder;
}
在Activity中, 可以通過Binder定義的介面, 來進行遠端調用.
5、在伺服器端呼叫用戶端的函數. 前提是當用戶端獲取的IBinder介面的時候,要去註冊回呼函數, 只有這樣, 伺服器端才知道該調用那些函數在: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的創建方法:
AIDL語法很簡單,可以用來聲明一個帶一個或多個方法的介面,也可以傳遞參數和返回值。 由於遠端調用的需要, 這些參數和返回值並不是任何類型.
下面是些AIDL支持的資料類型:
1. 不需要import聲明的簡單Java程式設計語言類型(int,boolean等)
2. String, CharSequence不需要特殊聲明 
3. List, Map和Parcelables類型, 這些類型內所包含的資料成員也只能是單一資料型別, String等其他比支持的類型. 


下面是AIDL語法:
 // 檔案名: SomeClass.aidl
 // 檔可以有注釋, 跟java的一樣
 // 在package以前的注釋, 將會被忽略.
 // 函數和變數以前的注釋, 都會被加入到生產java代碼中.
package com.cmcc.demo;
 // import 引入語句
import com.cmcc.demo.ITaskCallback;
 
interface ITaskBinder {
    //函數跟java一樣, 可以有0到多個參數 ,可以有一個返回值
    boolean isTaskRunning();
       
    void stopRunningTask();   
    //參數可以是另外的一個aidl定義的介面
    void registerCallback(ITaskCallback cb);   
  
void unregisterCallback(ITaskCallback cb);
//參數可以是String, 可以用in表入輸入類型, out表示輸出類型.
int getCustomerList(in String branch, out String[] customerList);
 



實現介面時有幾個原則:
.拋出的異常不要返回給調用者. 跨進程拋異常處理是不可取的.
.IPC調用是同步的。如果你知道一個IPC服務需要超過幾毫秒的時間才能完成的話,你應該避免在Activity的主執行緒中調用。
也就是IPC調用會引起應用程式導致介面失去回應. 這種情況應該考慮建立一個執行緒來處理.
.不能在AIDL介面中聲明靜態屬性。


IPC的調用步驟:
 1. 聲明一個介面類別型的變數,該介面類別型在.aidl檔中定義。
 2. 實現ServiceConnection。
 3. 調用ApplicationContext.bindService(),並在ServiceConnection實現中進行傳遞. 
 4. 在ServiceConnection.onServiceConnected()實現中,你會接收一個IBinder實例(被調用的Service). 調用
    YourInterfaceName.Stub.asInterface((IBinder)service)將參數轉換為YourInterface類型。
 5. 調用介面中定義的方法。 你總要檢測到DeadObjectException異常,該異常在連接斷開時被拋出。它只會被遠程方法拋出。
 6. 斷開連接,調用介面實例中的ApplicationContext.unbindService()
 
下面是整個程式:
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);
        }
    };
}


狼翔月影 發表在 痞客邦 PIXNET 留言(0) 人氣()