Android實戰技術:IPC方式簡介教程

來源:互聯網
上載者:User
非即時,通知性的方式

第一種方式就是Intent,Intent可以非常方便的通訊,但是它是非即時的,無法進行即時的像函數調用那樣的即時的通訊。


即時的函數調用

但是IPC的根本目的還是為了實現函數的調用,即使是傳遞資料也是要通過函數調用的方式,為什麼呢?因為程式運行總是要知道狀態,要有邏輯上的行為,因此必須通訊函數才能體現出行為。

IPC的機制除了進程,或者說不同的應用程式之間進行通訊,同時也能夠讓不同的組件之間進行像普通對象那樣進行即時的調用。因為Android的組件都是由系統架構統一的進行構建和銷毀,所以你就無法建立對象,因此,就沒有辦法像普通的對象那樣進行組合或者彙總,從而也就沒有辦法進行直接調用。但是IPC的方式就可以讓Activity/Service擷取另外一個Service對象的引用,從而直接調用其上的方法。

還有,就是這些IPC方法總是產生客戶/服務端模式的調用,也即是用戶端組件(Activity/Service)持有服務端Service的組件,只能是用戶端主動調用服務端的方法,服務端無法反過來調用用戶端的方法,因為IPC的另一端Service無法擷取用戶端的對象。

文檔對此的描述。

有三種方式可以進行IPC通訊:

1. 直接使用Binder對象

缺點是這種方式不能進行跨進程,跨應用程式的函數調用。只能實現在同一個進程之中,同一個應用程式之中的不同的組件之間通訊。

優點就是這種方式使用起來特別簡單,對於公開出來的方法沒有任何的限制,可以傳遞任何的對象,甚至是回調等等。

總體上來講如果不需要跨進程,這是最好的實現方式,可以完全實現像普通對象之間的組合和彙總。但是這裡,最好不要像Android文檔中的樣本那樣,直接返回Service對象,因為Service對象會有很多Public的方法,如果直接返回Service對象會導致公開很多不必須的方法,一旦Client端調用這些方法,將導致奇怪的現象和Bug,一個方法就是用Proxy對Service對象進行封裝,只公開需要的介面。

樣本:

service:

public class BinderPrinterService extends Service {    private static final String TAG = "PlayerService";private IBinder mBinder;    @Override    public void onCreate() {    mBinder = new ProxyService(this);    }        @Override    public IBinder onBind(Intent intent) {        return mBinder;    }        public void print(String msg, TextView tv) {    try {    Log.e(TAG, "Preparing printer...");    tv.setText("Preparing printer...");Thread.sleep(1000);Log.e(TAG, "Connecting printer...");tv.setText("Connecting printer...");Thread.sleep(1000);Log.e(TAG, "Printing.... " + msg);tv.setText("Printing.... ");Thread.sleep(1000);Log.e(TAG, "Done");} catch (InterruptedException e) {}    tv.setText(msg);    Toast.makeText(this, "Printing is done.", Toast.LENGTH_SHORT).show();    }}class ProxyService extends Binder {private BinderPrinterService mService;public ProxyService(BinderPrinterService svc) {mService = svc;}public void print(String msg, TextView tv) {mService.print(msg, tv);}}

client:

public class BinderClientActivity extends Activity {    ProxyService mService;    private TextView mStatusPanel;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.printer_activity);        setTitle("Binder client Activity");        mStatusPanel = (TextView) findViewById(R.id.status);        ((Button) findViewById(R.id.play)).setText("Print via extending Binder");    }    @Override    protected void onStart() {        super.onStart();        doBindService();    }private void doBindService() {Intent intent = new Intent(this, BinderPrinterService.class);        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);}    @Override    protected void onStop() {        super.onStop();        doUnbindService();    }private void doUnbindService() {if (mService != null) {            unbindService(mConnection);        }}public void onButtonClick(View v) {if (mService == null) {return;}mService.print("Tree of liberty must be refreshed from time to time with blood of patroits and tyrants",mStatusPanel);}    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName className, IBinder service) {            mService = (ProxyService) service;        }@Override        public void onServiceDisconnected(ComponentName arg0) {        mService = null;        }    };}

2. 使用Messenger對象

這是利用了訊息迴圈隊列來進行通訊的,也就是說服務端封裝成了一個Handler,用戶端向這個Handler發送Message,服務端再處理這個Message從而實現通訊。

