Android四大組件之Service

Service概述

  • Service是一種可以在后臺(tái)執(zhí)行長(zhǎng)時(shí)間運(yùn)行操作而沒有用戶界面的應(yīng)用組件。

  • 它可由其它應(yīng)用組件(如Activity)啟動(dòng),一旦被啟動(dòng)將在后臺(tái)一直運(yùn)行,即使啟動(dòng)服務(wù)的組件已銷毀也不受影響。非常適合用于執(zhí)行那些不需要和用戶交互而且還需要長(zhǎng)期運(yùn)行的任務(wù)。

  • 組件可以綁定到服務(wù),從而進(jìn)行交互。

  • 適用場(chǎng)景:播放音樂;處理網(wǎng)絡(luò)事務(wù);在后臺(tái)記錄位置信息等。

Service分類

  • 按運(yùn)行地點(diǎn)可分為本地服務(wù)(LocalService)和遠(yuǎn)程服務(wù)(RemoteService)

    1. 本地服務(wù):依附在主進(jìn)程上而不是獨(dú)立進(jìn)程,所以不能進(jìn)行耗時(shí)操作,可以在service里面創(chuàng)建一個(gè)Thread來執(zhí)行耗時(shí)任務(wù)。主進(jìn)程被kill后,服務(wù)便終止。

    2. 遠(yuǎn)程服務(wù):即獨(dú)立服務(wù),對(duì)應(yīng)進(jìn)程名為所在包名加上指定的android:process字符串。因?yàn)槭仟?dú)立進(jìn)程,所以Activity所在進(jìn)程被kill時(shí),該服務(wù)不受其它進(jìn)程影響??蛇M(jìn)行跨進(jìn)程通信(IPC)。

  • 按運(yùn)行類型可分為前臺(tái)服務(wù)(Foreground Service)和后臺(tái)服務(wù)

    1. 前臺(tái)服務(wù):會(huì)在通知欄顯示Ongoing的Notification。當(dāng)服務(wù)被終止時(shí),通知欄的Notification也會(huì)消失,這樣對(duì)于用戶有一定的通知作用。常見如音樂播放服務(wù)。

    2. 后臺(tái)服務(wù):默認(rèn)的服務(wù)即為后臺(tái)服務(wù),不會(huì)顯示在Notification。當(dāng)服務(wù)被終止時(shí),用戶是看不到效果的。某些不需要運(yùn)行或終止提示的服務(wù),如天氣更新,日期同步等。

  • 按使用方式可分為:

    1. 使用startService:主要用于啟動(dòng)一個(gè)服務(wù)執(zhí)行后臺(tái)任務(wù),不進(jìn)行通信。對(duì)應(yīng)的停止服務(wù)為stopService。

    2. 使用bindService:該方式啟動(dòng)的服務(wù)可進(jìn)行通信,調(diào)用者掛掉后,服務(wù)也會(huì)跟著掛掉。對(duì)應(yīng)的停止服務(wù)為unbindService。

    3. 使用startService和bindService:停止服務(wù)stopService和unbindService也必須使用。

Service的生命周期

Service生命周期
  • onCreate(): 首次創(chuàng)建服務(wù)時(shí)調(diào)用。如果服務(wù)已運(yùn)行,則不會(huì)調(diào)用。只會(huì)在第一次創(chuàng)建Service時(shí)調(diào)用,適合完成一些初始化工作。

  • onStartCommand(): 當(dāng)另一個(gè)組件通過調(diào)用startService()請(qǐng)求啟動(dòng)服務(wù)時(shí),系統(tǒng)將調(diào)用該方法。

  • onDestroy(): 當(dāng)服務(wù)不再使用且將被銷毀時(shí),系統(tǒng)調(diào)用該方法。

  • onBind(): 當(dāng)另一個(gè)組件通過調(diào)用bindService()與服務(wù)綁定時(shí),系統(tǒng)將調(diào)用該方法。該方法是抽象方法,所以onBind()方法必須重寫。

  • onUnbind(): 當(dāng)另一個(gè)組件通過調(diào)用unbindService()與服務(wù)解綁時(shí),系統(tǒng)將調(diào)用該方法。

  • onRebind(): 當(dāng)startService和bindService都啟用,之后調(diào)用unbindService解綁服務(wù)時(shí),另一個(gè)組件想要與該服務(wù)綁定,并且之前onUnbind()方法返回true,該方法將被調(diào)用。

生命周期調(diào)用:

  • 啟動(dòng)-->停止Service:startService() > onCreate() > onStartCommand() > onStartCommand() (多次啟動(dòng))--> stopService() > onDestroy()

  • 綁定-->解綁Service:bindService() > onCreate() > onBind() --> unbindService() > onUnbind() > onDestroy()

  • 啟動(dòng)綁定-->解綁停止Service:startService() > onCreate() > onStartCommand() > bindService() > onBind() --> unbindService() > onUnbind() > stopService() > onDestroy()

  • 解綁綁定Service:unbindService() > onUnbind(ture) > bindService() > onRebind()

