Service詳解

Service詳解

Service作用

  Service是一個不提供用戶交互的可以在後臺長期執行操作的應用程序組件。其它的應用程序組件可以啓動Service,當切換到其他應用程序,該service會在後臺繼續運行。其他組件也可以綁定到service與其交互,甚至可以執行進程間通信。

Service的兩種形式

  • 開始狀態
    當一個應用程序組件(例如Activity)通過調用startService()啓動一個Service後,它就處於“開始狀態”。一旦處於“開始狀態”,一個Service可以無限期地在後臺運行,甚至當啓動它的組件已經被銷燬。通常,一個“開始狀態”的Service會執行一個單獨的操作,不會返回結果給調用者。例如,通過網絡上傳或下載文件。當操作完成,這個Service應該停止它自己。
  • 綁定狀態
    當一個應用程序組件通過調用bindService()綁定到一個Service,則它處於“綁定狀態”。處於“綁定狀態”的Service提供了client-server接口,其允許這些組件可以與該Service執行諸如發送請求,獲取結果,進程間通信等交互。一個處於“綁定狀態”的Service僅和綁定到的應用程序組件運行一樣長的時間。多個組件可以每個綁定到該Service一次,但當所有這些組件與該Service解綁後,該Service纔會被銷燬。

注意:你的Service可以同時工作在兩種形式。

在manifest中聲明service

  像其他應用程序組件一樣,所有的Service必須在manifest中進行聲明。

聲明代碼
<manifest ... >
  ...
  <application ... >
      <service android:enabled=["true" | "false"]
         android:exported=["true" | "false"]
         android:icon="drawable resource"
         android:isolatedProcess=["true" | "false"]
         android:label="string resource"
         android:name="string"
         android:permission="string"
         android:process="string" >
    . . .
</service>
      ...
  </application>
</manifest>
聲明說明
  • android:name屬性是唯一必須的屬性,它具體了Service的類名。一旦發佈了應用程序,你不應該再更改這個名字,如果更改,可能導致代碼出錯,因爲你的代碼依賴明確的intents去啓動或綁定到Service。
  • To ensure your app is secure, always use an explicit intent when starting or binding your Service and do not declare intent filters for the service. If it’s critical that you allow for some amount of ambiguity as to which service starts, you can supply intent filters for your services and exclude the component name from the Intent, but you then must set the package for the intent with setPackage(), which provides sufficient disambiguation for the target service.
聲明屬性
android:enabled
  • 該屬性設置爲“true”,則該Service可以被實例化並被使用,否則,該Service不可用。該屬性默認爲“true”。
  • <application>元素也有enabled屬性,它作用於所有的應用程序組件。
android:exported
  • 該屬性設置爲“true”,則其他應用的組件可以觸發該Service或與它交互,如果設置爲“false”,則只有當前應用或有相同用戶id的應用程序纔可以啓動或綁定到該Service。
  • 該屬性默認值依賴於該Service是否包含intent filters。沒有任何的intent filters意味着該Service只能被使用具體的類名觸發。該種情況只限於應用程序內部使用(其他應用程序不能知道具體類名)。所以這種情況,默認值爲“false”。另一方面,只要有至少有一個filter爲外部所使用,則默認值爲“true”。
  • 該屬性不是唯一限制其他應用使用該Service的方式。可以使用權限來限制與該Service交互的實體。
android:icon

設置代表該Service的一個圖標。如果沒有設置則使用 <application>元素設置的icon屬性。

android:isolatedProcess

該屬性設置爲“true”,則該Service會在一個特殊的進程下運行,該進程與其他系統進程分開,且沒有自己的權限。與其通信的唯一方式就是通過Service API。

android:label

一個展示給用戶的Service名字。如果沒有設置則使用 <application>元素設置的label屬性。

android:name
  • Service的名字,它必須是一個具體的類名。
  • 一旦你發佈了應用程序,不應該再改變這個名字(除非你已經設置了android:exported="false")。
  • 這個名字沒有默認值,它必須被定義。
android:permission
  • 設置一個要創建或綁定到該Service的實體必須具有的權限的名字。如果startService()bindService(),或stopService()的調用者沒有被授予這個權限,則這些方法不能起效並且Intent對象不能傳遞到該Service。
  • 如果這個屬性沒有設置,則使用 <application>元素設置的permission屬性。如果都沒有設置,則該Service不受任何權限保護。
