顯示具有 Service 標籤的文章。 顯示所有文章
顯示具有 Service 標籤的文章。 顯示所有文章

星期日, 5月 24, 2015

Android Interface Definition Language (AIDL) (一)

Android Interface Definition Language (AIDL)


行程間通訊(IPC,Inter-Process Communication),指至少兩個行程或執行緒間傳送資料或訊號的一些技術和方法。在Android系統中提供一些方法進行IPC,AIDL為其中一種方法。

AIDL (android interface definition language) 與其他你可能已經使用過的IDLs相似,它允許你在client和service兩者上編寫介面(interface)用來在彼此之間進行行程間通訊。在Android上,一個行程按理是無法存取其他行程的記憶體,所以說,它們需要將它們的物件分解為作業系統可以理解的原始狀態,封送(marshell)物件跨過邊界。編寫進行封送的程式是乏味的,所以Android利用AIDL處理好了。

注意,只有在如果你允許來自其他應用程式的用戶端存取你的服務(service)及想在你的服務(service)中處理多執行緒,使用AIDL是必須的。如果你不需要在跨不同應用程式間實現並行IPC,你應該藉由實現一個Binder建立你的介面(interface),或者你想進行IPC,但不需要處理多執行緒,利用一個Messager實現你的介面(interface)。無論如何,在你實現AIDL前確保你理解bound service了。

在你開始設計你的AIDL介面(interface)前,注意調用AIDL介面(interface)是函式直接調用,你不應該做出假設在執行緒中調用函式的可能。發生情況不同依據是否來自在本地行程或遠端行程中的一個執行緒的調用。特別是
  • 執行在相同執行緒中的本地行程產生的調用正在調用,如果此執行緒是你的主要使用者介面執行緒(UI thread),此執行緒繼續在AIDL介面中執行。如果此執行緒是其他執行緒,是在你的服務(service)中執行你的程式的其中之一執行緒。因此,如果只有本地的執行緒正在存取服務(service),你可以完全地控制在服務中執行的執行緒(但是,如果是這樣的話,你不應該使用AIDL,但應該實現一個Binder來建立介面來取代之)。
  • 來自遠端行程的調用被一個執行緒池(thread pool)調度,該平台在你自己的行程中維護,你必須為來自未知執行緒的調用作準備,可能在同一時間許多調用發生,換句話說,AIDL的實現必須是完全地執行緒安全(thread-safe)。
  • oneway關鍵字改變遠端調用的行為。當使用時,遠端調用不會阻塞,它會簡易地送交易資料並立即回傳,介面的實現終究會接收它如來自Binder執行緒池正規的遠端調用。如果oneway備用在本地調用,不會有任何影響且調用仍是同步的。







星期六, 9月 24, 2011

繼承Service類實現started類型的service

Android應用程式學習筆記

繼承Service類實現started類型的service

首先,我們從之前學習到實現started有兩種方法,一種是繼承IntentService類別,一種是繼承Service類別,以下有我們的程式碼,一樣我們設定三個按鈕,一個按鈕是啟動service,一個是
停止service,一個是再啟動service一次,service自己會運行10秒鐘後自動銷毀。

那有幾個特點我們藉這次機會實驗一下:
1.started service的生命週期是
組件呼叫startService()方法 -> onCreate() -> onStartCommand() -> Service running -> Service stoped by itself or client -> onDestroy()
那我們就在各個回呼方法中加上 System.out.println("")來驗證,當執行到該回呼方法就會輸出回呼方法的名稱。

2.一旦service啟動後,就會無限地在後台運行。所以我們試著在啟動service後10秒鐘內按下"BACK"關閉應用程式,看看service是不是還在後台運行。因為service在10秒鐘會自己銷毀,並呼叫onDestory()方法來驗證。

3.它與IntentService不同是能處理多執行續。(還在試)

4.執行續一次只執行一個請求,所以後進的請求必須等前一個請求完成後才會被處理。那我們這就用兩個按鈕來對service傳送兩個請求,並看看是不是在第一個請求執行10秒後,才在執行第二個請求。



package test.helloservice;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;

