Android學習七之Service(三)

 上次上心理學課的時候,老師講到溝通,溝通的一個必要前提是心平氣和,可是昨天我真的特別氣憤發火,現在的大學生怎麼變成這樣了!並不是我這個人很保守或是怎樣,不喜歡別人女生還要提出那種無恥的要求,學校裏又不是紅燈區!


好吧,算我說了一些廢話。


上回說到要Service有兩種:Started 和 Bound,已經學過了Started,因爲文檔裏專門拿出一頁來介紹Bound Service,

我估計這個Bound Service 更常用。

首先打開文檔:
http://developer.android.com/guide/topics/fundamentals/bound-services.html


1,什麼是Bound Service?

Bound Service是在C/S模式下作爲服務端(Server)存在,Bound Service允許其他組件綁定這個服務,並且發送請求,接受響應,甚至進行進程間通信(IPC),Bound Service通常依賴於其他應用程序組件,並且不會無限期運行下去。
(有一點我聲明一下,就是這些名詞我沒法翻譯,每個人的理解都不一樣,我乾脆就不翻譯了,知道是這麼個意思就行了。)

2,基礎知識

要創建一個Bound Service:

首先需要繼承Service或其子類,這樣纔算是一個Service

其次需要覆寫onBind()方法,這樣纔算是一個Bound Service

打開API文檔:


在Service這個基類裏這個方法是抽象方法,需要返回一個實現了IBinder接口的類的對象,這個接口就是聲明瞭客戶端可以和Service進行通信。到下面找到更爲詳細的解釋:


這個返回的IBinder對象着實需要花點時間去理解,要實現一個Bound Service,最重要的方法就是這個。文檔裏提到一點:

 

Multiple clients can connect to the service at once. However, the system calls your service's onBind() method to retrieve the IBinder only when the first client binds. The system then delivers the same IBinder to any additional clients that bind, without calling onBind() again.
可以有多個客戶端同時連接這個Service,但是onBind()方法只會調用一次,返回的也只會有一個IBinder對象,所有連接到這個Service的客戶端都是得到這個相同的IBinder對象。


要綁定一個Service,客戶端可以調用

 boolean bindService (Intent service, ServiceConnection conn, int flags)

這是android.content.Context類中的方法,Service繼承Context。這個方法中需要傳入一個Intent 意圖、ServiceConnection  連接、int 標識位,還是直接拿文檔的過來吧。

如果成功綁定了Service,那麼會立即返回true,否則是false。
方法的參數裏有個ServiceConnection對象,這個ServiceConnction是什麼呢?因爲我沒下載到源代碼,只能看文檔

這是一個接口,接口裏定義了兩個方法:onServiceConnected()和onServiceDisconnected();從文檔裏可以看出這個接口是孤零零的,只有一個類實現了它。我們基本上是要自己實現這個接口,並且實現這兩個方法。

下面到最實用的部分:
3.,創建Bound Service的方法
       直接看代碼吧
public class LocalService extends Service {
    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();
    // Random number generator
    private final Random mGenerator = new Random();

    /**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            // Return this instance of LocalService so clients can call public methods
            return LocalService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    /** method for clients */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}


因爲onBind()方法需要返回一個IBinder對象,需要自己對應一個類實現IBinder,有兩種辦法可以定義這個類:
     1,繼承Binder類
找到Binder

在自己定義的類LocalBinder中定義了一個方法:getService()。


再看一下客戶端的代碼:

public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    /** Called when a button is clicked (the button in the layout file attaches to
      * this method with the android:onClick attribute) */
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call were something that might hang, then this request should
            // occur in a separate thread to avoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** Defines callbacks for service binding, passed to bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}

 從onServiceConnected()回調方法中拿到了Service,並賦值給了局部變量,在onStart()方法中調用bindService()綁定Service。

從新理一下:
 1,在自定義的Service中創建一個IBinder實例
 2,在onBind()返回這個實例
 3,在客戶端中先拿到IBinder實例,在調用他提供的getService()方法(自己定義的)拿到Service
 4,在onStart()方法中調用bindService()實現綁定

2,用一個Messenger
如果Service需要進行遠程進程通信,那麼需要一個Messenger爲Service提供一個接口
先找到Messenger

下面有一些方法:


如果使用Messenger,
先看代碼:
public class MessengerService extends Service {
    /** Command to the service to display a message */
    static final int MSG_SAY_HELLO = 1;

    /**
     * Handler of incoming messages from clients.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }
}

在這個Service中有個內部類,IncomingHandler繼承Handle,並且覆寫了handleMessage()方法。這有牽扯到一個問題:什麼是Handle?
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue

今天重點不是這個Handler.
在這個類中定義了一個Messenger變量,並且通過構造函數傳入一個Handler 對象初始化了。
在onBind()方法中,用Messenger的getBinder()方法返回了一個IBinder對象。

再看客戶端代碼:
public class ActivityMessenger extends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;

    /** Flag indicating whether we have called bind on the service. */
    boolean mBound;

    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = new Messenger(service);
            mBound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null;
            mBound = false;
        }
    };

    public void sayHello(View v) {
        if (!mBound) return;
        // Create and send a message to the service, using a supported 'what' value
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to the service
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

我用紅色標記了一些代碼,
在客戶端中先創建一個Messenger,在onServiceConnected()方法中進行賦值。在功能代碼sayHello()中先創建一個Message,再調用send()方法發出這個Message。
最後在onStop()方法進行了解綁定,用unbindService()方法,傳入一個ServiceConnection作爲參數。

稍微讀了一下上面代碼的人都會發現裏面有一個標識位: boolean mBound
在onStop()和onServiceDisconnected()方法中mBound=false; 
在onServiceConnected()方法中mBound=true;

再總結一下要創建一個Bound Service在客戶端(client)需要做哪些事:

1,實現ServiceConnection,實現接口中的兩個方法。
2,調用bindService()方法。
3,onServiceConnected()方法中需要通過一些定義好的方法取得Service。
4,調用unbindService()方法解除綁定。

最後把Bound Service的生命週期介紹完,然後喫飯去,再直接去上課

在看那個圖之前,我覺得應該可以參照Activity的生命週期把Bound Service的生命週期想象一下,雖然會有一些不同。


我忘了,沒有把onRebind()方法說一下。
我個人覺得這個圖實在是講的太詳細了,當然在API裏對Service的生命週期也是很詳細的文字描述。


不過顯然是圖形更好理解。

下次我打算把Content Providers學完,我記得是會用到Note Pad的例子



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章