android:process
  • 設置該Service運行在的進程名。正常的,應用程序的所有組件運行在爲該應用創建的一個默認進程中。它的名字與該應用的包名相同。<application>元素的process屬性能爲所有的組件設置不同的默認名字。各組件可以用它們自己的process屬性覆蓋這個默認值,這允許你將你的應用分爲幾個進程。
  • 如果指定到這個屬性的名字以冒號(:)開始,則這個新進程對該應用是私有的,它在需要時被創建且該Service運行在這個進程中。如果這個進程名以小寫字符開始,則這個Service運行在一個以這個名字命名的全局進程中。這允許不同應用程序的組件共享這個進程,以減少資源的使用。

Service生命週期

  Service的生命週期和activity的生命週期相似,但更簡單。然而,要更關注Service的創建和銷燬,因爲Service運行在後臺不能被用戶注意到。
  Service的生命週期可以追尋兩種不同的路徑:

  • “開始狀態”Service
    當其他組件調用startService()方法時,該Service被創建。該Service會無限期地運行,可以調用stopSelf()方法終止它。啓動它的組件,也可以調用stopService()方法來終止該Service。當該Service停止,系統就會銷燬它…
  • “綁定狀態”Service
    當其他組件(client)調用bindService()方法時,該Service被創建。接下來該client可以通過IBinder接口與該Service通信。該Service能夠調用unbindService()方法關閉連接。多個clients可以綁定到同一個Service,當所有它們解綁,系統可以銷燬該Service。(該Service不需要終止它自己)
    這兩條路徑不是完全分離的。當已調用startService()方法啓動Service後,還可以綁定到該Service。例如,一個後臺音樂Service可以先通過調用startService()方法啓動播放音樂。之後,當用戶可能需要對播放器進行控制或想獲取當前播放歌曲信息的時候,一個activity可以通過調用bindService()方法綁定到該Service。在這種情況下,調用stopService()stopSelf()方法不能終止該Service,直到所以的clients與該Service解綁。
Service生命週期回調(需要深入研究)
public class ExampleService extends Service {

    private static final String NAME = ExampleService.class.getSimpleName();
    private static final String TAG = "sxd";

    int mStartMode;       //指明該Service被殺死後的行爲表現
    IBinder mBinder;      //clients綁定接口
    boolean mAllowRebind; //指明onRebind是否應該被使用

    @Override
    public void onCreate() {
        //該Service被首次創建時調用
        Log.i(TAG, NAME + "--onCreate");
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, NAME + "--onStartCommand");
        //該Service正在運行,client再次調用startService()時調用
        return mStartMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, NAME + "--onBind");
        //client調用bindService()綁定到該Service時調用
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, NAME + "--onUnbind");
        //所有已綁定到該Service的client調用unbindService()與該Service解綁時調用
        return mAllowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        Log.i(TAG, NAME + "--onRebind");
        //當onUnbind()被調用後,又有新的client調用bindService()來綁定該Service時調用
    }
    @Override
    public void onDestroy() {
        Log.i(TAG, NAME + "--onDestroy");
        //該Service不再使用,銷燬時調用
    }

}

注意:不像activity的生命週期回調方法,你不被要求去調用這些回調方法的父類實現。

Service生命週期圖解

這裏寫圖片描述
圖:左側展示了調用startService()方法創建的Service的生命週期,右側展示了調用bindService()方法創建的Service的生命週期。

管理”綁定狀態”Service的生命週期

  當一個Service從所有的clients解綁,系統就會銷燬它(除非它也調用了onStartCommand()方法啓動)。如果一個Service是一個純淨的”綁定狀態”Service,則你不需要管理它的生命週期-Android系統基於是否還綁定了其他clients來管理它。
  然而,如果你選擇實現onStartCommand()回調方法,那麼你必須明確地終止該Service,因爲這個Service被考慮爲”開始狀態”Service。這種情況下,該Service運行直到調用了stopSelf()stopService()方法,不管它是否綁定了其他clients。
  如果你的Service處於”開始狀態”且接受綁定,如果你想在下次client綁定到該Service時,收到onRebind()方法回調,則當系統調用onUnbind()方法時,應該返回true。onRebind()方法返回void,但該client仍會在onServiceConnected()方法中收到IBinder。下面是這種邏輯的生命週期圖解:
這裏寫圖片描述

權限

  當在manifest中使用<service>標籤聲明Service時,可以施加該Service的全局訪問權限。這樣做,其他應用需要在它們自己的manifest中聲明相應的<uses-permission>元素以能夠啓動,終止或綁定到該Service。
  GINGERBREAD(Android 2.3)中,當使用Context.startService(Intent)時,你能夠在Intent中設置Intent.FLAG_GRANT_READ_URI_PERMISSIONIntent.FLAG_GRANT_WRITE_URI_PERMISSION。這可以授予該Service對在Intent中的特定URIs臨時的訪問權限。訪問持續到該Service調用了stopSelf(int)方法或直到該Service被完全終止。該方式對其他沒有請求受保護Service權限的應用或該Service完全沒有暴露的授權訪問同樣有用。
  一個Service通過權限保護獨立IPC對它的調用。通過在執行這個調用之前調用checkCallingPermission(String)方法檢測是否具有訪問權限。

Service進程生命週期

Android進程優先級
  1. 前臺進程( FOREGROUND_APP)
  2. 可視進程(VISIBLE_APP )
  3. 次要服務進程(SECONDARY_SERVER )
  4. 後臺進程 (HIDDEN_APP)
  5. 內容供應節點(CONTENT_PROVIDER)
  6. 空進程(EMPTY_APP)

一個Service已經啓動或有clients綁定到它,則系統會嘗試保持該Service的宿主進程。當運行在低內存狀態需要殺死已存在進程時,下面情況的Service宿主進程具有較高的優先級:

  • 如果Service正在onCreate(), onStartCommand()onDestroy()方法中執行代碼,則該宿主進程被作爲前臺進程以確保代碼執行而不被殺掉。
  • 如果一個Service已經被啓動,則它的宿主進程比任何對用戶可見的進程的優先級低,但比任何不可見的進程優先級高。因爲,只有少數的進程對用戶可見,這意味着該Service不會被殺死除非在低內存狀態下。然而,由於用戶不能直接感知到後臺Service,這種狀態其被考慮爲可以被殺死的候補者,你應該爲這種情況的發生做好準備。實際上,長時間運行的Services會增加被殺死的可能性。
  • 如果有clients綁定到了Service,則該Service的宿主進程至少和最重要的client同等重要。如果這些clinets中有一個對用戶可見,則該Service被考慮爲對用戶可見。clients重要程度影響service重要程度的方式可以通過BIND_ABOVE_CLIENTBIND_ALLOW_OOM_MANAGEMENTBIND_WAIVE_PRIORITYBIND_IMPORTANTBIND_ADJUST_WITH_ACTIVITY來調整。
  • “開始狀態”的Service能夠使用startForeground(int, Notification)方法將該Service設置爲前臺狀態,這種情況下,系統認爲它是用戶可以感知到的,因此在低內存狀態不會被作爲殺掉的候選(理論上,該Service還是會在面對前臺應用對內存的極度要求的壓力下被殺死,但這是可以不用關心的)。
  • 有其他組件運行在Service進程能夠提高該Service宿主進程的優先級。
    注意這意味着在大部分時間裏你的Service是可以正常運行的,但在內存極度緊缺的情況下,它會被系統殺死。如果這發生了,系統隨後會嘗試重啓該Service。一個重要的效果是,如果你實現了onStartCommand()方法去安排一個在另一個線程或異步執行的任務,那麼你可能想要使用START_FLAG_REDELIVERY以讓系統重傳一個Intent給你以使它不會在被處理時該Service被殺死導致其丟失。

onStartCommand()的返回值(需要深入研究)

  注意onStartCommand()必須返回一個整形值。這個整型值描述了當Service被殺死後,系統應該怎樣繼續該Service。這個返回值必須是下面各常量中的一個:

  • START_NOT_STICKY
    如果系統在onStartCommand()方法返回後殺死了該Service,則不會再重新創建該Service,除非有pending intents傳遞給它。對於避免你的Service在不必要的時候運行和你的應用能夠簡單重啓未完成的工作的情況,這是一個安全的選項。
  • START_STICKY
    如果系統在onStartCommand()方法返回後殺死了該Service,則會重新創建該Service並調用onStartCommand()方法,但不會重傳最後的Intent。系統會調用onStartCommand()附帶null的intent,除非有pending intent去啓動該Service,在這種情況下,會傳遞這些intents。
  • START_REDELIVER_INTENT
    如果系統在onStartCommand()方法返回後殺死了該Service,則會重新創建該Service並調用onStartCommand()方法附帶最後傳遞到該Service的Intent。任何pending intents是被輪流地傳遞。

