1. 程式人生 > >Android基礎之四大元件-詳解Service

Android基礎之四大元件-詳解Service

1 介紹

  Service是android的一種機制。Service是執行在後臺的程式碼,不能與使用者互動,可以執行在自己的程序,也可以執行在其他應用程式的上下文裡。需要通過某一個Activity或其他Context物件來呼叫。

1.1 Service的生命週期

這裡寫圖片描述
  被啟動的服務的生命週期:如果一個Service被某個Activity呼叫 Context.startService方法啟動,那麼不管是否有Activity使用bindService繫結或unbindService解除繫結到該Service,該Service都在後臺執行。如果一個Service被startService方法多次啟動,那麼onCreate方法只會呼叫一次,onStartCommand將會被呼叫多次(對應呼叫startService的次數),並且系統只會建立Service的一個例項(因此只需要一次呼叫stopService)

。該Service將會一直在後臺執行,而不管對應程式的Activity是否在執行,直到被呼叫stopService,或自身的stopSelf方法。當然如果系統資源不足,android系統也可能結束服務。
  被繫結的服務的生命週期:如果一個Service被某個Activity呼叫 Context.bindService方法繫結啟動,不管呼叫bindService呼叫幾次,onCreate方法都只會呼叫一次,onBind方法也只會呼叫一次,同時onStartCommand方法始終不會被呼叫。當連線建立之後,Service將會一直執行,除非呼叫Context.unbindService斷開連線或者之前呼叫bindService的Context不存在了(如Activity被finish的時候),系統將會自動停止Service,對應onDestroy將被呼叫。
  被啟動又被繫結的服務的生命週期:如果一個Service又被啟動又被繫結,則該Service將會一直在後臺執行。並且不管如何呼叫,onCreate始終只會呼叫一次,對應startService呼叫多少次,Service的onStartCommand便會呼叫多少次。呼叫unbindService將不會停止Service,需要unbindService與stopService同時呼叫或Service的stopSelf來停止服務。

  當服務被停止時清除服務:當一個Service被終止(①呼叫stopService;②呼叫stopSelf;③不再有繫結的連線,沒有被啟動)時,onDestroy方法將會被呼叫,在這裡你應當做一些清除工作,如停止在Service中建立並執行的執行緒。

1.2 註冊Service

  無論使用哪種啟動方法,都需要在xml裡註冊你的Service:

<service
    android:name=".packnameName.youServiceName"
    android:enabled="true" />

1.3 什麼時候使用Service

  • 播放多媒體的時候,使用者啟動了其他的Activity這個時候程式要在後臺繼續播放。
  • 後臺service下載外掛、更新包。
  • 檢測SD卡上檔案的變化。
  • 在後臺記錄你地理位置的改變等等。

1.4 Service分為本地服務(LocalService)和遠端服務(RemoteService)

  預設情況下是在同一個主執行緒中。但可以通過配,android:process=”:remote” 屬性讓 Service 執行在不同的程序。
  本地服務依附在主程序上而不是獨立的程序,這樣在一定程度上節約了資源,另外Local服務因為是在同一程序因此不需要IPC,也不需要AIDL。相應bindService會方便很多。主程序被Kill後,服務便會終止。
  遠端服務為獨立的程序,對應程序名格式為所在包名加上你指定的android:process字串。由於是獨立的程序,因此在Activity所在程序被Kill的時候,該服務依然在執行,不受其他程序影響,有利於為多個程序提供服務具有較高的靈活性。該服務是獨立的程序,會佔用一定資源,並且使用AIDL進行IPC稍微麻煩一點。

2 Service的具體問題

2.1 Service可以執行耗時操作嗎

  不能,超過20s就會出現ARN。

2.2 Service的啟動方法,互相的區別

(1)startService啟動的服務:主要用於啟動一個服務執行後臺任務,不進行通訊。停止服務使用stopService;當用訪問者啟動之後,如果訪問者不主動關閉,Service就不會關閉。
(2)bindService啟動的服務:該方法啟動的服務可以進行通訊,停止服務使用unbindService。應用程式可以通過ServiceConnection進行資料互動。在實現Service時重寫的onBind方法中,其返回的物件會傳給ServiceConnection物件的onServiceConnected(ComponentName name, IBinder service)中的service引數;也就是說獲取了serivce這個引數就得到了Serivce元件返回的值。

//其中只要與Service連線成功conn就會呼叫其onServiceConnected方法。
Context.bindService(Intent intent,ServiceConnection conn,int flag)

(3)startService同時也bindService 啟動的服務:停止服務應同時使用stepService與unbindService。