優點,最突出的優點就是它可以保證線程上的安全,或者說時序上的安全。因為用戶端是向Handler發送訊息,而不是直接的函數調用,所以呢?Message都是按先後的順序放到服務端的訊息佇列當中,然後再由服務端Service決定如何處理這些Message。因為直接的函數調用會導致被調用的函數也會出現在調用者的線程之中,也就是說被調用到的函數也會繼續存在於調用者的調用棧中,因此有可能產生線程的問題。而Messenger方式,並不是直接的函數調用,而是僅向Service的Handler發送一個Message,然後調用者就此返回,其調用棧也就此停止,Service可以選擇如何處理這一個Message。Messenger方式即可以在同一個進程之中,也可以跨進程實現真正的IPC。

但是它的缺點也是相當的明顯的,就是它是發送一個Message對象,而不是直接的函數調用,所以非常的不直觀,另外,Message對象也無法方便的攜帶過多的參數,如果超過一個對象,只能封裝打包成一個對象然後再放到Message.obj中。需要注意的是,如果是在同一個進程之中,Message可以攜帶任何對象,但如果是跨進程,則Message.obj中存放的必須是實現了Parcelable介面的對象,否則無法實現IPC,會有Exception。還有一個缺點,就是Message對象的標識(Message.what)必須是Client端和Service端都定義一致,否則就無法通訊,這在調試的時候必須注意,因為這不會有編譯或者執行階段錯誤,但就是無法正常工作,是比較隱形Bug。

樣本:

Service:

public class MessengerPrinterService extends Service {    static final int MSG_PRINT = 1;private static final String TAG = "PrinterService";    private Handler mServiceHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case MSG_PRINT:                print("Freedom is nothing but a chance to be better!", (TextView) msg.obj);                    break;                default:                    super.handleMessage(msg);            }        }    };    final Messenger mMessenger = new Messenger(mServiceHandler);    @Override    public IBinder onBind(Intent intent) {        return mMessenger.getBinder();    }        public void print(String msg, TextView tv) {    try {    Log.e(TAG, "Preparing printer...");    if (tv != null) {    tv.setText("Preparing printer...");    }Thread.sleep(1000);Log.e(TAG, "Connecting printer...");if (tv != null) {tv.setText("Connecting printer...");}Thread.sleep(1000);Log.e(TAG, "Printing.... " + msg);if (tv != null) {tv.setText("Printing.... ");}Thread.sleep(1000);Log.e(TAG, "Done");} catch (InterruptedException e) {}    if (tv != null ) {    tv.setText(msg);    }    Toast.makeText(this, "Messenger Printing is done.", Toast.LENGTH_LONG).show();    }}

Local client(in the same application):