代碼實(shí)現(xiàn)

MyService.java代碼如下:

public class MyService extends Service {
    public static final String TAG = "Joy";
    //通過binder實(shí)現(xiàn)調(diào)用者client與Service之間的通信
    private MyBinder binder = new MyBinder();

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

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

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"onDestroy");
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG,"onUnbind");
        return super.onUnbind(intent);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG,"onBind");
        return binder;
    }

    public class MyBinder extends Binder {
        public MyService getService() {
            return MyService.this;
        }
    }
    //該方法提供給綁定者調(diào)用
    public int getNumber() {
        return new Random().nextInt();
    }
}
  • 使用startService啟動(dòng)service
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent(this, MyService.class);
        startService(intent);
        startService(intent);
        stopService(intent);
        startService(intent);
    }
}

log如下:

02-27 16:22:02.518 26058-26058/com.android.myapplication D/Joy: oncreate
02-27 16:22:02.518 26058-26058/com.android.myapplication D/Joy: onStartCommand startId = 1
02-27 16:22:02.518 26058-26058/com.android.myapplication D/Joy: onStartCommand startId = 2
02-27 16:22:02.518 26058-26058/com.android.myapplication D/Joy: onDestroy
02-27 16:22:02.518 26058-26058/com.android.myapplication D/Joy: oncreate
02-27 16:22:02.518 26058-26058/com.android.myapplication D/Joy: onStartCommand startId = 1

總結(jié):可以看出多次調(diào)用startService不會(huì)重復(fù)執(zhí)行onCreate回調(diào),但每次都會(huì)執(zhí)行onStartCommand回調(diào),且只需要調(diào)用一次stopService方法便可以停止服務(wù),無論之前被調(diào)用了幾次啟動(dòng)服務(wù)方法。

  • 使用bindService啟動(dòng)service
public class MainActivity extends AppCompatActivity {
    public static final String TAG = "Joy";
    private Button bind;
    private Button unbind;
    private MyService myService = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bind = findViewById(R.id.bind);
        unbind = findViewById(R.id.unbind);
        bind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bindService(new Intent(MainActivity.this, MyService.class), conn, Context.BIND_AUTO_CREATE);
            }
        });

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

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            MyService.MyBinder myBinder = (MyService.MyBinder) service;
            myService = myBinder.getService();
            int num = myService.getNumber();
            Log.d(TAG,"onServiceConnected");
            Log.d(TAG,"num = "+num);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG,"onServiceDisconnected");
        }
    };
}

啟動(dòng)界面如下:



點(diǎn)擊BIND SERVICE按鈕log如下:

02-27 16:43:47.166 16555-16555/com.android.myapplication D/Joy: oncreate
02-27 16:43:47.166 16555-16555/com.android.myapplication D/Joy: onBind
02-27 16:43:47.176 16555-16555/com.android.myapplication D/Joy: onServiceConnected
02-27 16:43:47.176 16555-16555/com.android.myapplication D/Joy: num = 445451174

點(diǎn)擊UNBIND SERVICE按鈕或者按返回鍵log如下:

02-27 16:44:37.036 16555-16555/com.android.myapplication D/Joy: onUnbind
02-27 16:44:37.036 16555-16555/com.android.myapplication D/Joy: onDestroy

總結(jié):

  1. 綁定者可以調(diào)用服務(wù)里面的方法。
  2. 即使多次點(diǎn)擊BIND SERVER按鈕,也不會(huì)多次觸發(fā)onBind()方法。
  3. Service中需要?jiǎng)?chuàng)建一個(gè)實(shí)現(xiàn)IBinder的內(nèi)部類。在onBind()方法中需返回一個(gè)IBinder實(shí)例,不然onServiceConnected()方法不會(huì)調(diào)用。
  4. onServiceDisconnected() 在service正常關(guān)閉的情況下是不會(huì)被調(diào)用的,該方法只在Service被異常中止或者被殺死的時(shí)候調(diào)用。
  • Remote Service啟動(dòng)服務(wù)
    因?yàn)檫h(yuǎn)程服務(wù)是在另一個(gè)進(jìn)程,它不受其它進(jìn)程影響,可以為其它應(yīng)用程序提供調(diào)用的接口--實(shí)際上就是進(jìn)程間通信(IPC)。Android提供了AIDL工具來幫助進(jìn)程間接口的建立。

前臺(tái)服務(wù)

