Android學習筆記(五三):服務Service(下)- Remote Service

來源:互聯網
上載者:User

之前所談的Service屬於Local Service,即Service和Client在同一進程內(即同一application內),Service的生命週期服從進程的生命週期。在實際應用上,有時希望Service作為後台服務,不僅被同一進程內的activity使用,也可被其他進程所使用,針對這種情況,需要採用bindService,也就是Remote Service的方式。

在Android中,不同app屬不同進程(process),進程是安全性原則的邊界,一個進程不能訪問其他進程的儲存(例如採用ContentProvider)。在Remote Service中將涉及處理序間通訊,也就是通常講的IPC(interprocess commnication),需要在進程A和進程B之間建立串連,以便進行相互的通訊或資料傳遞 。

Android提供AIDL(Android Interface Definition Language)工具協助IPC之間介面的建立,大大地簡化了開發人員視圖。右僅用於協助理解代碼。通過下面的步驟實現client和service之間的通訊:

【1】定義AIDL介面 ,Eclipse將自動為Service建立介面IService
【2】Client串連Service,串連到IService暴露給Client的Stub,獲得stub對象;換句話,Service通過介面中的Stub向client提供服務,在IService中對抽象IService.Stub具體實現。 
【3】Client和Service串連後,Client可向使用本地方法那樣,簡單地直接調用IService.Stub裡面的方法。

下面的例子給出client從提供定時計數的Remote Service,稱為TestRemoteService,中獲得服務的例子。

步驟1:通過AIDL檔案定義Service向client提供的介面,ITestRemoteService.aidl檔案如下

package com.wei.android.learning.part5;
interface ITestRemoteService {
    int getCounter();
}

我們在src的目錄下添加一個I<ServiceClassName>.aidl檔案,文法和java的相同。在這個例子中Service很簡單,只提供計數器的值,故在介面中我們定義了int getCounter( )。

AIDL檔案很簡單,Eclipse會根據檔案自動產生相關的一個java interface檔案,不過沒有顯示出來,如果直接使用命令列工具會協助產生java檔案。

步驟2:Remote Service的編寫,通過onBind(),在client串連時,傳遞stub對象。 TestRemoteService.java檔案如下:

/* Service提供一個定時計數器,採用Runnable的方式實現,複習一下Android學習筆記(三一):線程:Message和Runnable中的例子3。為了避免幹擾注意力,灰掉這部分代碼。此外,我們提供showInfo(),用於跟蹤Service的運行情況,這部分也灰掉。*/
public class TestRemoteService extends Service{
    private Handler serviceHandler = null;
    private int counter = 0;
    private TestCounterTask myTask = new TestCounterTask(); 
    
    public void onCreate() { 
        super.onCreate();
        showInfo("remote service onCreate()");
    } 

    public void onDestroy() {
        super.onDestroy();
        serviceHandler.removeCallbacks(myTask); //停止計數器
        serviceHandler = null;
        showInfo("remote service onDestroy()");
    } 

    public void onStart(Intent intent, int startId) {
       // 開啟計數器
        super.onStart(intent, startId);
        serviceHandler=new Handler();
        serviceHandler.postDelayed(myTask, 1000);
        showInfo("remote service onStart()");
    }

   //步驟2.1:具體實現介面中暴露給client的Stub,提供一個stub inner class來具體實現。 
    private ITestRemoteService.Stub stub= new ITestRemoteService.Stub() {
       //步驟2.1:具體實現AIDL檔案中介面的定義的各個方法。
        public int getCounter() throws RemoteException { 
            showInfo("getCounter()");
            return counter;
        }
    }; 
    
   //步驟2.2:當client串連時,將觸發onBind(),Service向client返回一個stub對象,由此client可以通過stub對象來訪問Service,本例中通過stub.getCounter()就可以獲得計時器的當前計數。在這個例子中,我們向所有的client傳遞同一stub對象。 
   public IBinder onBind(Intent arg0) { 
        showInfo("onBind() " + stub); //我們特別跟蹤了stub對象的地址,可以在client串連service中看看通過ServiceConnection傳遞給client
        return stub;
   } 

