Android 使用IntentService

童思宇發表於2018-02-08

本篇文章還是在Android 使用前臺服務都知道服務中的程式碼都是預設執行在主執行緒當中的,如果直接在服務裡去處理一些耗時的邏輯,就很容易出現ANR(Application Not Responding)的情況,所以這個時候就需要用到Android多執行緒程式設計的技術了,我們應該在服務的每個具體的方法裡開啟一個子執行緒,然後在這裡去處理那些耗時的邏輯,因此,一個比較標準的服務就可以寫成如下形式:

public class MyService extends Service {

    ...

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //處理具體的邏輯
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }

    ...
}

但是,這種服務一旦啟動之後,就會一直處於執行狀態,必須呼叫stopService()或者stopSelf()方法才能讓服務停止下來,所以,如果想要實現讓一個服務在執行完畢後自動停止的功能,就可以這樣寫:

public class MyService extends Service {

    ...

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //處理具體的邏輯
                stopSelf();
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }

    ...
}


雖說這種寫法並不複雜,但是總會有一些人忘記開啟執行緒,或者忘記呼叫stopSelf()方法,為了可以簡單的建立一個非同步的,會自動停止的服務,Android專門提供了一個IntentService類,這個類就很好的解決了前面所提到的兩種尷尬,下面我們就來看一下它的用法.

新建一個MyIntentService類繼承自IntentService,程式碼如下:

public class MyIntentService extends IntentService {

    private static final String TAG = "MyIntentService";
    
    public MyIntentService() {
        super("MyIntentService");//呼叫父類的有參建構函式
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //列印當先執行緒的id
        Log.d(TAG, "onHandleIntent: " + "Thread id is " + Thread.currentThread().getId());
    }

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

這裡首先要提供一個午餐的建構函式,並且必須在其內部呼叫父類的有參建構函式,然後要在子類中去實現onHandleIntent()這個抽象方法,在這個方法中可以去處理一些具體的邏輯,而且不用擔心ANR的問題,因為這個方法已經是在子執行緒中執行的了,這裡為了證實一下,我們在onHandleIntent()方法中列印了當前執行緒的id,另外根據IntentService的特性,這個服務在執行結束後應該會自動停止的,所以我們又重寫了onDestroy()方法,在這裡也列印了一行日誌,以證實服務是不是停止掉了.

接下來修改activity_main.xml中的程式碼,加入一個用於啟動MyIntentService這個服務的按鈕,如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.servicetest.MainActivity">

    ...
    
    <Button
        android:id="@+id/start_intent_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start IntentService"
        android:textAllCaps="false"
        app:layout_constraintTop_toBottomOf="@id/unbind_service"/>

</android.support.constraint.ConstraintLayout>

然後修改MainActivity中的程式碼,如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private static final String TAG = "MainActivity";
    
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...
        Button startIntentService = findViewById(R.id.start_intent_service);
        startIntentService.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            ...
            case R.id.start_intent_service:
                //列印主執行緒的id
                Log.d(TAG, "onClick: " + "Thread id is " + Thread.currentThread().getId());
                Intent intentService = new Intent(this,MyIntentService.class);
                startService(intentService);
                break;
                default:
                    break;
        }
    }
}

可以看到,我們在Start IntentService按鈕的點選事件裡面去啟動MyIntentService這個服務,並在這裡列印了一下主執行緒的id,稍後用於和IntentService進行比對,你會發現,其實IntentService的用法和普通的服務沒什麼兩樣.

最後不要忘記,服務都需要在AndroidManifest.xml裡進行註冊,如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.servicetest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        ...
        <service android:name=".MyIntentService" />
    </application>

</manifest>

現在重新執行一下程式,如圖:

點選Start IntentService按鈕後,觀察logcat中列印的日誌,如圖:

可以看到,不僅MyIntentService和MainActivity所在的執行緒id不一樣,而且onDestroy()方法也得到了執行,說明MyIntentService在執行完畢後確實自動停止了,集開啟執行緒和自動停止於一身.

相關文章