前臺(tái)服務(wù)是那些被認(rèn)為用戶所認(rèn)可的且在系統(tǒng)內(nèi)存不足時(shí)不允許系統(tǒng)殺死的服務(wù)。前臺(tái)服務(wù)必須給狀態(tài)欄提供一個(gè)通知,它被放到正在運(yùn)行(Ongoing)標(biāo)題之下,表示只有在這個(gè)服務(wù)被終止或從前臺(tái)主動(dòng)移除通知后才能被解除。

  • 使用原因:
    在一般情況下,Service都是在后臺(tái)運(yùn)行,它們的系統(tǒng)優(yōu)先級(jí)相對(duì)較低。當(dāng)系統(tǒng)內(nèi)存不足時(shí),在后臺(tái)運(yùn)行的Service就有可能被回收。所以可以選擇將需要保持運(yùn)行的Service設(shè)置為前臺(tái)服務(wù)。

  • 代碼實(shí)現(xiàn) :

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG,"onStartCommand startId = "+startId);
        Notification.Builder builder = new Notification.Builder(this.getApplicationContext());
        Intent intent1 = new Intent(this,MainActivity.class);

        builder.setContentIntent(PendingIntent.getActivity(this,0,intent1,0))
                .setLargeIcon(BitmapFactory.decodeResource(this.getResources(),R.mipmap.ic_launcher))
                .setContentTitle("title")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentText("content")
                .setWhen(System.currentTimeMillis());

        Notification notification = builder.build();
        notification.defaults = Notification.DEFAULT_SOUND;

        startForeground(110,notification);
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopForeground(true);
        Log.d(TAG,"onDestroy");
    }

重寫onStartCommand()方法,使用startForeground(int, Notification)方法來啟動(dòng)service。需要在onDestroy()方法中通過stopForeground(true)方法來取消前臺(tái)運(yùn)行狀態(tài)。

IntentService

Service不能直接處理耗時(shí)的任務(wù),一般可以使用Service+Thread來實(shí)現(xiàn)。而IntentService可以處理異步任務(wù)請(qǐng)求。

  • IntentService是Service的子類,內(nèi)部有一個(gè)工作線程來處理耗時(shí)操作。
  • 當(dāng)任務(wù)執(zhí)行完成后,IntentService會(huì)自動(dòng)停止,不需要手動(dòng)結(jié)束。
  • 如果多次啟動(dòng)IntentService,每個(gè)耗時(shí)操作會(huì)以工作隊(duì)列的方式在IntentService的onHandleIntent() 方法中執(zhí)行,依次執(zhí)行,使用串行的方式,執(zhí)行完自動(dòng)結(jié)束。無需處理多線程問題。

相關(guān)代碼:

MyIntentService.java代碼:

public class MyIntentService extends IntentService {

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        long endTime = System.currentTimeMillis() + 20 *1000;
        Log.d("MyIntentService","onHandleIntent");
        while (System.currentTimeMillis() < endTime) {
            synchronized (this) {
                try {
                    wait(endTime - System.currentTimeMillis());
                } catch (Exception e) {

                }
            }
        }
        Log.d("MyIntentService","finished");
    }
}

MainActivity.java利用startService啟動(dòng):

        final Intent intent = new Intent(MainActivity.this, MyIntentService.class);
        startService(intent);

log如下:

02-27 15:36:18.061 23525-24995/com.android.myapplication D/MyIntentService: onHandleIntent
0227-04 15:36:38.061 23525-24995/com.android.myapplication D/MyIntentService: finished

因MyIntentService繼承了IntentService,所以不需要實(shí)現(xiàn)onBind(), onStartCommand()方法,只需實(shí)現(xiàn)onHandleIntent()即可。

不建議通過bindService來啟動(dòng)IntentService,因?yàn)镮ntentService源碼中onBind()默認(rèn)返回null。

Service與Thread的區(qū)別

  • Thread:Thread 是程序執(zhí)行的最小單元,它是分配CPU的基本單位。Thread可以用來執(zhí)行一些異步的操作。

  • Service:Service是android的一種機(jī)制,當(dāng)它是Local Service,對(duì)應(yīng)的Service是運(yùn)行在主進(jìn)程的 main 線程上的。如果是Remote Service,對(duì)應(yīng)的Service是運(yùn)行在獨(dú)立進(jìn)程的 main 線程上。

Thread的運(yùn)行是獨(dú)立于Activity的,當(dāng)Activity被finish后,如果Thread沒有被主動(dòng)停止或run()方法沒有執(zhí)行完畢,Thread會(huì)一直執(zhí)行。因此當(dāng)Activity被finish后,Thread引用則不被持有,且沒有辦法在別的Activity對(duì)同一個(gè)Thread進(jìn)行控制。
假如有需求的話,可以創(chuàng)建并啟動(dòng)一個(gè)Service,在Service里面創(chuàng)建、運(yùn)行并控制該Thread,即可實(shí)現(xiàn)對(duì)Thread的控制。

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

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