Service后臺服務(wù)、前臺服務(wù)、IntentService、跨進(jìn)程服務(wù)、無障礙服務(wù)、系統(tǒng)服務(wù)文章

原文章轉(zhuǎn)載:http://blog.csdn.net/qq_30379689/article/details/53318861

前言

作為四大組件之一的Service類,是面試和筆試的必備關(guān)卡,我把我所學(xué)到的東西總結(jié)了一遍,相信你看了之后你會(huì)對Service娓娓道來,在以后遇到Service的問題胸有成竹,廢話不多說,開車?yán)?/p>

Service簡介

Service是Android中實(shí)現(xiàn)程序后臺運(yùn)行的解決方案,它非常適用于去執(zhí)行那些不需要和用戶交互而且還要求長期運(yùn)行的任務(wù)。Service默認(rèn)并不會(huì)運(yùn)行在子線程中,它也不運(yùn)行在一個(gè)獨(dú)立的進(jìn)程中,它同樣執(zhí)行在UI線程中,因此,不要在Service中執(zhí)行耗時(shí)的操作,除非你在Service中創(chuàng)建了子線程來完成耗時(shí)操作

Service的運(yùn)行不依賴于任何用戶界面,即使程序被切換到后臺或者用戶打開另一個(gè)應(yīng)用程序,Service仍然能夠保持正常運(yùn)行,這也正是Service的使用場景。當(dāng)某個(gè)應(yīng)用程序進(jìn)程被殺掉時(shí),所有依賴于該進(jìn)程的Service也會(huì)停止運(yùn)行

后臺服務(wù)

后臺服務(wù)可交互性主要是體現(xiàn)在不同的啟動(dòng)服務(wù)方式,startService()和bindService()。bindService()可以返回一個(gè)代理對象,可調(diào)用Service中的方法和獲取返回結(jié)果等操作,而startService()不行

<a name="t4" target="_blank" style="text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);"></a> 不可交互的后臺服務(wù)

不可交互的后臺服務(wù)即是普通的Service,Service的生命周期很簡單,分別為onCreate、onStartCommand、onDestroy這三個(gè)。當(dāng)我們startService()的時(shí)候,首次創(chuàng)建Service會(huì)回調(diào)onCreate()方法,然后回調(diào)onStartCommand()方法,再次startService()的時(shí)候,就只會(huì)執(zhí)行一次onStartCommand()。服務(wù)一旦開啟后,我們就需要通過stopService()方法或者stopSelf()方法,就能把服務(wù)關(guān)閉,這時(shí)就會(huì)回調(diào)onDestroy()

一、創(chuàng)建服務(wù)類

創(chuàng)建一個(gè)服務(wù)非常簡單,只要繼承Service,并實(shí)現(xiàn)onBind()方法

public class BackGroupService extends Service {

    /**
     * 綁定服務(wù)時(shí)調(diào)用
     *
     * @param intent
     * @return
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("Service", "onBind");
        return null;
    }

    /**
     * 服務(wù)創(chuàng)建時(shí)調(diào)用
     */
    @Override
    public void onCreate() {
        Log.e("Service", "onCreate");
        super.onCreate();
    }

    /**
     * 執(zhí)行startService時(shí)調(diào)用
     *
     * @param intent
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e("Service", "onStartCommand");
        //這里執(zhí)行耗時(shí)操作
        new Thread() {
            @Override
            public void run() {
                while (true){
                    try {
                        Log.e("Service", "doSomething");
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 服務(wù)被銷毀時(shí)調(diào)用
     */
    @Override
    public void onDestroy() {
        Log.e("Service", "onDestroy");
        super.onDestroy();
    }
}

二、配置服務(wù)

Service也是四大組件之一,所以必須在manifests中配置

<service android:name=".Service.BackGroupService"/>

<a name="t7" target="_blank" style="text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);"></a>三、啟動(dòng)服務(wù)和停止服務(wù)

