Android四大組件之 Service

一    Service簡介php

        Service是運行在後臺的,沒有界面的,用來處理耗時比較長的。Service不是一個單獨的進程,不是一個單獨的線程。java

        Service有兩種類型:android

  1. 本地服務(Local Service):用於應用程序內部
  2. 遠程服務(Remote Sercie):用於android系統內部的應用程序之間
       

      前者用於實現應用程序本身的一些耗時任務,好比查詢升級信息,並不佔用應用程序好比Activity所屬線程,而是單開線程後臺執行,這樣用戶體驗比較 好。windows

      後者可被其餘應用程序複用,好比天氣預報服務,其餘應用程序不須要再寫這樣的服務,調用已有的便可。app

     

二  Service的生命週期eclipse

      Service有startService()和bindService()兩種啓動Service方法,每種方法Service的生命週期是不同的。ide

     1 經過startService()
       Service會經歷 onCreate --> onStartCommand()
       stopService的時候直接onDestroy

       若是是 調用者 直接退出而沒有調用stopService的話,Service會一直在後臺運行。
       下次調用者再起來仍然能夠stopService。this

    2 經過bindService()  
       Service只會運行onCreate()-->onBind()

      這個時候 調用者和Service綁定在一塊兒
       unbindService的時候  onUnbind()-->onDestroyed()
      調用者退出了,Srevice就會調用onUnbind()-->onDestroyed()
      所謂綁定在一塊兒就共存亡了。 spa

 

注意:Service的onCreate的方法只會被調用一次,
就是你不管多少次的startService又 bindService,Service只被建立一次。
若是先是bind了,那麼start的時候就直接運行Service的onStart方法,
若是先是start,那麼bind的時候就直接運行onBind方法。若是你先bind上了,就stop不掉了,
只能先UnbindService, 再StopService,因此是先start仍是先bind行爲是有區別的。

Android中的服務和windows中的服務是相似的東西,服務通常沒有用戶操做界面,它運行於系統中不容易被用戶發覺,可使用它開發如監控之類的 程序。

服務不能本身運行,須要經過調用Context.startService()或Context.bindService()方法啓動服務。
這兩個方法均可以啓動Service,可是它們的使用場合有所不一樣。使用startService()方法啓用服務,調用者與服務之間沒有關連,
即便調用者退出了,服務仍然運行。使用bindService()方法啓用服務,調用者與服務綁定在了一塊兒,調用者一旦退出,服務也就終止,大有「不求同 時生,必須同時死」的特色。

若是打算採用Context.startService()方法啓動服務,在服務未被建立時,系統會先調用服務的onCreate()方法,
接着調用onStart()方法。若是調用startService()方法前服務已經被建立,屢次調用startService()方法並不會致使屢次 建立服務,
但會致使屢次調用onStart()方法。採用startService()方法啓動的服務,只能調用Context.stopService()方法結 束服務,服務結束時會調用onDestroy()方法。

若是打算採用Context.bindService()方法啓動服務,在服務未被建立時,系統會先調用服務的onCreate()方法,
接着調用onBind()方法。這個時候調用者和服務綁定在一塊兒,調用者退出了,系統就會先調用服務的onUnbind()方法,
接着調用onDestroy()方法。若是調用bindService()方法前服務已經被綁定,
屢次調用bindService()方法並不會致使屢次建立服務及綁定(也就是說onCreate()和onBind()方法並不會被屢次調用)。
若是調用者但願與正在綁定的服務解除綁定,能夠調用unbindService()方法,調用該方法也會致使系統調用服務的 onUnbind()-->onDestroy()方法.   線程

 

 

三    代碼實例

編寫不需和Activity交互的本地服務示例

本地服務編寫比較簡單。首先,要建立一個Service類,該類繼承android的Service類。這裏寫了一個計數服務的類,每秒鐘爲計數器 加一。在服務類的內部,還建立了一個線程,用於實現後臺執行上述業務邏輯。

 

 

package com.easymorse;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class CountService extends Service {

   
private boolean threadDisable;

   
privateint count;

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

    @Override
   
public void onCreate() {
       
super.onCreate();
       
new Thread(new Runnable() {

            @Override
           
publicvoid run() {
               
while (!threadDisable) {
                   
try {
                        Thread.sleep(
1000);
                    }
catch (InterruptedException e) {
                    }
                    count
++;
                    Log.v(
"CountService","Count is " + count);
                }
            }
        }).start();
    }

    @Override
   
public void onDestroy() {
       
super.onDestroy();
       
this.threadDisable= true;
        Log.v(
"CountService","on destroy");
    }

   
public int getCount() {
       
return count;
    }

}

 須要將該服務註冊到配置文件AndroidManifest.xml中,不然沒法找到:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package