Service主要方法詳解

  • public void onCreate ()
    該Service被第一次創建時調用,不要直接調用該方法。
  • public void onDestroy ()
    該Service不再使用,或被移除時調用。
  • public int onStartCommand (Intent intent, int flags, int startId)
    每次調用startService(Intent)方法時,該方法都會被調用。
    參數:
    intent:該Intent由startService(Intent)方法提供。如果該Service被殺死後重啓,且之前返回了除START_STICKY_COMPATIBILITY之外的任何狀態,則重啓後該Intent爲null。
    flags:啓動請求的附加數據。當可取值爲爲0START_FLAG_REDELIVERYSTART_FLAG_RETRY
    startId:一個唯一的整型值代表了特定的啓動請求。用於stopSelfResult(int)來終止特定Service。
    返回值描述了當Service被殺死後,系統應該怎樣繼續該Service
  • public abstract IBinder onBind (Intent intent)
    返回與該Service的通信渠道。如果clients不用綁定到該Service,則返回null。這個返回的IBinder,通常是用aidl描述的負責接口。
    參數intent是通過Context.bindService方法傳遞過來,用來綁定到該Service的。
    注意: any extras that were included with the Intent at that point will not be seen here.
  • public boolean onUnbind (Intent intent)
    所有已綁定到該Service的client調用unbindService()與該Service解綁時調用
  • public void onRebind (Intent intent)
    當onUnbind()被調用後,又有新的client調用bindService()來綁定該Service時調用
  • public void onConfigurationChanged (Configuration newConfig)
    當組件正在運行且設備配置發生改變時調用。
  • public void onLowMemory ()
  • public void onTrimMemory (int level)
    當系統決定到了釋放不需要內存的良好時機時調用。
    level的可能取值爲TRIM_MEMORY_COMPLETE, TRIM_MEMORY_MODERATE, TRIM_MEMORY_BACKGROUND, TRIM_MEMORY_UI_HIDDEN, TRIM_MEMORY_RUNNING_CRITICAL, TRIM_MEMORY_RUNNING_LOW, TRIM_MEMORY_RUNNING_MODERATE
  • public void onTaskRemoved (Intent rootIntent)
    當該Service正在運行且用戶移除了源自該Service應用程序的任務時調用。如果你設置了ServiceInfo.FLAG_STOP_WITH_TASK則你將不會接收到該回調。該Service將被簡單的終止。
    rootIntent:被用來創建被移除任務的原始root intent。
  • public final void startForeground (int id, Notification notification)
    使該Service運行在前臺,當處於該狀態時,提供了向用戶展示正在運行的通知。
  • public final void stopForeground (boolean removeNotification)
    移除該Service的前臺狀態。
  • public final void stopSelf ()
    終止正在運行的Service
  • public final void stopSelf (int startId)
    stopSelfResult(int)的老版本
  • public final boolean stopSelfResult (int startId)
    終止最近一次由該startId被啓動的Servive。

IntentService詳解

  IntentService是Service的子類,用來處理異步請求。Clients通過調用startService(Intent)方法發送請求。該Service在需要時啓動,用工作線程輪流處理每一個Intent,完成工作後終止自己。
  “工作隊列處理器”模式常用來從應用程序主線程卸載任務。IntentService類簡化了這個模式。爲了使用它,繼承IntentService類並實現onHandleIntent(Intent)方法。IntentService會接受Intents,創建工作線程,並在適當的時候終止該Service。
  所有請求在一個單獨的工作線程中處理,每次只能處理一個請求。
  相對Service,IntentService做了如下處理:

  • 創建默認的工作線程來執行所有傳遞到onStartCommand()的intents,以將處理工作從應用程序主線程中分離出去。
  • 創建一個工作隊列來向onHandleIntent()每次傳遞一個intent,所以你不需要擔心多線程問題。
  • 當所有請求被處理完後,服務自動結束,不需要再調用stopSelf()方法結束服務。
  • 提供了返回值爲null的默認的onBind()方法。
  • 提供了默認的onStartCommand()方法,以發送intent到工作隊列,然後到onHandleIntent()方法。