    /* 用Runnable使用定時計數器,每10秒計數器加1。 */
    private class TestCounterTask implements Runnable{
        public void run() { 
            ++ counter;
            serviceHandler.postDelayed(myTask,10000);
            showInfo("running " + counter);
        }
    } 
    /* showInfo( ) 協助我們進行資訊跟蹤,更好瞭解Service的運行情況 */
    private void showInfo(String s){ 
        System.out.println("[" +getClass().getSimpleName()+"@" + Thread.currentThread().getName()+ "] " + s);
    }
}

步驟3:Client和Service建立串連,獲得stub,ServiceTest4.java代碼如下

public class ServiceTest4 extends Activity{
    private ITestRemoteService remoteService = null;
//步驟3.1 定義介面變數
    private boolean isStarted = false;
    private CounterServiceConnection conn = null;   
//步驟3.1 定義串連變數,實現ServiceConnection介面
     
    protected void onCreate(Bundle savedInstanceState) { 
        … … //5個button分別觸發startService( ),stopService( ) , bindService( ), releaseService( )和invokeService( ),下面兩行,一行是顯示從Service中獲得的計數值,一行顯示狀態。    
    }

    private void startService(){
        Intent i = new Intent(); 
        i.setClassName("com.wei.android.learning", "com.wei.android.learning.part5.TestRemoteService"); //我的這個包裡面還有層次,如*.part1、*.part2,etc
        startService(i); //和之前的local service一樣,通過intent開啟Service,觸發onCreate()[if Service沒有開啟]->onStart()
        isStarted = true; 
        updateServiceStatus(); 
    }
    private void stopService(){
        Intent i = new Intent();
        i.setClassName("com.wei.android.learning","com.wei.android.learning.part5.TestRemoteService");
        stopService(i); //觸發Service的 onDestroy()[if Service存在]
        isStarted = false;
        updateServiceStatus();
    }   
   //步驟3.3:bindService( )通過一個實現ServiceConnection介面的類於Service之間建立串連,注意到裡面的參數Context.BIND_AUTO_CREATE,觸發onCreate()[if Service不存在] –> onBind()。
    private void bindService(){ 
        if(conn == null){
            conn = new CounterServiceConnection();
            Intent i = new Intent();
            i.setClassName("com.wei.android.learning","com.wei.android.learning.part5.TestRemoteService");
           bindService(i, conn,Context.BIND_AUTO_CREATE);
            updateServiceStatus();
        }
    }

    private void releaseService(){
        if(conn !=null){
            unbindService(conn); //中斷連線,解除綁定
            conn = null;
            updateServiceStatus();
        }
    }
    private void invokeService(){ 
        if(conn != null){
            try{
                Integer counter = remoteService.getCounter();
//一旦client成功綁定到Service,就可以直接使用stub中的方法。
                TextView t = (TextView)findViewById(R.id.st4_notApplicable);
                t.setText("Counter value : " + Integer.toString(counter));
            }catch(RemoteException e){
                Log.e(getClass().getSimpleName(),e.toString());
            }
        }
    }
   //步驟3.2 class CounterServiceConnection實現ServiceConnection介面,需要具體實現裡面兩個觸發onServiceConnected()和onServiceDisconnected() 
    private class CounterServiceConnection implements ServiceConnection{
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 從串連中獲得stub對象,根據我們的跟蹤,remoteService就是service中的stub對象 
            remoteService = ITestRemoteService.Stub.asInterface(service);
            showInfo("onServiceConnected()" + remoteService);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) { 
            remoteService = null;
            updateServiceStatus();
            showInfo("onServiceDisconnected");
        }
    }
    private void updateServiceStatus() { 
        TextView t = (TextView)findViewById( R.id.st4_serviceStatus);
        t.setText( "Service status: "+(conn == null ? "unbound" : "bound")+ ","+ (isStarted ? "started" : "not started"; ));     
      }
    private void showInfo(String s){ 
        System.out.println("[" +getClass().getSimpleName()+"@" + Thread.currentThread().getName()+ "] " + s);
    }
}

 相關連結:
我的Android開發相關文章

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.