public class HelloService extends Service {

//宣告一個Looper物件作為與執行續通訊的接口
private Looper mServiceLooper;
//宣告一個Handler物件作為處理訊息的接口
private ServiceHandler mServiceHandler;

//Handler接收從執行續傳來的訊息
private final class ServiceHandler extends Handler{
public ServiceHandler(Looper looper){
super(looper);
System.out.println("ServiceHandler");
System.out.println("Thread------> " + Thread.currentThread().getId());
}

//處理訊息的方法
@Override
public void handleMessage(Message msg){
System.out.println("handleMessage");
System.out.println("Thread------> " + Thread.currentThread().getId());
//通常我們把要執行的工作在此處完成
long endTime = System.currentTimeMillis() + 10*1000;
       while (System.currentTimeMillis() < endTime) {
           synchronized (this) {
               try {
                   wait(endTime - System.currentTimeMillis());
               } catch (Exception e) {
               }
           }
       }
     
       stopSelf(msg.arg1);
}
}



//第一次產生service時系統呼叫此方法
@Override
public void onCreate(){
System.out.println("onCreate");
System.out.println("Thread------> " + Thread.currentThread().getId());
//因為通常直service工作會在主執行緒中完成,但此方式可能會阻塞主執行緒,所以我們另外產生執行緒
HandlerThread thread = new HandlerThread("ServiceStartArguments" ) ;
thread.start();
//從執行續取得Looper物件
mServiceLooper = thread.getLooper();
//將Looper物件設置到handler,如此handler才會處理執行許傳遞的訊息
mServiceHandler = new ServiceHandler(mServiceLooper);
}


@Override
public int onStartCommand(Intent intent , int flags , int startId){
System.out.println("onStartCommand");
System.out.println("Thread------> " + Thread.currentThread().getId());

Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);

return START_STICKY;

}
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
System.out.println("onBind");
System.out.println("Thread------> " + Thread.currentThread().getId());
return null;
}

@Override
public void onDestroy(){
System.out.println("onDestroy");
System.out.println("Thread------> " + Thread.currentThread().getId());
super.onDestroy();
}

}


以下那我們實驗結果

1.

2.3.4.可以自己回去實驗。

星期三, 9月 21, 2011

Service(八)

Android應用程式學習筆記

Running a Service in the Foreground

Service不僅只在後台執行,也可以讓service在前台運行,只是這樣的用法比較少。

只要呼叫startForeground()啟動service,就可以讓service在前台執行。這個方法有兩個參數,第一個參數是整數,只用來標識notification。第二個參數是Notification物件。

Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
        System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
        getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION, notification);

停止service只要呼叫stopForeground()。


Managing the Lifecycle of a Service

service的生命週期比起activity的簡單多了,然而,密切關注你的service如何產生及如何銷毀的甚至更重要,因為service可以在後台執行,不會被使用者意識到。

service生命週期可以分成兩部分來看:
  • A started service
    Service由其他組件在呼叫startService()方法產生,service會無限地運行,藉著自己呼叫stopSelf()停止自己,或者其他組件呼叫stopService()來停止service,當service停止後,系統銷毀。
  • A bound service
    service由其它組件呼叫bindService()產生,客戶端通過IBinder與service通訊,客戶端可以透過呼叫unbindService()關閉與service的連結。假設許多客戶端綁定在同一個service,當它們都不再與service連結時,系統就會銷毀service。
以下你可以練習來幫助了解生命週期

public class ExampleService extends Service {
    int mStartMode;       // indicates how to behave if the service is killed
    IBinder mBinder;      // interface for clients that bind
    boolean mAllowRebind; // indicates whether onRebind should be used

    @Override
    public void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return mStartMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return mAllowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}



星期二, 9月 20, 2011

繼承IntentService實作started service

Android應用程式學習筆記

繼承IntentService實作started service

之前已學習到started service有兩種實現方式,一種是繼承IntentService類別,另一種是繼承Service類別,今天就來實現一下繼承IntentService的started service。

1.首先開啟新專案HelloIntentService
2.main.xml產生兩個按鈕,一個是啟動service,一個是停止service
3.HelloIntentService的activity執行按鈕按下啟動service及停止service

啟動service:

Intent intent = new Intent(this , HelloIntentService.class);
    startService(intent);
停止service:

Intent intent = new Intent(this , HelloIntentService.class);
    stopService(intent);



4.在HelloIntentService/src目錄下新增.java文件


package test.hellointentservice;

import android.app.IntentService;
import android.content.Intent;
import android.os.IBinder;