IntentService主要方法
  • public int onStartCommand (Intent intent, int flags, int startId)
    使用IntentService時,該方法不需要被重載。
  • public void setIntentRedelivery (boolean enabled)
    設置intent重傳行爲。通常根據需要在構造函數中調用。
    如果設置爲true,onStartCommand(Intent, int, int)方法返回START_REDELIVER_INTENT,這時如果進程在onHandleIntent(Intent)方法返回前死掉,則進程會被重啓並重新傳遞intent。如果有多個intent已經被傳遞,則只有最近傳遞的一個intent能夠保證被重傳。
    如果設置爲false(默認),onStartCommand(Intent, int, int)方法返回START_NOT_STICKY,這時如果進程死掉,則intent不會再被重傳。
  • protected abstract void onHandleIntent (Intent intent)
    該方法在工作線程中被觸發,以執行每個請求任務。
IntentService使用示例
IntentService基本使用示例
public class MyIntentService extends IntentService {

    private static final String NAME = MyIntentService.class.getSimpleName();
    private static final String TAG = "sxd";

    public static final String INTENT_KEY = "intent_key";

    public MyIntentService() {
        super(NAME);
    }

    @Override
    public void onCreate() {
        Log.i(TAG, NAME + "--onCreate");
        Log.i(TAG, NAME + "--onCreate++currentThread++id:" + Thread.currentThread().getId());
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, NAME + "--onStartCommand");
        Log.i(TAG, NAME + "--onStartCommand++currentThread++id:" + Thread.currentThread().getId());
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.i(TAG, NAME + "--onHandleIntent++begin:" + intent.getStringExtra(INTENT_KEY));
        Log.i(TAG, NAME + "--onHandleIntent++currentThread++id:" + Thread.currentThread().getId());
        try {
            Thread.sleep(5 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.i(TAG, NAME + "--onHandleIntent++end:" + intent.getStringExtra(INTENT_KEY));
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, NAME + "--onDestroy");
        Log.i(TAG, NAME + "--onDestroy++currentThread++id:" + Thread.currentThread().getId());
        super.onDestroy();
    }

}
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String NAME = MainActivity.class.getSimpleName();
    private static final String TAG = "sxd";

    private Button mStartService;
    private Button mStopService;
    private Button mBindService;
    private Button mUnbindService;

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

    private void initView() {
        mStartService = (Button) this.findViewById(R.id.start_service);
        mStartService.setOnClickListener(this);
        mStopService = (Button) this.findViewById(R.id.stop_service);
        mStopService.setOnClickListener(this);
        mBindService = (Button) this.findViewById(R.id.bind_service);
        mBindService.setOnClickListener(this);
        mUnbindService = (Button) this.findViewById(R.id.unbind_service);
        mUnbindService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start_service:
                startService();
                break;
            case R.id.stop_service:
                stopService();
                break;
            /*case R.id.bind_service:
                bindService();
                break;
            case R.id.unbind_service:
                unbindService();
                break;*/
        }
    }

    private void startService() {
        Log.i(TAG, NAME + "--startService++mainThread++id:" + Thread.currentThread().getId());
        Intent intent = new Intent(this, MyIntentService.class);
        intent.putExtra(MyIntentService.INTENT_KEY, "1");
        startService(intent);
        intent.putExtra(MyIntentService.INTENT_KEY, "2");
        startService(intent);
        intent.putExtra(MyIntentService.INTENT_KEY, "3");
        startService(intent);
    }

    private void stopService() {
        Intent intent = new Intent(this, MyIntentService.class);
        stopService(intent);
    }

    /*private void bindService() {
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    private void unbindService() {
        unbindService(mConnection);
    }*/

}

該示例程序,點擊“啓動服務”按鈕,執行結果如下圖
這裏寫圖片描述
執行結果證明:

  • onHandleIntent(Intent)方法確實是在獨立線程運行。
  • Intent請求確實是被逐個處理,且使用相同的工作線程處理。
  • Service確實是在所有請求處理完成後,自動結束。