="com.easymorse" android:versionCode="1" android:versionName="1.0">
   
<applicationandroid:icon="@drawable/icon" android:label="@string/app_name">
       
<activityandroid:name=".LocalServiceDemoActivity"
            android:label
="@string/app_name">
           
<intent-filter>
               
<actionandroid:name="android.intent.action.MAIN"/>
               
<categoryandroid:name="android.intent.category.LAUNCHER"/>
           
</intent-filter>
       
</activity>
       
<serviceandroid:name="CountService"/>
   
</application>
   
<uses-sdkandroid:minSdkVersion="3"/>
</manifest/>

 

 

在Activity中啓動和關閉本地服務。

package com.easymorse;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

public class LocalServiceDemoActivityextends Activity {
   
/** Called when the activity is first created.*/
    @Override
   
public void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

       
this.startService(new Intent(this, CountService.class));
    }

    @Override
   
protectedvoid onDestroy() {
       
super.onDestroy();
       
this.stopService(new Intent(this, CountService.class));
    }
}

 

可經過日誌查看到後臺線程打印的計數內容。

編寫本地服務和Activity交互的示例

上面的示例是經過startService和stopService啓動關閉服務的。適用於服務和activity之間沒有調用交互的狀況。若是之 間須要傳遞參數或者方法調用。須要使用bind和unbind方法。

具體作法是,服務類須要增長接口,好比ICountService,另外,服務類須要有一個內部類,這樣能夠方便訪問外部類的封裝數據,這個內部類 須要繼承Binder類並實現ICountService接口。還有,就是要實現Service的onBind方法,不能只傳回一個null了。

這是新創建的接口代碼:

package com.easymorse;

public interface ICountService {
   
public abstract int getCount();
}

 

修改後的CountService代碼:

package com.easymorse;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class CountServiceextends Serviceimplements ICountService {

   
privateboolean threadDisable;

   
privateint count;

   
private ServiceBinder serviceBinder=new ServiceBinder();

   
public class ServiceBinderextends Binderimplements ICountService{
        @Override
       
publicint getCount() {
           
return count;

        }

    }

    @Override

    public IBinder onBind(Intent intent) {
       
return serviceBinder;
    }

    @Override
   
public void onCreate() {
       
super.onCreate();
       
new Thread(new Runnable() {

            @Override
           
publicvoid run() {
               
while (!threadDisable) {
                   
try {
                        Thread.sleep(
1000);
                    }
catch (InterruptedException e) {
                    }
                    count
++;
                    Log.v(
"CountService","Count is " + count);
                }
            }
        }).start();
    }

    @Override
   
public void onDestroy() {
       
super.onDestroy();
       
this.threadDisable= true;
        Log.v(
"CountService","on destroy");
    }

   
/* (non-Javadoc)
     * @see com.easymorse.ICountService#getCount()
    
*/
   
public int getCount() {
       
return count;
    }

}

 

服務的註冊也要作改動,AndroidManifest.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package
="com.easymorse" android:versionCode="1" android:versionName="1.0">
   
<applicationandroid:icon="@drawable/icon" android:label="@string/app_name">
       
<activityandroid:name=".LocalServiceDemoActivity"
            android:label
="@string/app_name">
           
<intent-filter>
               
<actionandroid:name="android.intent.action.MAIN"/>
               
<categoryandroid:name="android.intent.category.LAUNCHER"/>
           
</intent-filter>
       
</activity>
       
<serviceandroid:name="CountService">
           
<intent-filter>
               
<actionandroid:name="com.easymorse.CountService"/>
           
</intent-filter>
       
</service>
   
</application>
   
<uses-sdkandroid:minSdkVersion="3"/>
</manifest>

 

Acitity代碼再也不經過startSerivce和stopService啓動關閉服務,另外,須要經過ServiceConnection的 內部類實現來鏈接Service和Activity。

package com.easymorse;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

public class LocalServiceDemoActivity extends Activity {

   
private ServiceConnection serviceConnection= new ServiceConnection() {

        @Override
       
publicvoid onServiceConnected(ComponentName name, IBinder service) {
            countService
= (ICountService) service;
            Log.v(
"CountService","on serivce connected, count is"
                   
+ countService.getCount());
        }

        @Override
       
publicvoid onServiceDisconnected(ComponentName name) {
            countService
=null;
        }

    };

   
private ICountService countService;

   
/** Called when the activity is first created.*/
    @Override
   
public void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
this.bindService(new Intent("com.easymorse.CountService"),
               
this.serviceConnection, BIND_AUTO_CREATE);
    }

    @Override
   