我們通過兩個(gè)按鈕分別演示啟動(dòng)服務(wù)和停止服務(wù),通過startService()開啟服務(wù),通過stopService()停止服務(wù)

public class MainActivity extends AppCompatActivity {

    Button bt_open, bt_close;

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

        bt_open = (Button) findViewById(R.id.open);
        bt_close = (Button) findViewById(R.id.close);

        final Intent intent = new Intent(this, BackGroupService.class);

        bt_open.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //啟動(dòng)服務(wù)
                startService(intent);
            }
        });

        bt_close.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //停止服務(wù)
                stopService(intent);
            }
        });
    }
}

當(dāng)你開啟服務(wù)后,還有一種方法可以關(guān)閉服務(wù),在設(shè)置中,通過應(yīng)用->找到自己應(yīng)用->停止

image

四、運(yùn)行代碼

運(yùn)行程序后,我們點(diǎn)擊開始服務(wù),然后一段時(shí)間后關(guān)閉服務(wù)。我們以Log信息來驗(yàn)證普通Service的生命周期:onCreate->onStartCommand->onDestroy

11-24 00:19:51.483 16407-16407/com.handsome.boke2 E/Service: onCreate
11-24 00:19:51.483 16407-16407/com.handsome.boke2 E/Service: onStartCommand
11-24 00:19:51.485 16407-16613/com.handsome.boke2 E/Service: doSomething
11-24 00:19:53.490 16407-16613/com.handsome.boke2 E/Service: doSomething
11-24 00:19:55.491 16407-16613/com.handsome.boke2 E/Service: doSomething
11-24 00:19:57.491 16407-16613/com.handsome.boke2 E/Service: doSomething
11-24 00:19:58.056 16407-16407/com.handsome.boke2 E/Service: onDestroy
11-24 00:19:59.492 16407-16613/com.handsome.boke2 E/Service: doSomething
11-24 00:20:01.494 16407-16613/com.handsome.boke2 E/Service: doSomething
11-24 00:20:03.495 16407-16613/com.handsome.boke2 E/Service: doSomething

其中你會(huì)發(fā)現(xiàn)我們的子線程進(jìn)行的耗時(shí)操作是一直存在的,而我們Service已經(jīng)被關(guān)閉了,關(guān)閉該子線程的方法需要直接通過Home鍵關(guān)閉該應(yīng)用程序

可交互的后臺服務(wù)

可交互的后臺服務(wù)是指前臺頁面可以調(diào)用后臺服務(wù)的方法,可交互的后臺服務(wù)實(shí)現(xiàn)步驟是和不可交互的后臺服務(wù)實(shí)現(xiàn)步驟是一樣的,區(qū)別在于啟動(dòng)的方式和獲得Service的代理對象

<a name="t10" target="_blank" style="text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);"></a>一、創(chuàng)建服務(wù)類

和普通Service不同在于這里返回一個(gè)代理對象,返回給前臺進(jìn)行獲取,即前臺可以獲取該代理對象執(zhí)行后臺服務(wù)的方法

public class BackGroupService extends Service {

    /**
     * 綁定服務(wù)時(shí)調(diào)用
     *
     * @param intent
     * @return
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("Service", "onBind");
        //返回代理對象
        return new MyBinder();
    }

    /**
     * 代理類
     */
    class MyBinder extends Binder {
        public void showToast() {
            Log.e("Service", "showToast");
        }

        public void showList() {
            Log.e("Service", "showList");
        }
    }

    /**
     * 解除綁定服務(wù)時(shí)調(diào)用
     * @param intent
     * @return
     */
    @Override
    public boolean onUnbind(Intent intent) {
        Log.e("Service", "onUnbind");
        return super.onUnbind(intent);
    }

    /**
     * 服務(wù)創(chuàng)建時(shí)調(diào)用
     */
    @Override
    public void onCreate() {
        Log.e("Service", "onCreate");
        super.onCreate();
    }