該示例程序,點擊“啓動服務”按鈕後,在所有請求未處理完成時,點擊“停止服務”按鈕,執行結果如下圖
這裏寫圖片描述
觀察執行結果可知,當主動停止Service後,Service立即終止,但當前正在處理的任務不會立即終止,而是執行完成後終止,由於Service終止,則後續任務不會再被進行傳遞處理。

使用Service實現IntentService功能
public class ImitateIntentService extends Service {

    private static final String NAME = ImitateIntentService.class.getSimpleName();
    private static final String TAG = "sxd";

    public static final String INTENT_KEY = "intent_key";

    private Looper mServiceLooper;
    private ServiceHandler mServiceHandler;

    // Handler that receives messages from the thread
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            Bundle bundle = msg.getData();
            Log.i(TAG, NAME + "--ServiceHandler++begin:" + bundle.getString(INTENT_KEY));
            Log.i(TAG, NAME + "--ServiceHandler++currentThread++id:" + Thread.currentThread().getId());
            // Normally we would do some work here, like download a file.
            // For our sample, we just sleep for 5 seconds.
            try {
                Thread.sleep(5 * 1000);
            } catch (InterruptedException e) {
                // Restore interrupt status.
                Thread.currentThread().interrupt();
            }
            // Stop the service using the startId, so that we don't stop
            // the service in the middle of handling another job
            Log.i(TAG, NAME + "--ServiceHandler++end:" + bundle.getString(INTENT_KEY));
            stopSelf(msg.arg1);
        }
    }

    @Override
    public void onCreate() {
        Log.i(TAG, NAME + "--onCreate");
        Log.i(TAG, NAME + "--onCreate++currentThread++id:" + Thread.currentThread().getId());
        // Start up the thread running the service.  Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block.  We also make it
        // background priority so CPU-intensive work will not disrupt our UI.
        HandlerThread thread = new HandlerThread(NAME, Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();

        // Get the HandlerThread's Looper and use it for our Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, NAME + "--onStartCommand");
        Log.i(TAG, NAME + "--onStartCommand++currentThread++id:" + Thread.currentThread().getId());

        // For each start request, send a message to start a job and deliver the
        // start ID so we know which request we're stopping when we finish the job
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        Bundle bundle = new Bundle();
        bundle.putString(INTENT_KEY, intent.getStringExtra(INTENT_KEY));
        msg.setData(bundle);
        mServiceHandler.sendMessage(msg);

        // If we get killed, after returning from here, restart
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, NAME + "--onBind");
        Log.i(TAG, NAME + "--onBind++currentThread++id:" + Thread.currentThread().getId());
        // We don't provide binding, so return null
        return null;
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, NAME + "--onDestroy");
        Log.i(TAG, NAME + "--onDestroy++currentThread++id:" + Thread.currentThread().getId());
    }
}

該示例程序使用Service和Handler,實現了IntentService功能。
該示例程序,點擊“啓動服務”按鈕,執行結果如下圖
這裏寫圖片描述
執行結果和IntentService示例完全一樣。

Service使用示例

本地Service的使用示例
public class MyService extends Service {

    private static final String NAME = MyService.class.getSimpleName();
    private static final String TAG = "sxd";

    private MyBinder mBinder = new MyBinder();

