Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> AndroidService 深度解析(2)

AndroidService 深度解析(2)

編輯:關於Android編程

AndroidService 深度解析(2)

上一篇文章我們對Service的生命周期進行了測試及總結。這篇文章我們介紹下綁定運行的Service的實現。

綁定運行的Service可能是僅為本應用提供服務,稱為本地Service;也可能為其他應用提供跨進程服務,即遠程Service。下面分別進行介紹:

本地Service

如果Service只服務於本應用,那麼我們只需要繼承Binder類,定義我們需要實現的方法即可,當發起綁定連接時,Service將會在onBind方法中返回這個繼承類的對象,使得客戶端與Service共享一個Binder對象。Binder就像一座橋梁,使客戶端與Service能夠互相聯系。下面貼出本地Service的實現示例:

LocalService代碼:

 

 

public classLocalService extends Service {
    private String TAG =getClass().getSimpleName();
    MyBinder myBinder = new MyBinder();
    ServiceListener myServiceListener;
    public LocalService() {
    }
 
    public interface ServiceListener {
        public String getActivityInfo();
    }
 
    private void setListener(ServiceListenermyServiceListener) {
        this.myServiceListener = myServiceListener;
    }
 
    //綁定成功後,Service就可以通過這個方法獲得Activity的信息
    private void getActivityInfo() {
        String activityInfo =myServiceListener.getActivityInfo();
        Log.d(TAG, TAG ++activityInfo------> + activityInfo);
    }
 
    private String getInfo() {
        return Hello,我是LocalService的方法,你可以通過它的對象訪問我!;
    }
 
    public class MyBinder extends Binder {
        public String getServiceInfo() {
            return getInfo();
        }
 
        public void setServiceListener(ServiceListenermyServiceListener) {
            setListener(myServiceListener);
        }
    }
 
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, TAG +------>onBind());
        return myBinder;
    }
 
    @Override
    public void onRebind(Intent intent) {
        Log.d(TAG, TAG +------>onRebind());
        super.onRebind(intent);
    }
 
    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, TAG +------>onUnbind());
        //return false;這裡的返回值決定下一次綁定時是否執行onRebind
        return true;
    }
}

 

 

LocalActivity代碼:

 

 

public classLocalActivity extends ActionBarActivity {
    private String TAG =getClass().getSimpleName();
    Intent serviceIntent;
 
    @Override
    protected void onCreate(BundlesavedInstanceState) {
        super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_local);
        Log.d(TAG, TAG +------>onCreate());
        serviceIntent = new Intent(this,LocalService.class);
    }
 
    public void bindService(View v) {
        Log.d(TAG, TAG +------>bindService());
        bindService(serviceIntent,serviceConnection, Service.BIND_AUTO_CREATE);
    }
 
    public void unBindService(View v) {
        Log.d(TAG, TAG +------>unBindService());
        unbindService(serviceConnection);
    }
 
    @Override
    protected void onDestroy() {
        Log.d(TAG, TAG +------>onDestroy());
        super.onDestroy();
    }
 
    ServiceConnection serviceConnection = newServiceConnection() {
        @Override
        public voidonServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, TAG +------>onServiceConnected());
            LocalService.MyBinder binder =(LocalService.MyBinder) service;
            String localServiceInfo =binder.getServiceInfo();
            Log.d(TAG, TAG ++onServiceConnected------> + localServiceInfo);
            binder.setServiceListener(newLocalService.ServiceListener() {
                @Override
                public String getActivityInfo(){
                    return Hello,我在LocalActivity中,LocalService可以調用我獲得LocalActivity的消息!;
                }
            });
        }
 
        @Override
        public voidonServiceDisconnected(ComponentName name) {
            Log.d(TAG, TAG +------>onServiceDisconnected());
        }
    };
}

 

 

Activity對應的布局就是兩個按鈕,分別實現綁定和解綁功能,如圖:

 

Activity與Service都是需要在Manifest文件中注冊的哦。

 

我們啟動Activity,先後綁定Service,輸出日志如下:

 

 