public class HelloIntentService extends IntentService {

public HelloIntentService(){
super("IntentService");
}

//當service第一次被啟動時,系統第一個呼叫此方法,接著才會呼叫其他方法。
@Override
public void onCreate(){
super.onCreate();
System.out.println("Service--------->onCreate");
System.out.println("thread-------->" + Thread.currentThread().getId());
}

@Override
public void onStart(Intent intent , int startId){
super.onStart(intent, startId);
System.out.println("Service------->onStart");
System.out.println("thread-------->" + Thread.currentThread().getId());
}

@Override
protected void onHandleIntent(Intent intent){
System.out.println("Service--------->onHandleIntent");
System.out.println("intent.flag--------->" + intent.getFlags());
System.out.println("thread-------->" + Thread.currentThread().getId());
long endTime = System.currentTimeMillis() + 10*1000;
     while (System.currentTimeMillis() < endTime) {
         synchronized (this) {
             try {
                 wait(endTime - System.currentTimeMillis());
             } catch (Exception e) {
             }
         }
     }
}
//started形式的service所必須複寫的方法,組件呼叫startService()方法後,系統會自動呼叫此方法。
@Override
public  int onStartCommand(Intent intent , int flags , int startId){
System.out.println("service--------->onStartCommand");
System.out.println("thread-------->" + Thread.currentThread().getId());
return super.onStartCommand(intent, flags, startId);
}
//bound形式的service必須複寫的方法
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
System.out.println("Service---->onBind");
return null;
}
//當service銷毀時呼叫此方法。
@Override
public void onDestroy(){
super.onDestroy();
System.out.println("Service------->onDestroy");
}
}

IntentService執行時會產生新的thread來完成工作,因此我們就來試試是否如此。在每個方法都加上System.out.println("thread-------->" + Thread.currentThread().getId())來看看現在是哪一個執行續。
其中也要注意,我們之前說過能複寫onCreate()、onDestroy()、onStartCommand()等方法,但唯有onBind()方法不需要呼叫super類別,所以我們在複寫時都有加上super.類別。

執行結果如下

從中可以發現activity啟動intentservice的順序。
activity呼叫startService()啟動service ---> 系統呼叫onCreate()產生新的執行續 ---> 系統呼叫onStartCommand() ---> 呼叫onStart(),傳遞intent物件到onHanldeIntent() ---> 呼叫onHandleIntent(),執行service工作 ---> 工作完成,service銷毀,呼叫onDestroy()。

因為service第一次啟動,所以會先呼叫onCreate()方法。
給service執行的工作放在onHandleIntent()方法中。

另外你可以試試,在service工作還沒完成之前把activity關閉,看看service是不是會受到影響。
那我們發現是不會的,activity關閉後,service並沒有因此停止,而是繼續執行直到工作完成。

星期一, 9月 19, 2011

Service(七)

Android應用程式學習筆記

Service(七)

Bound Services
The Basics
Bound services實現了Service類別,並允許其他應用程式綁定它與它互動。service要提供binding,必須實現onBind()回呼方法,此方法會回傳IBinder物件,IBinder定義了客戶端與service之間互動的接口。

客戶端通過呼叫bindService()方法綁定service,當客戶端執行綁定後,客戶端必須提供ServiceConnection,監視與service的連結。bindService()方法會立即返回,但是當android系統產生客戶端與service之間的連結,系統會呼叫ServiceConnection上的onServiceConnected()方法來傳遞IBinder物件,使客戶端可以與service通訊。

許多客戶端可以同時與service連結,然而,系統只會在第一個客戶端綁定時呼叫你的service的onBind()方法來取得IBinder物件,系統接著直接傳送相同的IBinder物件給另外的客戶端,不會再次呼叫onBind()方法來取得IBinder物件。

當最後一個客戶端不再綁定service時,系統就會銷毀service。

當你實現bound service時,最重要的部分就是定義onBind()方法,有幾種方式能定義onBind()方法。Handler是Messenger的基礎,與客戶端分享IBninder(),允許客戶端用Messenge物件寄送指令給service,除此之外,客戶端也可以擁有自己的messenger,所以service能寄送messenge回去。

這是一個進程間互相溝通的簡單方式,因為messenger排所有的請求入單一的執行續中,所以你不需設計你的service為thread-safe。

Creating a Bound Service
當我們要實現一個service並且提供綁定的功能,你必須提供IBinder物件給客戶端作為與service互動的接口,有三種方式可以定義接口。

