Android四大元件之Service,以及IntentService

StarkSong發表於2019-03-07

@[toc]

Service基本介紹

Android四大元件之一,沒有介面的後臺服務。我們如果想要在後臺執行一些任務,會用到Service。比如:後臺下載更新;後臺播放音樂等等。

Service生命週期

  • Service有兩種啟動方式,通過兩種不同的啟動方式,生命週期執行方法也是不一樣的:

    1. startService:onCreate --> onStartCommand --> onStart ---> onDestroy

      相對應的停止方法為:stopService

    重複呼叫 startService 不會重新走 onCreate方法,只會走 onStartCommand --> onStart

    1. bindService: onCreate --> onBind ---> onUnBind ---> onDestroy

      相對應的解綁方法為: unBindService

    重複呼叫 bindService 方法,不會再次走 onCreate 方法,只會走 onBind 方法

  • 混合啟動:

    service的onCreate只會走一次。

    先呼叫 startService,再呼叫 bindService。 onCreate --> onStartCommand --> onStart --(此時呼叫 bindService )---> onBind。

    先呼叫 bindService,再呼叫 startService。 onCreate --> onBind --(此時呼叫 startService )---> onStartCommand --> onStart。

    混合啟動想要銷燬 Service 必須 stopService、unBindService 都呼叫,否則無效。

Service的基本用法

  • 首先建立一個class繼承Service

    繼承 service 的服務,在裡面不能進行耗時操作,因為它本身也是依賴於主程式的,不是一個單獨的執行緒,無法進行耗時操作。


public class MyService extends Service {
    public static final String TAG = "MyService";

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

    @Override
    public void onStart(Intent intent, int startId) {
        Log.i(TAG, "onStart: ");
        super.onStart(intent, startId);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand: ");
        //在這裡建立一個子執行緒,睡眠5秒鐘後,呼叫關閉 service 方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    stopService1();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind: ");
        // 返回代理物件
        return new MyBinder();
    }

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

    public void stopService1() {
        Log.i(TAG, "停止服務");
        stopSelf();
    }
    
    public void receiveMsg(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
    
    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy: ");
        super.onDestroy();
    }
    
    //建立一個Service的代理類,通過它可以呼叫Service中的方法。
    public class MyBinder extends Binder {

        public void sendMsg(String msg) {
            receiveMsg(msg);
        }
    }
}

複製程式碼
  • 在 AndroidManifest.xml 檔案中註冊:
<!-- enabled 是否可以被啟用,如果設定為false,就無法啟用Service-->
<!-- exported 是否可以被外部*程式*啟用,-->
<service
    android:name=".service.MyService"
    android:enabled="true"
    android:exported="false" />
複製程式碼
  • 在Activity中啟動,以及關閉,以及怎麼呼叫Service中的方法:

public class ServiceActivity extends AppCompatActivity implements View.OnClickListener {

    private Button mBtnStartService;
    private Button mBtnStopService;
    private Button mBtnBindService;
    private Button mBtnUnbindService;
    private MyService.MyBinder myIBinder;
    private MyConnection myConnection;
    private Button mBtnCallService;//呼叫Service中的方法

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

    private void initView() {
        mBtnStartService = findViewById(R.id.btn_start_service);
        mBtnStopService = findViewById(R.id.btn_stop_service);
        mBtnBindService = findViewById(R.id.btn_bind_service);
        mBtnUnbindService = findViewById(R.id.btn_unbind_service);
        mBtnCallService = findViewById(R.id.btn_call_service);
    }

    private void initAction() {
        mBtnStartService.setOnClickListener(this);
        mBtnStopService.setOnClickListener(this);
        mBtnBindService.setOnClickListener(this);
        mBtnUnbindService.setOnClickListener(this);
        mBtnCallService.setOnClickListener(this);
    }

    private void initData() {
        myConnection = new MyConnection();
    }

    @Override
    public void onClick(View v) {
        Intent service = new Intent(this, MyService.class);
        switch (v.getId()) {
            case R.id.btn_start_service:
                startService(service);
                break;
            case R.id.btn_stop_service:
                stopService(service);
                break;
            case R.id.btn_bind_service:
                bindService(service, myConnection, BIND_AUTO_CREATE);
                break;
            case R.id.btn_unbind_service:
                unbindService(myConnection);
                break;
            case R.id.btn_call_service:
                myIBinder.sendMsg("我是Activity傳來的訊息");
                break;
            default:
                break;
        }
    }

    public class MyConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
        //獲取Service的代理
            myIBinder = (MyService.MyBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }
}