03-17 10:10:58.525  D/LocalActivity﹕LocalActivity------>onCreate()
03-17 10:11:00.955  D/LocalActivity﹕LocalActivity------>bindService()
03-17 10:11:00.975  D/LocalService﹕LocalService------>onBind()
03-17 10:11:00.995  D/LocalActivity﹕LocalActivity------>onServiceConnected()
03-17 10:11:00.995  D/LocalActivity﹕ LocalActivity+onServiceConnected------>Hello,我是LocalService的方法,你可以通過它的對象訪問我!
03-17 10:11:16.345  D/LocalActivity﹕LocalActivity------>unBindService()
03-17 10:11:16.345  D/LocalService﹕LocalService------>onUnbind()

 

 

上面的日志顯示,我們的確實現了Service的綁定與解綁工作,不僅如此,細心的你應該還發現了我們實現了Service與Activity中相互的調用吧。是的,在實際工作中我們不僅需要指示Service為我們提供服務,Service有時也需要獲取到客戶端的數據來更好地提供服務(LocalService中的getActivityInfo方法 通過回調實現)。

這裡我總結下具體的實現過程:

1、在Service類中,設計繼承Binder類的內部類MyBinder,添加需要向Activity提供的方法。本例中的getServiceInfo方法實現了獲取Service信息的功能,當然有時候為了簡便,我們直接提供方法返回Service對象,但是一般並不建議這樣做;同時注意到setServiceListener方法,它是實現Service調用Activity提供方法的重要環節,我們通過回調的方法實現了Service對Activity的訪問;

2、重寫onBind方法,並返回MyBinder對象。至此,Service類的設計就完成了;

3、在Activity中,重寫ServiceConnection接口的onServiceConnected與onServiceDisConnected方法;在onServiceConnected方法中,我們獲得了onBinder方法返回的MyBinder對象,然後調用setServiceListener方法設置Service訪問Activity所需要的回掉接口對象;
4、至此,Service與Activity之間的 “橋梁”搭建完畢。在Service中我們可以通過getActivityInfo方法獲得Activity的信息;而在Activity中,我們也可以通過getServiceInfo方法獲得Service的信息。

遠程Service

當我們的Service需要為其他應用提供服務的時候,我們就要用到遠程Service了。遠程Service有兩種實現方式,分別是Messenger方式與AIDL(Andriod進程間接口描述語言)方式。下面分別進行介紹。

Messenger方式

在這種方式中,我們定義一個Handler來處理不同的Message對象。這個Handler是Messenger實現與客戶端共享IBinder的基礎,它允許客戶端通過Message對象向Service發送命令。另外,客戶端也可以定義一個Messenger,這樣,Service也可以把消息發送給客戶端。 這是實現進程間通信的最簡單的方式,因為Messenger隊列將會在單線程中執行,我們不需要去考慮線程安全。

使用Messenger實現進程間通信的步驟:

l 實現一個Handler,它用來處理傳遞的Message對象;

l 創建一個Messenger對象,將Handler對象作為構造參數;

l 使用Messenger對象創建一個IBinder對象,並通過onBind返回;

l 客戶端將接收到的IBinder對象作為Messenger的構造參數,實例化一個Messenger對象,這個Messenger對象將擁有Handler的引用;

l 在客戶端通過Handler發送Message對象,Service中就可以通過Handler的handleMessage處理這個Message。

下面提供一個示例:

我們在ServiceTest工程下新建MessengerService類:

 

public class MessengerService extendsService {
   /**
    * Command to the service to display a message
    */
   static final int MSG_SAY_HELLO = 1;
   static final int MSG_GET_CLIENT_MESSENGER = 2;
   static final int MSG_FROM_SERVICE = 3;
 
   private String TAG = getClass().getSimpleName();
   Messenger messengerToClient;
 
   /**
    * Handler of incoming messages from clients.
    */
   class ServiceIncomingHandler extends Handler {
       @Override
       public void handleMessage(Message msg) {
            Log.d(TAG, TAG + ------>handleMessage());
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Log.d(TAG,handleMessage------>MSG_SAY_HELLO!);
                   Toast.makeText(getApplicationContext(), hello!,Toast.LENGTH_SHORT).show();
                    break;
                case MSG_GET_CLIENT_MESSENGER:
                    Log.d(TAG,handleMessage------>Service收到Activity的messenger對象!);
                    //此處獲得可向客戶端發送消息的Messenger對象
                    messengerToClient =msg.replyTo;
                    Message serviceMsg =Message.obtain(null, MSG_FROM_SERVICE, 0, 0);
                    try {//向客戶端發送消息
                       messengerToClient.send(serviceMsg);
                    } catch (RemoteException e){
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
       }
   }
 
