編輯:Android開發實例
前言
本文介紹一下Android下使用綁定服務進行時數據交互的幾種方法。關於Android下Service的內容,前面兩篇已經介紹了,不清楚的可以移步過去先看看:http://www.fengfly.com/plus/view-214081-1.html 、http://www.fengfly.com/plus/view-214082-1.html。
在前面的文章中已經介紹到了,對於Service組件而言,它只有在綁定模式下才可以與客戶端進行時交互,這裡講解幾個方法進行綁定服務與客戶端間的交互方法:
雖然根據官方文檔給出了三個方法,其中AIDL涉及的內容超出本博客內容范圍,以後有機會在另外介紹,本文只介紹1、2兩種方式的數據交互。
使用IBinder接口
如果看了之前關於Service博客的人,應該對IBinder接口有所了解,這裡簡單介紹一下IBinder。
IBinder是遠程對象的基本接口,是為高性能而設計的輕量級遠程調用機制的核心部分。但它不僅用於遠程調用,也可以用於進程內調用。這個接口定義了與遠程對象交互的協議,一般不直接實現這個接口,而是從它的實現類Binder中繼承。
通過IBinder進行服務的交互一般有兩種方式,一種方式是使用IBinder.transact()方法向遠端的IBinder對象發送一個發出調用,會回調遠端的Binder.onTransact()方法,這個方法傳遞的數據是Parcel。Parcel是一種緩沖區,除了數據外還有有一些描述它內容的元素,如果查看源碼的話會發現,Parcel本質上是一個Serialize,只是它在內存中完成了序列化和反序列化,利用的是連續的內存空間,因此效率會更高,並且AIDL的數據也是通過Parcel來交互的。另外一種方法就是拋棄IBinder中原生的方法,使用自定義的接口方法進行數據交互,這也是Android官方推薦綁定服務的一種數據交互方式。當然,不管是使用transact()給遠程服務交互,還是使用自定義的接口交互,都是同步執行的,直到遠程服務執行完並返回結果才會繼續向下執行。
其他關於適應IBinder服務的內容,在http://www.fengfly.com/plus/view-214081-1.html 中已經講解過了,這裡不再累述。下面使用一個例子來演示一下使用自定義接口與服務進行交互的例子。
服務:IBinderSer.java
- package cn.bgxt.servicebinddatedemo;
- import android.app.Service;
- import android.content.Intent;
- import android.os.Binder;
- import android.os.IBinder;
- import android.os.Parcel;
- import android.os.RemoteException;
- import android.util.Log;
- public class IBinderSer extends Service {
- private final String TAG="main";
- private final int MULTIPLE=1024;
- public final IBinder mBinder=new LocalBinder();
- public class LocalBinder extends Binder{
- // 在Binder中定義一個自定義的接口用於數據交互
- // 這裡直接把當前的服務傳回給宿主
- public IBinderSer getService(){
- return IBinderSer.this;
- }
- }
- @Override
- public IBinder onBind(Intent intent) {
- Log.i(TAG, "The service is binding!");
- // 綁定服務,把當前服務的IBinder對象的引用傳遞給宿主
- return mBinder;
- }
- public int getMultipleNum(int num){
- // 定義一個方法 用於數據交互
- return MULTIPLE*num;
- }
- }
調用服務的Activity:IBinderActivity.java
- package cn.bgxt.servicebinddatedemo;
- import android.app.Activity;
- import android.app.Service;
- import android.content.ComponentName;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.Bundle;
- import android.os.IBinder;
- import android.os.Messenger;
- import android.os.Parcel;
- import android.os.RemoteException;
- import android.view.View;
- import android.widget.Button;
- import android.widget.Toast;
- public class IBinderActivity extends Activity {
- private Button btnStart, btnInvoke, btnStop;
- IBinderSer mService=null;
- private ServiceConnection mConnection = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mService = null;
- }
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- // 獲取服務上的IBinder對象,調用IBinder對象中定義的自定義方法,獲取Service對象
- IBinderSer.LocalBinder binder=(IBinderSer.LocalBinder)service;
- mService=binder.getService();
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.layout_service);
- btnStart = (Button) findViewById(R.id.btnStartSer);
- btnInvoke = (Button) findViewById(R.id.btnInvokeMethod);
- btnStop = (Button) findViewById(R.id.btnStopSer);
- btnStart.setOnClickListener(onclick);
- btnInvoke.setOnClickListener(onclick);
- btnStop.setOnClickListener(onclick);
- }
- View.OnClickListener onclick = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.btnStartSer:
- Toast.makeText(getApplicationContext(), "綁定服務成功", Toast.LENGTH_SHORT).show();
- bindService(new Intent(IBinderActivity.this,IBinderSer.class),mConnection,Service.BIND_AUTO_CREATE);
- break;
- case R.id.btnInvokeMethod:
- if(mService==null){
- Toast.makeText(getApplicationContext(), "請先綁定服務", Toast.LENGTH_SHORT).show();
- return;
- }
- // 調用綁定服務上的方法,進行數據交互
- int iResult=mService.getMultipleNum(10);
- Toast.makeText(getApplicationContext(), "服務計算結果為:"+iResult, Toast.LENGTH_SHORT).show();
- break;
- case R.id.btnStopSer:
- Toast.makeText(getApplicationContext(), "服務解除綁定", Toast.LENGTH_SHORT).show();
- unbindService(mConnection);
- mService=null;
- break;
- default:
- break;
- }
- }
- };
- }
執行結果:
使用Messenger類
除了使用IBinder之外,還可以使用Messenger,那麼先來聊聊什麼是Messenger。
Messenger引用了一個Handler獨享,可以使用Messenger.send(Message msg)方法跨進程向服務發送消息,只需要在服務中使用Handler創建一個Messenger,宿主持有這個Messenger就可以與服務進行通信。之前介紹的handler+Message的通信方式不同,那都是在同一個進程中的,從工作線程持有一個主線程的Handler對象,從而向主線程發送消息,這裡不了解的可以看看之前的http://www.fengfly.com/plus/view-213516-1.html。而上面介紹過了,Android可以使用IBinder實現跨進程通信,並且也將Handler與IBinder結合起來實現跨進程發送消息。
當然這裡提一下,Messenger管理的是一個消息隊列,它會依據消息進入的先後次序予以執行,所以也不需要把服務設計為線程安全是。
實現Messenger實現進程通信,主要有以下幾點注意:
下面通過一個簡單的例子來演示一下利用Messenger在服務與客戶端進行的通信。
服務:MessengerSer.java
- package cn.bgxt.servicebinddatedemo;
- import android.app.Service;
- import android.content.Intent;
- import android.os.Handler;
- import android.os.IBinder;
- import android.os.Message;
- import android.os.Messenger;
- import android.util.Log;
- import android.widget.Toast;
- public class MessengerSer extends Service {
- private final String TAG="main";
- static final int MSG_SAY_HELLO = 1;
- public class IncomingHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SAY_HELLO:
- Toast.makeText(getApplicationContext(), "Service say hello!",
- Toast.LENGTH_SHORT).show();
- Log.i(TAG, "Service say hello!");
- break;
- default:
- super.handleMessage(msg);
- }
- }
- }
- IncomingHandler incomingHandler=new IncomingHandler();
- final Messenger mMessenger=new Messenger(new IncomingHandler());
- @Override
- public IBinder onBind(Intent arg0) {
- return mMessenger.getBinder();
- }
- }
服務綁定的Activity:MessengerActivity.java
- package cn.bgxt.servicebinddatedemo;
- import android.app.Activity;
- import android.app.Service;
- import android.content.ComponentName;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.Bundle;
- import android.os.IBinder;
- import android.os.Message;
- import android.os.Messenger;
- import android.os.RemoteException;
- import android.view.View;
- import android.widget.Button;
- import android.widget.Toast;
- public class MessengerActivity extends Activity {
- private Button btnStart, btnInvoke, btnStop;
- private Messenger mService = null;
- private ServiceConnection mConnection = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mService = null;
- }
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- // 使用服務端的IBinder對象實例化一個Messenger對象
- mService = new Messenger(service);
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- setContentView(R.layout.layout_service);
- btnStart = (Button) findViewById(R.id.btnStartSer);
- btnInvoke = (Button) findViewById(R.id.btnInvokeMethod);
- btnStop = (Button) findViewById(R.id.btnStopSer);
- btnStart.setOnClickListener(onclick);
- btnInvoke.setOnClickListener(onclick);
- btnStop.setOnClickListener(onclick);
- }
- View.OnClickListener onclick = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.btnStartSer:
- Toast.makeText(getApplicationContext(), "綁定服務成功", Toast.LENGTH_SHORT).show();
- bindService(new Intent(getApplicationContext(),MessengerSer.class), mConnection, Service.BIND_AUTO_CREATE);
- break;
- case R.id.btnInvokeMethod:
- if(mService==null){
- Toast.makeText(getApplicationContext(), "請先綁定服務",Toast.LENGTH_SHORT).show();
- return ;
- }
- // 實例化一個Message對象
- Message msg=Message.obtain(null, MessengerSer.MSG_SAY_HELLO, 0, 0);
- try{
- // 把Message獨享傳遞給服務端處理
- mService.send(msg);
- }
- catch(RemoteException e){
- e.printStackTrace();
- }
- break;
- case R.id.btnStopSer:
- Toast.makeText(getApplicationContext(), "服務解除綁定", Toast.LENGTH_SHORT).show();
- unbindService(mConnection);
- mService=null;
- break;
- default:
- break;
- }
- }
- };
- }
執行結果:
使用AIDL
AIDL(Android Interface Definition Language),它可以實現跨進程間的通信。之前講到的Messenger實現跨進程通信,其實也是基於AIDL作為底層結構。但是正如上面提到的,Messenger創建的一個消息隊列是在一個單獨的線程中,所以服務一次僅處理一個請求,然而,如果想要服務同時處理多個請求,就需要使用到AIDL,但是這種情況下就要考慮多線程和線程安全的問題了。這個不在本篇博客的范疇內,以後有機會在細細講解。
源碼下載
在Android的應用開發中,我們會用到各種代碼調試;其實在Android的開發之後,我們可能會碰到一些隨機的問題,如cpu過高,內存洩露等,我們無法簡單的進行代
Android提供了許多方法來控制播放的音頻/視頻文件和流。其中該方法是通過一類稱為MediaPlayer。Android是提供MediaPlayer類訪問內置的媒體播放
可以顯示在的Android任務,通過加載進度條的進展。進度條有兩種形狀。加載欄和加載微調(spinner)。在本章中,我們將討論微調(spinner)。Spinner 用
Android應用程序可以在許多不同地區的許多設備上運行。為了使應用程序更具交互性,應用程序應該處理以適合應用程序將要使用的語言環境方面的文字,數字,文件等。在本章中,我