Android Service

Service 簡介

ServiceAndroid四大組件之一吧凉,一般而言,Activity 用于提供一個可供交互的屏幕界面瞻想,以此完成一些任務(wù)咆耿。而Service則與 Activity 不同,它主要用于執(zhí)行一些不需要與用戶進(jìn)行交互且需要長時間運(yùn)行的任務(wù)故硅。值得注意是庶灿,Service雖然是處理后臺任務(wù)的,但它仍然運(yùn)行在主線程中吃衅,所以往踢,如果需要執(zhí)行耗時任務(wù),還需要開啟一個子線程徘层,不然會出現(xiàn) ANR(Application Not Responding)現(xiàn)象峻呕。慶幸的是,Android中存在一個 IntentService類趣效,它擁有自己的線程瘦癌,是獨(dú)立于 主線程的,所以我們需要使用Service的時候可以直接繼承自 IntentService跷敬。

Service 的兩種啟動方法

  1. Context.startService()

如果我們需要程序在后臺執(zhí)行一項任務(wù)讯私,即使當(dāng)前用戶并沒有與程序發(fā)生交互,而且這項任務(wù)的執(zhí)行并不需要用戶的干預(yù)干花,即不需要與用戶發(fā)生過多的交互妄帘,我們可以選擇該方法。

當(dāng) Context.startService()方法調(diào)用后池凄,系統(tǒng)會首先調(diào)用 ServiceonCreate()方法(第一次運(yùn)行調(diào)用該方法抡驼,之后就不再調(diào)用),然后調(diào)用 onStartCommand(Intent, int, int)肿仑。該Service 會一直運(yùn)行下去致盟,直到 Context.stopService()或者 StopSelf()被調(diào)用碎税。

onStartCommand(Intent, int, int) 方法會返回一個整形常量,該常量用于告訴系統(tǒng)如何處理 Service的重啟操作馏锡,其中三個返回值為

返回 START_STICKY 代表當(dāng)前系統(tǒng)出于某些原因關(guān)閉Service 時雷蹂,Service 會被重新啟動。但是杯道,當(dāng)系統(tǒng)重啟 Service 時匪煌, onStartCommand()參數(shù)中的Intent會被置為 null

返回START_NOT_STICKY 意味著 Service不會在系統(tǒng)關(guān)閉它時重新啟動党巾,適合執(zhí)行一次性的操作 萎庭。

返回 START_REDELIVER_INTENT的效果和 START_STICKY 基本一樣,但是 onStartCommand()會接收到Service 被銷毀之前接收到的最后一個 Intent齿拂。

  1. Context.bindService()

對應(yīng)于上一個方法驳规,如果該后臺任務(wù)需要與用戶發(fā)生頻繁的交互,可以采用該方法署海,當(dāng)然這種方法比上一種方法復(fù)雜些吗购。

當(dāng)Context.bindService() 方法調(diào)用后,如果該 Service 還不存在砸狞,那么 首先調(diào)用onCreate()方法捻勉,然后調(diào)用onBind(Intent)方法。 該 Service會一直運(yùn)行只要Connection 是建立著的趾代。

Service 的使用

1.如果采用的是Context.startService(),簡單用法如下:

public class MyService extends Service{

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

@Override
public void onCreate() {
    super.onCreate();
    Log.d("MyService", "onCreate");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d("MyService", "onStartCommand");
    return super.onStartCommand(intent, flags, startId);
}
}

然后在Activity 中調(diào)用startService(Intent) 即可贯底。

這種方法,我們可以在onCreate()方法中初始化一些我們需要的變量撒强,在 onStartCommand()方法中執(zhí)行任務(wù)禽捆,因為該方法 在每次啟動服務(wù)時都會被調(diào)用,而onCreate() 只在服務(wù)創(chuàng)建時執(zhí)行一次飘哨。
使用這種方法胚想,當(dāng)服務(wù)完成時,可以使用發(fā)送 Broadcast的方法通知Activity已經(jīng)完成芽隆,然后停止該服務(wù)浊服。

2.使用 Context.bindService(),簡單用法如下

public class MyLocalService extends Service {