    @Override
    public void onCreate() {
        //該Service被首次創建時調用
        Log.i(TAG, NAME + "--onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, NAME + "--onStartCommand");
        //該Service正在運行,client再次調用startService()時調用
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, NAME + "--onBind");
        //client調用bindService()綁定到該Service時調用
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, NAME + "--onUnbind");
        //所有已綁定到該Service的client調用unbindService()與該Service解綁時調用
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        Log.i(TAG, NAME + "--onRebind");
        //當onUnbind()被調用後,又有新的client調用bindService()來綁定該Service時調用
        super.onRebind(intent);
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, NAME + "--onDestroy");
        //該Service不再使用,銷燬時調用
        super.onDestroy();
    }

    class MyBinder extends Binder {

        public void doSomething() {
            Log.i(TAG, NAME + "--MyBinder++doSomething()");
        }

    }

}
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String NAME = MainActivity.class.getSimpleName();
    private static final String TAG = "sxd";

    private Button mStartService;
    private Button mStopService;
    private Button mBindService;
    private Button mUnbindService;

    private MyService.MyBinder myBinder;

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

    private void initView() {
        mStartService = (Button) this.findViewById(R.id.start_service);
        mStartService.setOnClickListener(this);
        mStopService = (Button) this.findViewById(R.id.stop_service);
        mStopService.setOnClickListener(this);
        mBindService = (Button) this.findViewById(R.id.bind_service);
        mBindService.setOnClickListener(this);
        mUnbindService = (Button) this.findViewById(R.id.unbind_service);
        mUnbindService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start_service:
                startService();
                break;
            case R.id.stop_service:
                stopService();
                break;
            case R.id.bind_service:
                bindService();
                break;
            case R.id.unbind_service:
                unbindService();
                break;
        }
    }

    private void startService() {
        Intent intent = new Intent(this, MyService.class);
        startService(intent);
    }

    private void stopService() {
        Intent intent = new Intent(this, MyService.class);
        stopService(intent);
    }

    private void bindService() {
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    private void unbindService() {
        unbindService(mConnection);
    }

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, NAME + "--onServiceDisconnected++name:" + name.getClassName());
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, NAME + "--onServiceConnected++name:" + name.getClassName());
            myBinder = (MyService.MyBinder) service;
            myBinder.doSomething();
        }
    };

}
遠程Service的使用示例
應用程序內使用遠程Service示例

IMyServiceAIDL.aidl文件

interface IMyServiceAIDL {

    int add(int a, int b);

}

  遠程Service運行在獨立進程中,Activity等組件要與其通信,則需要使用AIDL來實現跨進程通信。AIDL是Android接口定義語言。創建.aidl文件,定義需要進行的操作。.aidl文件編譯後,會自動生成相應的.java文件。

MyService.java文件

public class MyService extends Service {

    private static final String NAME = MyService.class.getSimpleName();
    private static final String TAG = "sxd";

    @Override
    public void onCreate() {
        //該Service被首次創建時調用
        Log.i(TAG, NAME + "--onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, NAME + "--onStartCommand");
        //該Service正在運行,client再次調用startService()時調用
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, NAME + "--onBind");
        //client調用bindService()綁定到該Service時調用
        return new MyServiceAIDLImpl();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, NAME + "--onUnbind");
        //所有已綁定到該Service的client調用unbindService()與該Service解綁時調用
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        Log.i(TAG, NAME + "--onRebind");
        //當onUnbind()被調用後,又有新的client調用bindService()來綁定該Service時調用
        super.onRebind(intent);
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, NAME + "--onDestroy");
        //該Service不再使用,銷燬時調用
        super.onDestroy();
    }

    class MyServiceAIDLImpl extends IMyServiceAIDL.Stub {

        @Override
        public int add(int a, int b) throws RemoteException {
            return a + b;
        }

    }

}

  onBind()方法中需要返回.aidl文件定義接口的實例。

在manifest中註冊Service

<service
            android:name=".MyService"
            android:process=":remote">
            <intent-filter>
                <action android:name="com.example.sunxiaodong.remoteservice.MyService"/>
            </intent-filter>
        </service>

  Service註冊時,使用android:process聲明屬性將該Service定義爲遠程Service。因爲在其它應用程序中,不能獲得該Service的具體類名,所以如果需要在其它應用程序中使用該遠程Service,則需要爲該Service定義action。

