1. 程式人生 > >一篇就夠了系列之Service全解析

一篇就夠了系列之Service全解析

前言:

一篇就夠了系列之Activity全解析中詳細介紹了Activity的相關知識點,感興趣的同學可以看看。本篇文章主要介紹下Service的一些學習感悟,希望能對大家有所幫助。
下面從以下四個部分開展:

  1. Service基礎

  2. Service兩種啟動方式

  3. IntentService

  4. Service各種使用場景

Service基礎

定義

Service是一個一種可以在後臺執行長時間執行操作而沒有使用者介面的應用元件。(A Service is an application component representing either an application’s desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use)。相對於Activity可以進行操作而言,Service更像是一個在後臺默默服務的角色,不可感知,但的的確確存在。作為Android四大元件之一,Service是需要在manifest中進行註冊的。

作用

那麼,Service可以用來做什麼呢?App中最常用到Service的功能應該就是下載和音樂播放等了。包括我們整合第三方的推送服務等等。簡單總結起來,Service主要是做不需要依賴UI,長時間執行的一些功能。

Service和Thread的區別

Thread:執行緒,程式執行的最小單位,是cpu分配資源的基本單位,每個程序可以有N個執行緒同時執行。
而通過Service的定義我們知道,它和Thread沒有任何關聯。更多的誤解是來自於,Service這個單詞的中文意義。實際上,和Activity一樣,Service也是執行在主執行緒中(即UI執行緒),所以,雖然說,Service常用來執行長時間執行,耗時操作的程式碼,但是,此時的程式碼我們必須把其放在一個新的子執行緒中執行,這樣才不回造成執行緒阻塞(ANR)。

Service的獨特之處就在於其可以在程式處於後臺的時候,仍然可以繼續執行,此時的Activity會處於OnStop或者OnDestory狀態,這樣,我們的程式就達到了Activity無法執行的功能,Service的價值就體現了出來。

Service兩種啟動方式

Service的啟動需要依賴Activity,並且有兩種啟動方式。

startService:
Activity程式碼:

public class ServiceActivity extends AppCompatActivity {

    @Override
    protected void onCreate
(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_service); //開啟服務 findViewById(R.id.tv_start).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startService(new Intent(ServiceActivity.this,MyService.class)); } }); //停止服務 findViewById(R.id.tv_stop).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { stopService(new Intent(ServiceActivity.this,MyService.class)); } }); } }

Service程式碼:

public class MyService extends Service {

    //第一次進入會呼叫
    @Override
    public void onCreate() {
        Log.i("wy","oncreate");
        super.onCreate();
    }

    //每次進入都會呼叫
    @Override
    public int onStartCommand(Intent intent,  int flags, int startId) {
        Log.i("wy","onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.i("wy","onDestroy");
        super.onDestroy();
    }

    //bind啟動會呼叫
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i("wy","onBind");
        return super.onBind(intent);
    }

}

以上程式碼可以簡單總結幾點:

  • startService和stopService都是Activity中的方法,並且都是通過Intent進行的跳轉。
  • startService呼叫後,Service中會走onCreate和onStartCommand,如果繼續呼叫startService,此時只有onStartCommand的方法會走,onCreate表示建立,既然已將建立過了物件,此時該方法不會繼續走,即使我們把該Activity殺死(注意,不是App殺死),再次進入,仍然如此,說明Service不依賴於該Activity的存在而存在。
  • 只有呼叫stopService方法,才會走onDestroy,說明此時該Service已經殺死。
  • 事實上,我們可以在其他Activity中呼叫stopService方法關閉該Service,這也說明了這種啟動方式,Service和Activity除了開啟和關閉,沒有其他任何關心

bindService

Activity程式碼:

public class ServiceActivity extends AppCompatActivity {

    private MyService.MyBinder mBinder;

    private ServiceConnection mServiceConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinder= (MyService.MyBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

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

        //開啟服務
        findViewById(R.id.tv_bind).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bindService(new Intent(ServiceActivity.this,MyService.class)
                ,mServiceConnection,BIND_AUTO_CREATE);
            }
        });
        //停止服務
        findViewById(R.id.tv_unbind).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                unbindService(mServiceConnection);
            }
        });
    }
}

Service程式碼:

public class MyService extends Service {

    private MyBinder mBinder=new MyBinder();

    public class MyBinder extends Binder{
        public void someMethod(){

        }

    }

    //第一次進入會呼叫
    @Override
    public void onCreate() {
        Log.i("wy","oncreate");
        super.onCreate();
    }

    //每次進入都會呼叫
    @Override
    public int onStartCommand(Intent intent,  int flags, int startId) {
        Log.i("wy","onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.i("wy","onDestroy");
        super.onDestroy();
    }

    //bind啟動會呼叫
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i("wy","onBind");
        return mBinder;
    }

}

仔細觀察可以發現:
- bindService呼叫,首先會走:oncreate,然後呼叫onBind,而onStartCommand並不會走,說明這個方法是startActivity這種啟動方式特有的。
- bindService(new Intent(ServiceActivity.this,MyService.class),mServiceConnection,BIND_AUTO_CREATE);需要傳入三個引數,第一個是Intent,第二個是ServiceConnection,該物件是個interface,有兩個抽象方法,其中會回調出IBinder物件,通過MyService程式碼可以發現,有了IBinder物件,就相當於獲取到Service視力,可以呼叫其中的public的方法,實現了在Activity中呼叫Service的方法。
- 銷燬Activity或者呼叫unbindService,都會走onDestory方法,這也就切合Bind(繫結)這個詞彙,說明該Service和Activity已經繫結在一起,是一對一的關係。

IntentService

可以發現,上面兩種方式,各有利弊,如果我們想開啟一個任務在後臺執行,執行完畢後結束該Service而不需要主動結束。Android中就要這個一個類:IntentService
這是一繼承自Service的藉口,內部利用Handler機制實現不同執行緒間交流通訊,然後呼叫stopSelf結束該Service,直接上原始碼:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    //Handler物件
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            //呼叫我們自己實現的方法,也是需要繼承重寫的方法
            onHandleIntent((Intent)msg.obj);
            //結束對應startid的任務,全部結束後銷燬該Service
            stopSelf(msg.arg1);
        }
    }
    //onStartCommand中呼叫
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    //初始化HandlerThread物件,程式碼是HanderThread物件的標準寫法,意義是在子執行緒中建立了一個Handler,也就是ServiceHandler物件(一般預設生成的Handler物件都是在主執行緒中)
    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    //在onStartCommand中呼叫
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
    //每次點選startService就會呼叫該方法
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
    //Service銷燬呼叫,mServiceLooper.quit(),釋放記憶體,必須
    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }
    //工作執行緒,可以進行耗時操作
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

我們的程式碼:

public class MyIntentService extends IntentService {
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public MyIntentService() {

        super("MyIntentService");
        Log.i("intentservice","construct");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.i("intentservice","onHandleIntent");
        try
        {
            //模擬上傳耗時
            Thread.sleep(5000);

        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }


    @Override
    public void onCreate() {
        Log.i("intentservice","onCreate");
        super.onCreate();
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Log.i("intentservice","onstart"+startId);
        super.onStart(intent, startId);
    }

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

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("intentservice","onDestroy");
    }

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

連續6下點選startService,Log為:

construct
onCreate
onStartCommand
onstart1
onHandleIntent
onStartCommand
onstart2
onStartCommand
onstart3
onStartCommand
onstart4
onStartCommand
onstart5
onStartCommand
onstart6
onHandleIntent
onHandleIntent
onHandleIntent
onHandleIntent
onHandleIntent
onDestroy

大家可以嘗試執行,列印Log加深理解

Service各種使用場景及tips

  • IntentService:很明顯,這種service的用途會很廣泛,比如我們進行圖片下載,退出應用時一些狀態的儲存,甚至是啟動頁中需要初始化的一些耗時的程式碼
  • startService和bindService可以混用,只需要記住一點:startService只是啟動一個Service,與啟動者沒有關聯,殺死必須要呼叫stopService或者是stopself,bindService依賴於呼叫者,當呼叫者銷燬後,它也會跟著銷燬。
  • service的呼叫者有Activity,Service和ContentProvider
  • 在後臺執行的service在系統資源緊張的情況下還是會被系統回收,為了提高優先順序,我們可以使用前置Service,比如我們騎共享單車的的通知欄:
    @Override
    public void onCreate() {
        Log.i("wy","oncreate");
        super.onCreate();

        //獲取NotificationManager例項
        NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        //例項化NotificationCompat.Builde並設定相關屬性
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                //設定小圖示
                .setSmallIcon(R.mipmap.ic_launcher)
                //設定通知標題
                .setContentTitle("最簡單的Notification")
                //設定通知內容
                .setContentText("只有小圖示、標題、內容");
        //設定通知時間,預設為系統發出通知的時間,通常不用設定
        //.setWhen(System.currentTimeMillis());
        //通過builder.build()方法生成Notification物件,併發送通知,id=1
        Notification notification=builder.build();
        notifyManager.notify(1, notification);


        startForeground(1, notification);

    }

效果圖為:

這裡寫圖片描述

相關推薦

系列Service解析

前言: 一篇就夠了系列之Activity全解析中詳細介紹了Activity的相關知識點,感興趣的同學可以看看。本篇文章主要介紹下Service的一些學習感悟,希望能對大家有所幫助。 下面從以下四個部分開展: Service基礎 Service兩種啟動方式

系列Handler解析

前言: 兩年前寫過一篇Java中的多執行緒Thread Runnable及android的handler,現在想從原始碼角度好好分析下Handler及HandlerThread,畢竟Handler在Android開發和麵試中都是涉及很多的知識點,所以很有必要全

系列BroadcastReceiver解析

前言: 上一篇一篇就夠了系列之Service全解析,介紹了Android四大元件的Service,本篇繼續來介紹Android四大元件之BroadcastReceiver(廣播接收者)。 Broadacast(廣播): 首先,我們應該清楚在Andr

系列Activity解析

前言: Activity作為Android四大元件之一Google官方文件,是Android開發中最基本最常用的東西,那麼,Activity的定義到底是什麼呢? 從下面幾個方面介紹下Activity: 生命週期 任務棧 啟動模式 scheme跳轉協議

系列Android Manifest解析

前言: 前面幾篇介紹了android四大元件的知識,可以發現,四大元件都必須在一個叫AndroidManifest.xml檔案中進行註冊,那麼該檔案的作用是什麼呢?你們的內容各有什麼意義呢?帶著這些疑問,來開始下面內容的學習。官方文件,很詳細 作用:

【轉】【修真院“善良”系列十八】WEB程序員從零開始到就業的資料V1.0——只看這

absolute feed 自己 session rem 好的 ans 一個 css樣式 這是兩年以來,修真院收集整理的學習資料順序。以CSS15個任務,JS15個任務為基礎,分別依據要完成任務的不同的技能點,我們整理出來了這麽一篇在學習的時候需要看到的資料。這是Versi

【MYSQL學習筆記02】MySQL的高階應用Explain(完美詳細版,看這

版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/wx1528159409 最近學習MySQL的高階應用Explain,寫一篇學習心得與總結,目錄腦圖如下: 一、Explain基本概念 1. Explain定義 · 我們知道M

【死磕Java並發】—–J.U.CAQS(

ini tle 循環 針對 可能 width als 如果 boolean [隱藏目錄]1 獨占式1.1 獨占式同步狀態獲取1.2 獨占式獲取響應中斷1.3 獨占式超時獲取1.4 獨占式同步狀態釋放2 共享式2.1 共享式

java 設計模式單例模式

單例模式 單例物件(Singleton)是一種常用的設計模式。 在Java應用中,單例物件能保證在一個JVM中,該物件只有一個例項存在。這樣的模式有幾個好處: 1、某些類建立比較頻繁,對於一些大型的

Android開發顯示(弄懂ppi、dpi、pt、px、dp、dip、sp之間的關係看這

版權申明】非商業目的註明出處可自由轉載 博文地址:https://blog.csdn.net/ShuSheng0007/article/details/85165773 出自:shusheng007 文章目錄 概述 要解決的疑問 概念篇

【Flutter 系列——1】Flutter環境搭建及配置這(Windows)

最近正式入坑Flutter,首先從環境搭建開始,看了網上好多關於Windows環境搭建的資料,基本都是按官方文件寫的,看完的感受是,還不如直接去看官方文件。 本文主要總結我實際搭建的過程,最後發現不一定按網上那些部落格或者官方文件寫的來也可以搭建成功。 總的來說需要的

【死磕Java併發】—–J.U.CAQS(

作者:大明哥  原文地址:http://cmsblogs.com 越是核心的東西越是要反覆看,本文篇幅較長,希望各位細細品讀,來回多讀幾遍理解下。 AQS簡介 java的內建鎖一直都是備受爭議的,在JDK 1.6之前,synchronized這個重量級鎖其效能一直都

java8lambda表達式看這

重要 遍歷 equal jdk 之一 con res sts ()  java8增加了許多新特性,其中lambda表達式可以說為最重要的特性之一,本文將從如下幾個方面來學習lambda: 1、lambda表達式的基本定義  2、lambda表達式的語法  

【Java面試題系列】:Java基礎知識面試題,看這(持續更新)

文中面試題從茫茫網海中精心篩選,如有錯誤,歡迎指正! 1.前言 ​ 參加過社招的同學都瞭解,進入一家公司面試開發崗位時,填寫完個人資訊後,一般都會讓先做一份筆試題,然後公司會根據筆試題的回答結果,確定要不要繼續此次面試,如果答的不好,有些公司可能會直接說“技術經理或者總監在忙,你先回去等通知吧”,有些公司

【Java面試題系列】:Java基礎知識面試題,看這

路徑 拼接 i++ misc min 中新 dem 總結 內容 文中面試題從茫茫網海中精心篩選,如有錯誤,歡迎指正! 1.前言 參加過社招的同學都了解,進入一家公司面試開發崗位時,填寫完個人信息後,一般都會讓先做一份筆試題,然後公司會根據筆試題的回答結果,確定要不要繼續此

java高併發系列 - 第23天:JUC中原子類,

這是java高併發系列第23篇文章,環境:jdk1.8。 本文主要內容 JUC中的原子類介紹 介紹基本型別原子類 介紹陣列型別原子類 介紹引用型別原子類 介紹物件屬性修改相關原子類 預備知識 JUC中的原子類都是都是依靠volatile、CAS、Unsafe類配合來實現的,需要了解的請移步: volati

【K8S】Service服務詳解,看這!!

k8s用名稱空間namespace把資源進行隔離,預設情況下,相同的名稱空間裡的服務可以相互通訊,反之進行隔離。 1.1 Service Kubernetes中一個應用服務會有一個或多個例項(Pod,Pod可以通過rs進行多複本的建立),每個例項(Pod)的IP地址由網路外掛動態隨機分配(Pod重啟後IP地址

Java中的多線程你只要看這

== 討論 cin 線程池。 locking nth lis dset tro 引 如果對什麽是線程、什麽是進程仍存有疑惑,請先Google之,因為這兩個概念不在本文的範圍之內。 用多線程只有一個目的,那就是更好的利用cpu的資源,因為所有的多線程代碼都可以用單線程來實現。

關於HTTP協議,

改變 就會 足夠 options 服務器交互 視頻 用戶 base64加密 英文字母 關於HTTP協議,一篇就夠了 作者 RaphetS 關註 2016.10.13 06:48* 字數 4806 閱讀 12969評論 26喜歡 203贊賞 1 HTT

4-關於HTTP協議,

nal ont 操作 規則 自身 .com 工作 擴展 概念 HTTP簡介 HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用於從萬維網(WWW:World Wide Web )服務器傳輸超文本到本地瀏覽器的傳送協議。 H