    private LocalBinder mLocalBinder = new LocalBinder();

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

    public void doLongRunningOperation() {
        //執(zhí)行耗時任務(wù)
    }

    public class LocalBinder extends Binder {
        public MyLocalService getService() {
            return MyLocalService.this;
        }
    }
}

public class MainActivity extends AppCompatActivity implements ServiceConnection{

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

    @Override
    protected void onResume() {
        super.onResume();
        Intent intent = new Intent(this,MyLocalService.class);
        bindService(intent,this,BIND_AUTO_CREATE);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mLocalService != null){
            unbindService(this);
        }
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        mLocalService = ((MyLocalService.LocalBinder)iBinder).getService();
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        mLocalService = null;
    }
}

如圖示,當(dāng)在Activity 中調(diào)用 bindService() 之后胚吁,接著就會調(diào)用 Service 中的onBind() 方法牙躺,該方法會返回一個IBinderonServiceConnected()方法,然后就可以通過 IBinder 獲得該 Service對象腕扶,接著就可以像使用一個 普通 Java對象使用它了孽拷。

請注意,以上兩種方法在執(zhí)行耗時任務(wù)時半抱,都需要手動的去開啟一個子線程脓恕,為了方便和高效膜宋,我們可以使用 IntentService工具類。

IntentService 的使用

IntentServiceService中包裝了一個處理后臺線程的 Handler炼幔,多個調(diào)用會被內(nèi)部的Hnadler放到隊列中秋茫,所以能確保在同一時間只能有一個 Intent被處理,基于IntentServiceService 會一直處于啟動狀態(tài)乃秀,直到隊列中沒有要處理的任務(wù)肛著。

public class MyIntentService extends IntentService{

    public static String ACTION_DOWNLOAD = "com.example.myintentservice.download"; 
public MyIntentService(String name) {
        super(name);
        setIntentRedelivery(false);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        String action = intent.getAction();
        if (ACTION_DOWNLOAD.equals(action)){
            download();
        }
    }

    private void download(){
        //Download
    }
}

這個時候,我們需要在清單文件中為 Service 添加相應(yīng)的intent-filter环形,然后以帶有特定actionIntent調(diào)用 Context.startService()方法策泣。

Service 的通信

上面已經(jīng)提到過很多次衙傀,如果使用Context.startService(),和 Activity 交互是很不方便的抬吟,就好像Activity 提醒 Service你可以運(yùn)行了,然后Service就開始自顧自的運(yùn)行统抬,直到運(yùn)行結(jié)束火本,通過 Broadcast Receiver發(fā)送一條廣播,告訴Activity我完成任務(wù)了聪建,你可以調(diào)用 stopService()了钙畔。如果僅僅是這樣,是很不利于ServiceActivity 之間進(jìn)行大規(guī)慕痿铮快速更新操作的擎析。那么該怎么辦呢?

Context.bindService 天生就是解決這個問題的挥下,誰讓咱在創(chuàng)建的時候還能返回一個IBinder呢揍魂。 下面就來使用它完成一個下載任務(wù)

public class MyLocalService extends Service {

    private LocalBinder mLocalBinder = new LocalBinder();
    private Callback mCallback;

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

    public void doLongRunningOperation() {
        new MyAsyncTask().execute();
    }

    public class LocalBinder extends Binder {
        public MyLocalService getService() {
            return MyLocalService.this;
        }
    }

    public interface Callback{
        public void onOperationProgress(int progress);
        public void onOperationCompleted(boolean complete);
    }

    public void setCallback(Callback callback){
        mCallback = callback;
    }

    private final class MyAsyncTask extends AsyncTask<Void,Integer,Boolean>{

        @Override
        protected Boolean doInBackground(Void... voids) {
            int progress = 0;
            for (progress = 0;progress<=100;progress+=25) {
                publishProgress(progress);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return true;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            if (mCallback != null){
                mCallback.onOperationProgress(values[0]);
            }
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            if (mCallback != null && aBoolean){
                mCallback.onOperationCompleted(true);
            }
        }
    }
}

public class MainActivity extends AppCompatActivity implements ServiceConnection,MyLocalService.Callback{