    /**
     * 服務(wù)被銷毀時(shí)調(diào)用
     */
    @Override
    public void onDestroy() {
        Log.e("Service", "onDestroy");
        super.onDestroy();
    }
}

二、配置服務(wù)

<service android:name=".Service.BackGroupService"/>

三、綁定服務(wù)和解除綁定服務(wù)

我們通過兩個(gè)按鈕分別演示綁定服務(wù)和解除綁定服務(wù),通過bindService()開啟服務(wù),通過unbindService()停止服務(wù)

public class MainActivity extends AppCompatActivity {

    Button bt_open, bt_close;

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

        bt_open = (Button) findViewById(R.id.open);
        bt_close = (Button) findViewById(R.id.close);

        final Intent intent = new Intent(this, BackGroupService.class);

        bt_open.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bindService(intent, conn, BIND_AUTO_CREATE);
            }
        });

        bt_close.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(conn);
            }
        });

    }

    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //拿到后臺服務(wù)代理對象
            BackGroupService.MyBinder myBinder = (BackGroupService.MyBinder) service;
            //調(diào)用后臺服務(wù)的方法
            myBinder.showToast();
            myBinder.showList();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
}

這里和startService的區(qū)別在于多了一個(gè)ServiceConnection對象,該對象是用戶綁定后臺服務(wù)后,可獲取后臺服務(wù)代理對象的回調(diào),我們可以通過該回調(diào),拿到后臺服務(wù)的代理對象,并調(diào)用后臺服務(wù)定義的方法,也就實(shí)現(xiàn)了后臺服務(wù)和前臺的交互

<a name="t13" target="_blank" style="text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);"></a>四、運(yùn)行代碼

運(yùn)行程序后,我們點(diǎn)擊綁定服務(wù),然后一段時(shí)間后解除綁定服務(wù)。我們以Log信息來驗(yàn)證Service的生命周期:onCreate->onBind->onUnBind->onDestroy,其中也可以看到我們調(diào)用后臺服務(wù)的方法showToast和showList

11-24 00:55:32.775 15408-15408/com.handsome.boke2 E/Service: onCreate
11-24 00:55:32.775 15408-15408/com.handsome.boke2 E/Service: onBind
11-24 00:55:32.796 15408-15408/com.handsome.boke2 E/Service: showToast
11-24 00:55:32.796 15408-15408/com.handsome.boke2 E/Service: showList
11-24 00:55:34.290 15408-15408/com.handsome.boke2 E/Service: onUnbind
11-24 00:55:34.290 15408-15408/com.handsome.boke2 E/Service: onDestroy

混合性交互的后臺服務(wù)

或許你會(huì)迷惑,startService和bindService之間有什么關(guān)系?其實(shí)簡單的說兩者之間是沒有關(guān)聯(lián)的,類似于你親媽生了個(gè)雙胞胎一樣,只有純粹的血緣關(guān)系。那么問題來了,這兩個(gè)啟動(dòng)方式是否可以同時(shí)使用呢,答案是可以的

將上面兩種啟動(dòng)方式結(jié)合起來就是混合性交互的后臺服務(wù)了,即可以單獨(dú)運(yùn)行后臺服務(wù),也可以運(yùn)行后臺服務(wù)中提供的方法,其完整的生命周期是:onCreate->onStartCommand->onBind->onUnBind->onDestroy

<a name="t15" target="_blank" style="text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);"></a> 前臺服務(wù)

由于后臺服務(wù)優(yōu)先級相對比較低,當(dāng)系統(tǒng)出現(xiàn)內(nèi)存不足的情況下,它就有可能會(huì)被回收掉,所以前臺服務(wù)就是來彌補(bǔ)這個(gè)缺點(diǎn)的,它可以一直保持運(yùn)行狀態(tài)而不被系統(tǒng)回收。例如:墨跡天氣在狀態(tài)欄中的天氣預(yù)報(bào)

image

一、創(chuàng)建服務(wù)類

前臺服務(wù)創(chuàng)建很簡單,其實(shí)就在Service的基礎(chǔ)上創(chuàng)建一個(gè)Notification,然后使用Service的startForeground()方法即可啟動(dòng)為前臺服務(wù)

public class ForegroundService extends Service {

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

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

    /**
     * 啟動(dòng)前臺通知
     */
    private void showNotification() {
        //創(chuàng)建通知詳細(xì)信息
        Notification.Builder mBuilder = new Notification.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("2016年11月24日")
                .setContentText("今天天氣陰天,8到14度");
        //創(chuàng)建點(diǎn)擊跳轉(zhuǎn)Intent
        Intent intent = new Intent(this, MainActivity.class);
        //創(chuàng)建任務(wù)棧Builder
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(MainActivity.class);
        stackBuilder.addNextIntent(intent);
        PendingIntent pendingIntent = stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
        //設(shè)置跳轉(zhuǎn)Intent到通知中
        mBuilder.setContentIntent(pendingIntent);
        //獲取通知服務(wù)
        NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        //構(gòu)建通知
        Notification notification = mBuilder.build();
        //顯示通知
        nm.notify(0, notification);
        //啟動(dòng)為前臺服務(wù)
        startForeground(0, notification);
    }
}

二、配置服務(wù)

<service android:name=".Service.ForegroundService"/>

<a name="t18" target="_blank" style="text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);"></a>三、啟動(dòng)前臺服務(wù)

startService(new Intent(this, ForegroundService.class));

<a name="t19" target="_blank" style="text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);"></a>四、運(yùn)行代碼

我們可以看到狀態(tài)欄確實(shí)增加了我們這條通知

image

當(dāng)我們將該程序退出并殺掉的時(shí)候,通過設(shè)置->應(yīng)用->選擇正在運(yùn)行中的應(yīng)用,我們可以發(fā)現(xiàn),我們的程序退出殺掉了,而服務(wù)還在進(jìn)行著

image

IntentService

IntentService是專門用來解決Service中不能執(zhí)行耗時(shí)操作這一問題的,創(chuàng)建一個(gè)IntentService也很簡單,只要繼承IntentService并覆寫onHandlerIntent函數(shù),在該函數(shù)中就可以執(zhí)行耗時(shí)操作了

public class TheIntentService extends IntentService {

    public TheIntentService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        //在這里執(zhí)行耗時(shí)操作
    }
}

AIDL跨進(jìn)程服務(wù)

關(guān)于AIDL跨進(jìn)程服務(wù)的使用和原理分析,可以見我另一篇博客:Android基礎(chǔ)——初學(xué)者必知的AIDL在應(yīng)用層上的Binder機(jī)制

<a name="t22" target="_blank" style="text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);"></a> AccessibilityService無障礙服務(wù)

關(guān)于AccessibilityService無障礙服務(wù)的使用和實(shí)例,可以見我另一篇博客:Android進(jìn)階——學(xué)習(xí)AccessibilityService實(shí)現(xiàn)微信搶紅包插件

<a name="t23" target="_blank" style="text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);"></a> 系統(tǒng)服務(wù)

系統(tǒng)服務(wù)提供了很多便捷服務(wù),可以查詢Wifi、網(wǎng)絡(luò)狀態(tài)、查詢電量、查詢音量、查詢包名、查詢Application信息等等等相關(guān)多的服務(wù),具體大家可以自信查詢文檔,這里舉例幾個(gè)常見的服務(wù)

1. 判斷Wifi是否開啟

WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);
boolean enabled = wm.isWifiEnabled();

需要權(quán)限

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>

2. 獲取系統(tǒng)最大音量

AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
int max = am.getStreamMaxVolume(AudioManager.STREAM_SYSTEM);

3. 獲取當(dāng)前音量

AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
int current = am.getStreamMaxVolume(AudioManager.STREAM_RING);

4. 判斷網(wǎng)絡(luò)是否有連接

ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
boolean isAvailable = info.isAvailable();

需要權(quán)限

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

部分源碼下載

源碼下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容