複製程式碼
  • xml佈局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".service.ServiceActivity">

    <Button
        android:id="@+id/btn_start_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="start_service" />

    <Button
        android:id="@+id/btn_stop_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="stop_service" />

    <Button
        android:id="@+id/btn_bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="bind_service" />

    <Button
        android:id="@+id/btn_unbind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="unbind_service" />

    <Button
        android:id="@+id/btn_call_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="呼叫Service內部方法" />

</LinearLayout>
複製程式碼
  • 實驗結果:

    點選 bindService 繫結Service,然後點選 btn_call_service ,彈出吐司 “我是Activity傳來的訊息”,完成Activity和Service的呼叫

  • 注意: Service是有可能繫結失敗的,具體原因和解決方案:bindService不呼叫onServiceConnected的問題

  • onStartCommand:

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
    複製程式碼

    onStartComand使用時,返回的是一個(int)整形。

    這個整形可以有四個返回值:start_sticky、start_no_sticky、START_REDELIVER_INTENT、START_STICKY_COMPATIBILITY。

    它們的含義分別是:

    1. START_STICKY:如果service程式被kill掉,保留service的狀態為開始狀態,但不保留遞送的intent物件。隨後系統會嘗試重新建立service,由於服務狀態為開始狀態,所以建立服務後一定會呼叫onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啟動命令被傳遞到service,那麼引數Intent將為null。

    2. START_NOT_STICKY:“非粘性的”。使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統不會自動重啟該服務

    3. START_REDELIVER_INTENT:重傳Intent。使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統會自動重啟該服務,並將Intent的值傳入。

    4. START_STICKY_COMPATIBILITY:START_STICKY的相容版本,但不保證服務被kill後一定能重啟。

    flags表示啟動服務的方式:

    Additional data about this start request. Currently either 0,START_FLAG_REDELIVERY,or START_FLAG_RETRY。

    1. START_FLAG_REDELIVERY:如果你實現onStartCommand()來安排非同步工作或者在另一個執行緒中工作, 那麼你可能需要使用START_FLAG_REDELIVERY來讓系統重新傳送一個intent。這樣如果你的服務在處理它的時候被Kill掉, Intent不會丟失.

    2. START_FLAG_RETRY:表示服務之前被設為START_STICKY,則會被傳入這個標記。

IntentService:

  • 介紹

    相對於Service而言,IntentService本身擁有一個子執行緒,可以進行耗時操作,在任務程式碼完成後,會自動銷燬服務,不用手動呼叫。

  • 程式碼:

public class MyIntentService extends IntentService {
    private static final String TAG = "MyIntentService";

    public MyIntentService() {
        super("MyIntentService");
    }
多出一個onHandleIntent方法,可以在裡面做耗時操作
    @Override
    protected void onHandleIntent(Intent intent) {
        try {
            Thread.sleep(5000);
            Log.i(TAG, "onHandleIntent: 睡了5秒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


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

複製程式碼
  • 步驟:和Service一樣,在 Activity 中呼叫 startService 方法開啟服務。只不過 IntentService 在開啟後,onHandleIntent 中的程式碼執行完後,會自動銷燬服務。

  • 實驗結果:

    2019-03-05 00:26:14.972 16106-16106/com.sjc.myapplication I/MyIntentService: onCreate: 
    2019-03-05 00:26:14.981 16106-16106/com.sjc.myapplication I/MyIntentService: onStart: 
    2019-03-05 00:26:19.983 16106-17272/com.sjc.myapplication I/MyIntentService: onHandleIntent: 睡了52019-03-05 00:26:19.985 16106-16106/com.sjc.myapplication I/MyIntentService: onDestroy: 
    
    複製程式碼
  • 原始碼分析: IntentService原始碼並不長。

//本身是一個抽象類,繼承與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 方法後,呼叫了stopSelf方法,銷燬服務。
            onHandleIntent((Intent)msg.obj);
            //銷燬服務
            stopSelf(msg.arg1);
        }
    }

   
    public IntentService(String name) {
        super();
        mName = name;
    }

   
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
      
        super.onCreate();
        //在onCreate 中建立了一個 HandlerThread 執行緒
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        //開啟執行緒
        thread.start();

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

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
    //onStrat 中傳送訊息,然後回撥ServiceHandler 的 handleMessage 方法
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

 
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
    //退出Looper
        mServiceLooper.quit();
    }

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

    //抽象方法,在子類中重寫。
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

複製程式碼
  • 總結: IntentService 只能通過 startService 方式開始有用,才會回撥 onHandleIntent 方法。且執行完子執行緒中的任務後,自動銷燬。如果使用 bindService 方式開啟,和普通 Service 沒有差別。

參考文章:

相關文章