    private MyLocalService mLocalService;
    private Button mButton;
    private ProgressDialog mDialog;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.start);
        mDialog = new ProgressDialog(this);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mDialog.setTitle("Download");
                mDialog.show();
                mLocalService.doLongRunningOperation();
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        Intent intent = new Intent(this,MyLocalService.class);
        bindService(intent,this,BIND_AUTO_CREATE);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mLocalService != null){
            mLocalService.setCallback(null);
            unbindService(this);
        }
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        mLocalService = ((MyLocalService.LocalBinder)iBinder).getService();
        mLocalService.setCallback(this);
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        mLocalService = null;
    }

    @Override
    public void onOperationProgress(int progress) {
        mDialog.setMessage(progress+"%");
    }

    @Override
    public void onOperationCompleted(boolean complete) {
        if (complete){
            mDialog.dismiss();
            Toast.makeText(this, "Download success", Toast.LENGTH_SHORT).show();
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市棚瘟,隨后出現(xiàn)的幾起案子现斋,更是在濱河造成了極大的恐慌,老刑警劉巖偎蘸,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庄蹋,死亡現(xiàn)場離奇詭異,居然都是意外死亡迷雪,警方通過查閱死者的電腦和手機(jī)限书,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來章咧,“玉大人倦西,你說我怎么就攤上這事』塾剩” “怎么了调限?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵舟陆,是天一觀的道長。 經(jīng)常有香客問我耻矮,道長秦躯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任裆装,我火速辦了婚禮踱承,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘哨免。我一直安慰自己茎活,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布琢唾。 她就那樣靜靜地躺著载荔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪采桃。 梳的紋絲不亂的頭發(fā)上懒熙,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機(jī)與錄音普办,去河邊找鬼工扎。 笑死,一個胖子當(dāng)著我的面吹牛衔蹲,可吹牛的內(nèi)容都是我干的肢娘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼舆驶,長吁一口氣:“原來是場噩夢啊……” “哼橱健!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起贞远,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤畴博,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蓝仲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體俱病,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年袱结,在試婚紗的時候發(fā)現(xiàn)自己被綠了亮隙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡垢夹,死狀恐怖溢吻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤促王,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布犀盟,位于F島的核電站,受9級特大地震影響蝇狼,放射性物質(zhì)發(fā)生泄漏阅畴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一迅耘、第九天 我趴在偏房一處隱蔽的房頂上張望贱枣。 院中可真熱鬧,春花似錦颤专、人聲如沸纽哥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽春塌。三九已至,卻和暖如春累魔,著一層夾襖步出監(jiān)牢的瞬間摔笤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工垦写, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人彰触。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓梯投,卻偏偏與公主長得像,于是被迫代替她去往敵國和親况毅。 傳聞我的和親對象是個殘疾皇子分蓖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言:本文所寫的是博主的個人見解,如有錯誤或者不恰當(dāng)之處尔许,歡迎私信博主么鹤,加以改正!原文鏈接味廊,demo鏈接 Serv...
    PassersHowe閱讀 1,415評論 0 5
  • Service 作為android 四大組件之一蒸甜,主要用于再后臺處理一些耗時的邏輯或者去執(zhí)行一些長期運(yùn)行的任務(wù)。 ...
    ACE_Killer09閱讀 583評論 0 4
  • 描述 Service通秤喾穑總是稱之為“后臺服務(wù)”柠新,其中“后臺”一詞是相對于前臺而言的,具體是指其本身的運(yùn)行并不依賴于...
    pkqgo閱讀 667評論 1 3
  • 要說大學(xué)和高中最大的不一樣,我體會最深的就是沒有同桌了郊楣,
    遙遠(yuǎn)有多遠(yuǎn)閱讀 195評論 0 0
  • 今年六月份憔恳,我?guī)е鴰讉€學(xué)生去北京參加第八屆全國青少年文化遺產(chǎn)知識大賽瓤荔,孩子們一舉拿下了兩個團(tuán)體獎,四個個人獎钥组,特別...
    馬少軍閱讀 508評論 0 4