protectedvoid onDestroy() {

          this.unbindService(serviceConnection);       

          super.onDestroy();      //注意前後
    }
}

 

編寫傳遞基本型數據的遠程服務

上面的示例,能夠擴展爲,讓其餘應用程序複用該服務。這樣的服務叫遠程(remote)服務,其實是進程間通訊(RPC)。

這時須要使用android接口描述語言(AIDL)來定義遠程服務的接口,而不是上述那樣簡單的java接口。擴展名爲aidl而不是java。 可用上面的ICountService改動而成ICountSerivde.aidl,eclipse會自動生成相關的java文件。

package com.easymorse;

interface ICountService {
   
int getCount();
}

 

編寫服務(Service)類,稍有差異,主要在binder是經過遠程得到的,須要經過樁(Stub)來獲取。樁對象是遠程對象的本地代理。

package com.easymorse;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class CountService extends Service {

   
privateboolean threadDisable;

   
privateint count;

   
private ICountService.Stub serviceBinder= new ICountService.Stub() {

        @Override
       
publicint getCount()throws RemoteException {
           
return count;
        }
    };

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

    @Override
   
public void onCreate() {
       
super.onCreate();
       
new Thread(new Runnable() {

            @Override
           
publicvoid run() {
               
while (!threadDisable) {
                   
try {
                        Thread.sleep(
1000);
                    }
catch (InterruptedException e) {
                    }
                    count
++;
                    Log.v(
"CountService","Count is " + count);
                }
            }
        }).start();
    }

    @Override
   
public void onDestroy() {
       
super.onDestroy();
       
this.threadDisable= true;
        Log.v(
"CountService","on destroy");
    }
}

 

配置文件AndroidManifest.xml和上面的相似,沒有區別。

在Activity中使用服務的差異不大,只須要對ServiceConnection中的調用遠程服務的方法時,要捕獲異常。

private ServiceConnection serviceConnection= new ServiceConnection() {

    @Override
   
public void onServiceConnected(ComponentName name, IBinder service) {
        countService
= (ICountService) service;
       
try {
            Log.v(
"CountService","on serivce connected, count is"
                   
+ countService.getCount());
        }
catch (RemoteException e) {
           
thrownew RuntimeException(e);
        }
    }

    @Override
   
public void onServiceDisconnected(ComponentName name) {
        countService
=null;
    }

};

 

這樣就能夠在同一個應用程序中使用遠程服務的方式和本身定義的服務交互了。

若是是另外的應用程序使用遠程服務,須要作的是複製上面的aidl文件和相應的包構到應用程序中,其餘調用等都同樣。

編寫傳遞複雜數據類型的遠程服務

遠程服務每每不僅是傳遞java基本數據類型。這時須要注意android的一些限制和規定:

  1. android支持String和CharSequence
  2. 若是須要在aidl中使用其餘aidl接口類型,須要 import,即便是在相同包結構下;
  3. android容許傳遞實現Parcelable接口的類,須要import;
  4. android 支持集合接口類型List和Map,可是有一些限制,元素必須是基本型或者上述三種狀況,不須要import集合接口類,可是須要對元素涉及到的類型 import;
  5. 非基本數據類型,也不是String和CharSequence類型的,須要有方向指示,包括in、out和 inout,in表示由客戶端設置,out表示由服務端設置,inout是二者都可設置。

這裏將前面的例子中返回的int數據改成複雜數據類型:

package com.easymorse;

import android.os.Parcel;
import android.os.Parcelable;

public class CountBean implements Parcelable {

   
public static final Parcelable.Creator<CountBean> CREATOR= new Creator<CountBean>() {

        @Override
       
public CountBean createFromParcel(Parcel source) {
            CountBean bean
=new CountBean();
            bean.count
= source.readInt();
           
return bean;
        }

        @Override
       
public CountBean[] newArray(int size) {
           
returnnew CountBean[size];
        }

    };

   
public int count;

    @Override
   
public void writeToParcel(Parcel dest,int flags) {
        dest.writeInt(
this.count);
    }

    @Override
   
public int describeContents() {
       
return;
    }

}

 

而後,須要在相同包下建一個同名的aidl文件,用於android生成相應的輔助文件:

package com.easymorse;

parcelable CountBean;

 

這一步是android 1.5後的變化,沒法經過adt生成aidl,也不能用一個好比全局的project.aidl文件,具體見:

http://www.anddev.org/viewtopic.php?p=20991

而後,須要在服務的aidl文件中修改以下:

package com.easymorse;

import com.easymorse.CountBean;

interface ICountService {
    CountBean getCount();
}

 

其餘的改動很小,只需將CountService和調用CountService的部分修改成使用CountBean便可

相關文章
相關標籤/搜索