MainActivity.java文件

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String NAME = MainActivity.class.getSimpleName();
    private static final String TAG = "sxd";

    private Button mStartService;
    private Button mStopService;
    private Button mBindService;
    private Button mUnbindService;

    private EditText mAddendOne;
    private EditText mAddendTwo;
    private TextView mResult;

    private IMyServiceAIDL mIMyServiceAIDL;

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

    private void initView() {
        mStartService = (Button) this.findViewById(R.id.start_service);
        mStartService.setOnClickListener(this);
        mStopService = (Button) this.findViewById(R.id.stop_service);
        mStopService.setOnClickListener(this);
        mBindService = (Button) this.findViewById(R.id.bind_service);
        mBindService.setOnClickListener(this);
        mUnbindService = (Button) this.findViewById(R.id.unbind_service);
        mUnbindService.setOnClickListener(this);
        mAddendOne = (EditText) this.findViewById(R.id.addend_one);
        mAddendTwo = (EditText) this.findViewById(R.id.addend_two);
        mResult = (TextView) this.findViewById(R.id.result);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start_service:
                startService();
                break;
            case R.id.stop_service:
                stopService();
                break;
            case R.id.bind_service:
                bindService();
                break;
            case R.id.unbind_service:
                unbindService();
                break;
        }
    }

    private void startService() {
        Intent intent = new Intent(this, MyService.class);
        startService(intent);
    }

    private void stopService() {
        Intent intent = new Intent(this, MyService.class);
        stopService(intent);
    }

    private void bindService() {
        Intent intent = new Intent(this, MyService.class);//顯示啓動Service
        /*Intent intent = new Intent("com.example.sunxiaodong.remoteservice.MyService");
        intent.setPackage("com.example.sunxiaodong.remoteservice");//Android5.0後Service不能採用隱式啓動,所以必須加上包名*/
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    private void unbindService() {
        unbindService(mConnection);
    }

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIMyServiceAIDL = IMyServiceAIDL.Stub.asInterface(service);
            try {
                int result = mIMyServiceAIDL.add(Integer.parseInt(mAddendOne.getText().toString()), Integer.parseInt(mAddendTwo.getText().toString()));
                mResult.setText(String.valueOf(result));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };

}

  使用IMyServiceAIDL.Stub.asInterface()方法將傳入的IBinder對象傳換成了IMyServiceAIDL對象,接下來就可以調用在IMyServiceAIDL.aidl文件中定義的所有接口了。
  該示例程序,填入兩個加數後,點擊“綁定服務”按鈕,就可在結果位置,顯示兩個加數之和。

其他應用程序使用遠程Service示例

  在該應用程序中,要使用其它應用程序的遠程Service,需要將該遠程Service的.aidl文件連同包名路徑一起拷貝到該應用程序中。

client應用程序的MainActivity.java文件

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String NAME = MainActivity.class.getSimpleName();
    private static final String TAG = "sxd";

    private Button mStartService;
    private Button mStopService;
    private Button mBindService;
    private Button mUnbindService;

    private EditText mAddendOne;
    private EditText mAddendTwo;
    private TextView mResult;

    private IMyServiceAIDL mIMyServiceAIDL;

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

    private void initView() {
        mStartService = (Button) this.findViewById(R.id.start_service);
        mStartService.setOnClickListener(this);
        mStopService = (Button) this.findViewById(R.id.stop_service);
        mStopService.setOnClickListener(this);
        mBindService = (Button) this.findViewById(R.id.bind_service);
        mBindService.setOnClickListener(this);
        mUnbindService = (Button) this.findViewById(R.id.unbind_service);
        mUnbindService.setOnClickListener(this);
        mAddendOne = (EditText) this.findViewById(R.id.addend_one);
        mAddendTwo = (EditText) this.findViewById(R.id.addend_two);
        mResult = (TextView) this.findViewById(R.id.result);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start_service:
                startService();
                break;
            case R.id.stop_service:
                stopService();
                break;
            case R.id.bind_service:
                bindService();
                break;
            case R.id.unbind_service:
                unbindService();
                break;
        }
    }

    private void startService() {
        Intent intent = new Intent("com.example.sunxiaodong.remoteservice.MyService");
        intent.setPackage("com.example.sunxiaodong.remoteservice");//Android5.0後Service不能採用隱式啓動,所以必須加上包名
        startService(intent);
    }

    private void stopService() {
        Intent intent = new Intent("com.example.sunxiaodong.remoteservice.MyService");
        intent.setPackage("com.example.sunxiaodong.remoteservice");//Android5.0後Service不能採用隱式啓動,所以必須加上包名
        stopService(intent);
    }

    private void bindService() {
        Intent intent = new Intent("com.example.sunxiaodong.remoteservice.MyService");
        intent.setPackage("com.example.sunxiaodong.remoteservice");//Android5.0後Service不能採用隱式啓動,所以必須加上包名
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    private void unbindService() {
        unbindService(mConnection);
    }

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIMyServiceAIDL = IMyServiceAIDL.Stub.asInterface(service);
            try {
                int result = mIMyServiceAIDL.add(Integer.parseInt(mAddendOne.getText().toString()), Integer.parseInt(mAddendTwo.getText().toString()));
                mResult.setText(String.valueOf(result));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };

}

  對遠程Service的使用,基本上同相同應用程序內對遠程Service的使用方法相同,只是在啓動該Service時,需要使用action。

源碼地址

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