   /**
    * 將這個serviceMessenger發送給客戶端,客戶端就可以通過它聯系Service了
    */
   final Messenger serviceMessenger = new Messenger(newServiceIncomingHandler());
 
   /**
    * When binding to the service, we return an interface to our messenger
    * for sending messages to the service.
    */
   @Override
   public IBinder onBind(Intent intent) {
       Log.d(TAG, TAG + ------>onBind());
       Toast.makeText(getApplicationContext(), binding,Toast.LENGTH_SHORT).show();
       return serviceMessenger.getBinder();
   }
}


 

新建MessengerTest工程,並創建ActivityMessenger類:

 

public classActivityMessenger extends Activity {
    /**
     * Messenger for communicating with theservice.
     */
    Messenger messengerToService = null;
    /**
     * Flag indicating whether we have calledbind on the service.
     */
    boolean mBound;
    private String TAG =getClass().getSimpleName();
    /**
     * Command to the service to display amessage
     */
    static final int MSG_SAY_HELLO = 1;
    static final intMSG_SEND_MESSENGER_TO_SERVICE = 2;
    static final int MSG_FROM_SERVICE = 3;
 
    /**
     * Class for interacting with the maininterface of the service.
     */
    private ServiceConnection mConnection = newServiceConnection() {
        public voidonServiceConnected(ComponentName className, IBinder service) {
            // This is called when theconnection with the service has been
            // established, giving us theobject we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, sohere we get a client-side
            // representation of that from theraw IBinder object.
            Log.d(TAG, TAG +------>onServiceConnected());
            messengerToService = newMessenger(service);
            mBound = true;
            Message msg = Message.obtain(null,MSG_SEND_MESSENGER_TO_SERVICE, 0, 0);
            msg.replyTo = activityMessenger;
            try {//Messenger對象發送消息,這個msg對象將交給Service類中的handleMessage處理
                messengerToService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
 
        public voidonServiceDisconnected(ComponentName className) {
            // This is called when theconnection with the service has been
            // unexpectedly disconnected --that is, its process crashed.
            Log.d(TAG, TAG +------>onServiceDisconnected());
            messengerToService = null;
            mBound = false;
        }
    };
 
    /**
     * Handler of incoming messages fromservice.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg){
            Log.d(TAG, TAG + ------>handleMessage());
            switch (msg.what) {
                case MSG_FROM_SERVICE:
                    Log.d(TAG, TAG ++MSG_FROM_SERVICE------>Activity收到Service回復的消息!);
                   Toast.makeText(getApplicationContext(), MSG_FROM_SERVICE!,Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
 
    /**
     * Target we publish for service to sendmessages to IncomingHandler.
     */
    final Messenger activityMessenger = newMessenger(new IncomingHandler());
 
    public void sayHello(View v) {
        Log.d(TAG, TAG +------>sayHello());
        if (!mBound) return;
        // Create and send a message to theservice, using a supported 'what' value
        Message msg = Message.obtain(null,MSG_SAY_HELLO, 0, 0);
        try {//Messenger對象發送消息,這個msg對象將交給Service類中的handleMessage處理
            messengerToService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
 
    @Override
    protected void onCreate(BundlesavedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, TAG +------>onCreate());
    }
 
    @Override
    protected void onStart() {
        Log.d(TAG, TAG +------>onStart());
        super.onStart();
        // Bind to the service
        bindService(newIntent(com.example.servicestudy.remoteservie.MessengerService),mConnection,
                Context.BIND_AUTO_CREATE);
    }
 
    @Override
    protected void onStop() {
        Log.d(TAG, TAG +------>onStop());
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

 

Acitivty與Service都需要在Manifest文件中注冊。Service需要設置android:exported屬性為true,並設置intent-filter。如下所示:

 

好了,編碼工作我們已經完成了,下面我們進行一下測試,打印日志如下:

 

 

03-1713:11:46.195  D/ActivityMessenger﹕ActivityMessenger------>onCreate()
03-1713:11:46.195  D/ActivityMessenger﹕ActivityMessenger------>onStart()
03-1713:11:46.215  D/MessengerService﹕MessengerService------>onBind()
03-17 13:11:46.235  D/ActivityMessenger﹕ActivityMessenger------>onServiceConnected()
03-1713:11:46.275  D/MessengerService﹕MessengerService------>handleMessage()
03-17 13:11:46.275 D/MessengerService﹕ handleMessage------>Service收到Activity的messenger對象!
03-17 13:11:46.285  D/ActivityMessenger﹕ActivityMessenger------>handleMessage()
03-17 13:11:46.285 D/ActivityMessenger﹕ ActivityMessenger+MSG_FROM_SERVICE------>Activity收到Service回復的消息!
03-1713:11:55.425  D/ActivityMessenger﹕ ActivityMessenger------>sayHello()
03-1713:11:55.425  D/MessengerService﹕MessengerService------>handleMessage()
03-1713:11:55.425  D/MessengerService﹕handleMessage------>MSG_SAY_HELLO!
03-1713:12:00.665  D/ActivityMessenger﹕ActivityMessenger------>onStop()

 

 

看見了嗎?完美實現了Activity與Service互相發送消息。它們都是基於Messenger對象,Activity從Ibinder處獲得一個可向Service發送消息的Messenger對象,接著Activity給Service發送了一個消息,並將可向Activity發送消息的Messenger對象攜帶過去,這樣就實現了他們之間的交互。邏輯上和便馬上都很簡單易懂吧,點個贊。

好了,既然Messenger這麼簡單易用,為什麼我們還需要繼續看AIDL方式呢?不知道你有沒有想過,Messenger方式在處理客戶端發送的消息時,是將所有消息排成一個隊列,然後依次處理,也就是單線程處理方式,這種處理方式的優點是簡便不容易引起其他問題,比如線程安全;但是,對於一些即時性要求比較高的服務,這種方式可能就不夠用了,也許我們需要采用多線程的方式,將接收到的請求盡快處理,這時候就可以直接使用AIDL方式了。

AIDL方式

其實我悄悄告訴你,Messenger方式的底層實現也是基於AIDL方式實現的,系統為了方便跨進程的服務,為我們提供了一個Messenger類來便利的實現,但是它可能無法滿足我們的需求,這時候我們就需要直接基於AIDL方式實現了。

其實AIDL的實現不難,只是有很多細節需要注意。我這裡也不過多描述推薦一篇文章,有代碼和總結:http://blog.csdn.net/songjinshi/article/details/22918405

我把總結摘抄過來:

AIDL的創建方法:
AIDL語法很簡單,可以用來聲明一個帶一個或多個方法的接口,也可以傳遞參數和返回值。由於遠程調用的需要, 這些參數和返回值並不是任何類型.下面是些AIDL支持的數據類型:
1. 不需要import聲明的簡單Java編程語言類型(int,boolean等)
2.String, CharSequence不需要特殊聲明
3.List, Map和Parcelables類型, 這些類型內所包含的數據成員也只能是簡單數據類型, String等其他比支持的類型. ( (另外: 我沒嘗試Parcelables, 在Eclipse+ADT下編譯不過, 或許以後會有所支持).
下面是AIDL語法:
// 文件名: SomeClass.aidl // 文件可以有注釋, 跟java的一樣 // 在package以前的注釋, 將會被忽略. // 函數和變量以前的注釋, 都會被加入到生產java代碼中. package com.cmcc.demo;
//import 引入語句 import com.cmcc.demo.ITaskCallback;
interfaceITaskBinder {
//函數跟java一樣, 可以有0到多個參數 ,可以有一個返回值 boolean isTaskRunning();
voidstopRunningTask(); //參數可以是另外的一個aidl定義的接口 void registerCallback(ITaskCallback cb);
voidunregisterCallback(ITaskCallback cb);
//參數可以是String, 可以用in表入輸入類型, out表示輸出類型.
intgetCustomerList(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()

 

 

 

至此,關於Service的全部學習均已完成。我們進行了Android Service的完整測試學習,主要包括生命周期測試,本地綁定運行Service實現、遠程綁定運行Service的Messenger方式與AIDL方式實現 。 所有BOUND SERVICE的示例均實現了Service與客戶端的交互功能,即Service可以調用客戶端的方法,客戶端也可以調用Service的方法。 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved