Android必知必會的四大組件 -- Service篇

前言

寫着一篇文章的緣由,主要是由於在面試中,服務這個關鍵詞的出現頻率很是高。不少時候,面試官會問你,Service中可否進行耗時操做? 咱們稍後就會揭曉那麼這個答案。java

思惟導圖

生命週期

生命週期圖示

由圖中能夠直觀的看出幾點。android

啓動方式 存在方式
startService() 獨立於Activity運行,自行結束或被叫停
bindService() 綁定於Activity運行,Activity結束時,會被叫停
涉及方法 用途
onCreate()
onDestroy()
onStartCommand() 用於計數,服務被調用的次數
onBind() 與Activity組件綁定
onUnbind() 與Activity組件解綁

使用方法

Service方法須要在AndroidManifest.xml中進行註冊面試

// 第一步:在AndroidManifest.xml中進行註冊
<service android:name=".LocalService"/>

// 第二步:啓動
① startService(Intent);
② bindService(Intent, ServiceConnection, Int);

// 第三步:解綁(使用方法② 啓動時操做)
unBindService(ServiceConnection);

// 第四步:暫停
stopService(Intent);
複製代碼

Activity和Service的通訊

ActivityService的通訊其實就是基於IBinder來進行實現的。可是IBinder實際上是一個接口,對咱們而言通常使用他的實現類Binder並經過強制轉換來完成操做。bash

/** * Service方法繼承 * onBind()是一個抽象方法。 */
public class LocalService extends Service {
    private final IBinder binder = new ServiceBinder();

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

    public class ServiceBinder extends Binder {
        LocalService getLocalService(){
            return LocalService.this;
        }
    }
}
複製代碼

以上代碼,是一個用於通訊的基礎版本。異步

既然須要通訊,那咱們總須要知道對方是誰,若是使用的是startService(),上文已經提到他是獨立於Activity的,因此勢必使用的是bindService()ide

在上文的使用方法中已經提到了bindService()使用到的參數,IntentServiceConnectionIntoop

ServiceConnection

/** * bindService()方法中的參數之一。 * 用於對service進行操做 */
ServiceConnection connection = new ServiceConnection() {
           // Activity和Service綁定時調用
            @Override
            public void onServiceConnected(ComponentName name, IBinder binder) {
                // 基於Binder拿到咱們要的Service
                service = ((LocalService.ServiceBinder)binder).getLocalService();
                // 幹你須要乾的事情
            }
            // Activity和Service解綁時調用
            @Override
            public void onServiceDisconnected(ComponentName name) {
                service = null;
            }
        };
複製代碼

Int

  • BIND_AUTO_CREATE:收到綁定需求,若是Service還沒有建立,則當即建立。
  • BIND_DEBUG_UNBIND:用於測試使用,對unbind調用不匹配的調試幫助。
  • BIND_NOT_FOREGROUND:不容許此綁定將目標服務的進程提高到前臺調度優先級

這是一個已經存在於Service類中的值,這裏並不所有例舉,通常來講都是使用BIND_AUTO_CREATEpost

必需要調用的unbindService(ServiceConnection)

爲何咱們必定要調用這個方法,若是咱們不解綁會出現什麼樣的問題?學習

通過測試,Logcat中爆出了這樣的錯誤Activity has leaked ServiceConnection that was originally bound here。也就是說ServiceConnection內存泄漏了。這也是爲何咱們一直說須要解綁的緣由。測試

IntentService

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

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        
    }
}
複製代碼

先看一段咱們的繼承代碼,和Service不一樣的地方就是,必須重寫的方法是onHandleIntent(Intent intent)。 那咱們也和以前同樣作一個源碼導讀好了。

IntentService源碼導讀

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    @UnsupportedAppUsage
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;
    
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }
}
複製代碼

其實從整個代碼的變量咱們已經能夠作一個猜想了。Looper+Handler+Service的組成成分。那它的處理過程勢必依賴於一個Handler的通訊機制。另外看到了ServiceHandler中的stopSelf()方法,咱們也就清楚了一個問題爲何咱們不須要去控制IntentService的暫停。

接下來從生命週期的角度來看看這個IntentService,由於Binder機制上是一致的,因此分析主線就是onCreate() --> onStartCommand() --> onDestroy()

onCreate()

public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
複製代碼

建立了一個HandlerThread,去初始化了LooperHandler,也就說明服務在內部處理。

onStartCommand()

public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId); // 1 -->
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; // 2 -->
    }
複製代碼

onStart()

public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
複製代碼

其餘都是和Handler一致的,總體流程也就是Message封裝,再經過Handler進行一個發送。

mRedelivery

這個變量是幹什麼的?

/** * Constant to return from {@link #onStartCommand}: if this service's * process is killed while it is started (after returning from * {@link #onStartCommand}), and there are no new start intents to * deliver to it, then take the service out of the started state and * don't recreate until a future explicit call to * {@link Context#startService Context.startService(Intent)}. The * service will not receive a {@link #onStartCommand(Intent, int, int)} * call with a null Intent because it will not be restarted if there * are no pending Intents to deliver. * * <p>This mode makes sense for things that want to do some work as a * result of being started, but can be stopped when under memory pressure * and will explicit start themselves again later to do more work. An * example of such a service would be one that polls for data from * a server: it could schedule an alarm to poll every N minutes by having * the alarm start its service. When its {@link #onStartCommand} is * called from the alarm, it schedules a new alarm for N minutes later, * and spawns a thread to do its networking. If its process is killed * while doing that check, the service will not be restarted until the * alarm goes off. */
    public static final int START_NOT_STICKY = 2;

    /** * Constant to return from {@link #onStartCommand}: if this service's * process is killed while it is started (after returning from * {@link #onStartCommand}), then it will be scheduled for a restart * and the last delivered Intent re-delivered to it again via * {@link #onStartCommand}. This Intent will remain scheduled for * redelivery until the service calls {@link #stopSelf(int)} with the * start ID provided to {@link #onStartCommand}. The * service will not receive a {@link #onStartCommand(Intent, int, int)} * call with a null Intent because it will only be restarted if * it is not finished processing all Intents sent to it (and any such * pending events will be delivered at the point of restart). */
    public static final int START_REDELIVER_INTENT = 3;
複製代碼

一大段冗長的英文很煩,更況且我也就低分飄過6級的水平呢,哈哈哈哈!!

就不折磨大家了,直接作出一個解釋吧。

  • START_NOT_STICKY:默認模式,這是一個允許被殺的模式,隨時容許被叫停
  • START_REDELIVER_INTENT:告訴系統在崩潰後從新啓動服務,並從新傳遞在崩潰時存在的意圖

好了,以上基本就是整個IntentService的介紹了。

總結

  1. 在ANR機制中,Service的響應時長不能超過20s,其實也能夠比較直觀的看出,Service其實並不能進行所謂耗時操做。可是若是加上了Thread進行異步處理,那麼其實他仍是能夠進行耗時操做的。(具體看你怎麼進行回答,主要仍是一個知識點,Service運行在主線程)
  2. Service存在的緣由是Activity是一個常常會被銷燬的組件,雖然咱們一樣能夠經過Thread進行異步操做,可是當Activity實例被銷燬時,相應的捆綁在Activity生命週期內的Thread實例咱們也沒有能力再去尋找了。

以上就是個人學習成果,若是有什麼我沒有思考到的地方或是文章內存在錯誤,歡迎與我分享。


相關文章推薦:

手撕AsyncTask

幾張圖讓你明白Android四大啓動模式

Glide都在用的LruCache,你會幾分?

相關文章
相關標籤/搜索