2.3 Service與Activity怎麼實現通訊(程序內通訊)

(1)通過Binder物件。同進程下Activity與Service雙向通訊,Activity呼叫bindService (Intent service, ServiceConnection conn, int flags)方法。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    <application
        <service android:name=".MyService" />
    </application>
</manifest>
/**
 * 第1步,新建一個繼承自Service的類MyService;然後在AndroidManifest.xml裡註冊這個Service
 */
public class MyService extends Service {
    private static final String TAG = "xiaoguan";
    //第4.1步,例項化一個MyBinder物件
    private MyBinder mBinder = new MyBinder();
    private OnTestListener mListener;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        //第4.2步,在onBind回撥方法裡面返回這個mBinder物件
        return mBinder;
    }

    //第3步,新建一個繼承自Binder的類MyBinder
    public class MyBinder extends Binder {
        //Activity通過Binder來呼叫Service的方法將訊息傳給Service
        public void testMethod(String msg) {
            Log.d(TAG, "receive message from activity: "+msg);
            //並回調mListener.onTest告訴Activity已收到訊息
            mListener.onTest("hi, activity");
        }
        //MyBinder 裡面提供一個註冊回撥的方法
        public void setOnTestListener(OnTestListener listener) {
            mListener = listener;
        }
    }

    //自定義一個回撥介面
    public interface OnTestListener {
        void onTest(String str);
    }
}
public class MainActivity extends Activity {
    private static final String TAG = "xiaoguan";
    private MyService.MyBinder mBinder;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //第5步,所說的在Activity裡面取得Service裡的binder物件
            mBinder = (MyService.MyBinder) service;
            //第6步,註冊自定義回撥
            mBinder.setOnTestListener(new MyService.OnTestListener() {
                @Override
                public void onTest(String str) {
                    Log.d(TAG, "receive message from service: "+str);
                }
            });
        }

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(MainActivity.this, MyService.class);
        //第2步,Activity裡面使用bindService方式啟動MyService,也就是綁定了MyService
        bindService(intent,mConnection,BIND_AUTO_CREATE);

        findViewById(R.id.bt1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //點選按鈕呼叫mBinder裡面的方法,傳送訊息給Service
                mBinder.testMethod("hi, service");
            }
        });
    }
}
//結果列印
receive message from activity: hi, service
receive message from service: hi, activity

(2)通過廣播。Service向Activity傳送訊息,可以使用廣播,當然Activity要註冊相應的接收器。比如Service要向多個Activity傳送同樣的訊息的話,用這種方法就更好。

2.4 Service裡面可以彈出dialog或Toast

(1)Toast通常使用Activity和Application的context,也可以使用Service、ContentProvider和BroadcastReceiver的context。但是在IntentService的onHandleIntent()不能使用,因為其在子執行緒中。
(2)Dialog只能在Activity中使用,其他元件使用會丟擲異常。也有方法可以實現,如下(加個許可權,在manifest中新增此許可權以彈出dialog)。

<uses-permission Android:name="android.permission.SYSTEM_ALERT_WINDOW" />

dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
Builder builder = new AlertDialog.Builder(this);
builder.setMessage("是否重啟服務");
builder.setNegativeButton("取消", new OnClickListense() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
           // to do
    }
});
builder.setPositiveButton("確定", new OnClickListense() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
           // to do
    }
});
final AlertDialog dialog = buidler.create();
//在dialog show前新增此程式碼,表示該dialog屬於系統dialog。
dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
new Thread() {
     public void run() {
            SystemClock.sleep(2000);
            hanlder.post(new Runnable() {
                    @Override
                    public void run() {
                          dialog.show();
                    }
            });
     };
}.start();

2.5 為什麼開啟Service去建立子執行緒而不是Activity中

  (1)若直接在Activity中新開一條執行緒來做耗時操作,當該Activity退出到桌面或其他情況時將成為一個後臺程序。
  (2)若在Service中新啟動執行緒,則此時Android會依據程序中當前活躍元件重要程度,將其判斷為服務程序,優先順序比(1)高。
  因為服務程序的優先順序比後臺程序的優先順序高,所以對於一個需要啟動一個長時間操作的activity來說,開啟service去建立子執行緒比Activity中建立子執行緒更好,尤其是對於操作將很可能超出activity的持續時間時。
  比如要上傳一個圖片檔案,應該開啟一個service來進行上傳工作,這樣在使用者離開activity時工作仍在進行。使用service將會保證操作至少有服務程序的優先順序。

3 如何保證Service在後臺不被殺死,程序保活

4 IntentService

5 參考連結