public class MessengerClientActivity extends Activity {    Messenger mService = null;    private ServiceConnection mConnection = new ServiceConnection() {        public void onServiceConnected(ComponentName className, IBinder service) {            mService = new Messenger(service);        }        public void onServiceDisconnected(ComponentName className) {            mService = null;        }    };    public void onButtonClick(View v) {        if (mService == null) return;        // Create and send a message to the service, using a supported 'what' value        Message msg = Message.obtain(null, MessengerPrinterService.MSG_PRINT, 0, 0);        msg.obj = findViewById(R.id.status);        try {            mService.send(msg);        } catch (RemoteException e) {            e.printStackTrace();        }    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.printer_activity);        setTitle("Messenger client Activity");        ((Button) findViewById(R.id.play)).setText("Print via constructing Messenger");    }    @Override    protected void onStart() {        super.onStart();        bindService(new Intent(this, MessengerPrinterService.class), mConnection,            Context.BIND_AUTO_CREATE);    }    @Override    protected void onStop() {        super.onStop();        if (mService != null) {            unbindService(mConnection);        }    }}

遠端的Client(在另外一個應用程式進程裡面):

public class AnotherMessengerClientActivity extends Activity {    private static final int MSG_PRINT = 1;Messenger mService = null;    private ServiceConnection mConnection = new ServiceConnection() {        public void onServiceConnected(ComponentName className, IBinder service) {            mService = new Messenger(service);        }        public void onServiceDisconnected(ComponentName className) {            mService = null;        }    };    public void onButtonClick(View v) {        if (mService == null) return;        // Create and send a message to the service, using a supported 'what' value        Message msg = Message.obtain(null, MSG_PRINT, 0, 0);        try {            mService.send(msg);        } catch (RemoteException e) {            e.printStackTrace();        }    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.printer_activity);        setTitle("Another Messenger client Activity");        ((Button) findViewById(R.id.play)).setText("Print via constructing Messenger");    }    @Override    protected void onStart() {        super.onStart();        Intent intent = new Intent();        intent.setClassName("com.example.effectiveandroid", "com.example.effectiveandroid.MessengerPrinterService");        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);    }    @Override    protected void onStop() {        super.onStop();        if (mService != null) {            unbindService(mConnection);        }    }}

3. 使用AIDL進程通訊

這是最正統的IPC方式,實際上Messenger的跨進程通訊的底層也是通過AIDL來實現的。它的優點就是完全是為跨進程而設計的,介面由AIDL檔案指定,Client端和Service端都通過AIDL所描述的介面來進行調用和實現,不容易出錯。需要注意的是介面中的所有的對象都必須實現了Parcelable介面,即使僅僅是在同一個進程之中。它的缺點就是必須要在每一個想要通訊的地方定義一個完全一樣的AIDL檔案,否則IPC不會成功,而且它是嚴格的按照IPC的機制來的,所以即使是在同一進程之內,所有的介面的參數和傳回值的對象必須是實現了Parcelable介面的,但Binder對象和Messenger就無此限制。需要一點:定義AIDL會嚴格按照IPC的方式進程即使是在同一個進程之中。所以,除非是真的要跨進程通訊,否則不要使用AIDL。

AIDL檔案:

package com.example.effectiveandroid;interface PrinterInterface {void print(String msg);}

AIDL Service:

public class AIDLPrinterService extends Service {private static final String TAG = "AIDLPrinterService";private Handler mHandler = new Handler();@Overridepublic IBinder onBind(Intent intent) {return mBinder;}private PrinterInterface.Stub mBinder = new PrinterInterface.Stub() {@Overridepublic void print(String msg) throws RemoteException {AIDLPrinterService.this.print(msg);}};    public void print(String msg) {    try {    Log.e(TAG, "Preparing printer...");Thread.sleep(1000);Log.e(TAG, "Connecting printer...");Thread.sleep(1000);Log.e(TAG, "Printing.... " + msg);Thread.sleep(1000);Log.e(TAG, "Done");} catch (InterruptedException e) {}    mHandler.post(new Runnable() {    @Override    public void run() {    Toast.makeText(AIDLPrinterService.this, "via AIDL Printing is done.", Toast.LENGTH_LONG).show();    }    });    }}

Local client:

public class AIDLClientActivity extends Activity {    private static final String TAG = "PrinterClientActivity";PrinterInterface mService;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.printer_activity);        setTitle("Local AIDL client Activity");        ((Button) findViewById(R.id.play)).setText("Print via AIDL");    }    @Override    protected void onStart() {        super.onStart();        doBindService();    }private void doBindService() {Intent intent = new Intent(this, AIDLPrinterService.class);        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);}    @Override    protected void onStop() {        super.onStop();        doUnbindService();    }private void doUnbindService() {if (mService != null) {            unbindService(mConnection);        }}public void onButtonClick(View v) {if (mService == null) {Log.e(TAG, "what the fucl service is not ready");return;}try {mService.print("This message is from local client via AIDL interface");} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName className, IBinder service) {            mService = PrinterInterface.Stub.asInterface(service);        }@Override        public void onServiceDisconnected(ComponentName arg0) {        mService = null;        }    };}

client in another application process:

public class AnotherAIDLClientActivity extends Activity {    private static final String TAG = "PrinterClientActivity";PrinterInterface mService;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.printer_activity);        setTitle("Another AIDL client Activity");        ((Button) findViewById(R.id.play)).setText("Print via AIDL");    }    @Override    protected void onStart() {        super.onStart();        doBindService();    }private void doBindService() {Intent intent = new Intent();intent.setClassName("com.example.effectiveandroid", "com.example.effectiveandroid.AIDLPrinterService");        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);}    @Override    protected void onStop() {        super.onStop();        doUnbindService();    }    private void doUnbindService() {if (mService != null) {            unbindService(mConnection);        }    }public void onButtonClick(View v) {if (mService == null) {Log.e(TAG, "what the fucl service is not ready");return;}try {mService.print("call PrinterService via AIDL from another application");} catch (RemoteException e) {e.printStackTrace();}}    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName className, IBinder service) {            mService = PrinterInterface.Stub.asInterface(service);        }@Override        public void onServiceDisconnected(ComponentName arg0) {        mService = null;        }    };}

結論

對比且總結了一下:如果是在同一個進程(應用程式)之中,且不涉及複雜的線程模式,直接使用Binder對象方式是最方便快捷的;如果是涉及跨進程操作,且不涉及複雜的線程模式就使用AIDL方式;無論是同一進程內還是跨進程,如果涉及比較複雜的線程模式就推薦使用Messenger的方式。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.