一篇就夠了系列之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);

    }

效果圖爲:

這裏寫圖片描述

Notication的知識點可以翻牆看Google官方文檔Notification,全中文

差不多就這些了,期待大家的留言!
一篇就夠了系列之Activity全解析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章