Extending the Binder class
如果你的service是私有的,只有自己的應用程式且在同一個進程作為客戶端,你應該藉著Binder類別來產生接口,且是從onBind()方法回傳接口,客戶端取得接口(Binder)後就可以直接使用不管是在Binder或者是service的public方法。

這樣的方式比較適合使用在當你的service作為你的應用程式的後台工作者時。如果你想要你的service被其他應用程式使用或者被不同進程存取,你就不應該使用這種方式來實現bound service。

Using a Messenger
如果你需要接口能存取其他進程,你能用Messenger來產生service的接口。在此方式,service必須定義Handler來回應不同型態的Messenger,

Using AIDL
(看不太懂android官網上的解釋,先跳過,之後再加上來)

Extending the Binder class
繼承binder類別建立bound service有三個步驟:

  1. 在service中,定義Binder:
    • 包含客戶端可以呼叫的public方法。
    • 或回傳當前的service,它有客戶端可以呼叫的方法。
  2. 從onBind()回乎方法回傳Binder物件。
  3. 在客戶端,從onServiceConnected()中取得Binder物件。


以下為一個service例子,它提供客戶端通過Binder呼叫它的方法。

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);
    }
}
LocalBinder提供getService()方法給客戶端去取得目前的LocalService物件,這樣可以允許客戶端呼叫在service中的public方法。例如,客戶端可以呼叫service的getRandomNumber()方法。

以下是一個activity要綁定LocalService並當按鈕被點擊後呼叫getRandomNumber()方法。

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;
        }
    };
}

Using a Messenger
如果你需要你的service與遠端的進程通訊,你可以使用Messenger物件為你的service提供接口。此技術允許你執行進程間通訊卻無需AIDL。

以下總結如何使用Messenger:
  • Service實作Handler物件來接收每一個從客戶端的呼叫。
  • Handler用來產生Messenger物件(必須引用Handler類別)。
  • Messenger產生新的IBinder物件,service從onBind()方法中回傳給客戶端。
  • 客戶端使用IBinder傳送Messenge給service來實現Messenger。
  • Service在它的Handler接收每個Messenge,特別是在handleMessenge()方法中。
在此方式中,客戶端沒有方法去呼叫service,取代的是客戶端傳送Messenge(Messenge物件),service的handler接收Messenge來啟動service。

以下有一個使用Messenge為接口的例子

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();
    }
}
注意在Handler中的handleMessenege()方法是service是接收傳入messenege及決定要執行甚麼的動作地方。

所有客戶端需要做的是基於回傳自service的IBinder以及用send()方法寄送messenge創建Messenger。以下有一個簡單的activity去綁定service,並且傳送MSG_SAY_HELLO訊息給service。

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;
        }
    }
}
注意此例並沒有顯示service如何回應客戶端,如果你想要你的service能回應客戶端,那你也需要在客戶端創造Messenger。接著當客戶端接收到onServiceConnected()的回呼時,它寄送messenge給service。

Binding to a Service
應用程式組件(客戶端)能通過呼叫bindService(0方法來綁定service,android系統接著呼叫service的onBind()方法,onBind()方法會回傳IBinder為了與service互動。

綁定是異步的,bindService(0立即回傳而沒有回傳IBinder給客戶端,為了接收IBinder,客戶端必須創建ServiceConnection並傳送它給bindService()。ServiceConnection包括系統呼叫來傳遞IBinder的回呼方法。

所以,從你的客戶端綁定service,你必須:
  1. 實現ServiceConnection。必須複寫兩個回呼方法:
    • onServiceConnected():
      系統呼叫此方法來傳遞回傳自service的onBind()方法的IBinder。
    • onServiceDisconnected():
      當與service連結在非預期情況下消失時,系統呼叫此方法。並不是在客戶端不再綁定service時呼叫。
  2. 呼叫bindService(),傳遞ServiceConnection。
  3. 當系統呼叫onSeviceConnected()回呼方法,你能開始呼叫service的方法。
  4. 從service解除連結,呼叫unbindService()。
例子:

LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Because we have bound to an explicit
        // service that is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    // Called when the connection with the service disconnects unexpectedly
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "onServiceDisconnected");
        mBound = false;
    }
};
用此ServiceConnection,客戶端將ServiceConnection通過bindService()方法綁定service。

Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
第一個參數是一個Intent物件,裡面明確地定義了service的名稱。
第二個參數是ServiceConnection物件。
第三個參數是綁定的選擇的旗標,