天天看點

安卓基礎學習之服務元件及應用服務元件及應用

目錄

  • 服務元件及應用
    • 1. 服務概念
    • 2. 服務類型
      • 2.1 本地服務
      • 2.2 遠端服務
    • 3. service啟動方式與生命周期
    • 4. 兩種方法的簡單執行個體
      • 4.1 非綁定式的服務
        • 4.1.1 效果
        • 4.1.2 代碼
      • 4.2 綁定式的服務
        • 4.2.1 效果
        • 4.2.2 代碼

服務元件及應用

1. 服務概念

Service也是Android系統四大元件1之一,它是一種長生命周期、沒有可視化界面、運作于背景的一種服務程式。

比如:播放音樂的時候,你有可能想邊聽音樂邊幹些其他事情,當退出播放音樂的應用,如果不用Service,就聽不到歌了——這就是Service的用武之地了。

Android背景運作的很多Service是在系統啟動時被開啟的,以支援系統的正常工作。

比如:MountService監聽是否有SD卡安裝及移除,PackageManagerService提供軟體包的安裝、移除及檢視等。

激活和停止Service通常是由其他元件完成的。例如,元件Activity的超類Context提供了激活和停止Service的方法。

安卓基礎學習之服務元件及應用服務元件及應用

如同Activity元件一樣,Service元件必須在清單檔案裡使用相應的标簽。使用菜單File→New→Service建立的服務元件,将自動在清單檔案裡注冊。

2. 服務類型

服務類型可劃分為本地服務和遠端服務,也可劃分為綁定式服務與非綁定服務。

2.1 本地服務

本地服務用于應用程式内部(同一個apk内被調用),使用startService()啟動服務,使用stopService()停止服務。

在服務内部可以調用Service.stopSelf() 或 Service.stopSelfResult()來自己停止。無論調用了多少次startService(),都隻需調用一次stopService()來停止。

2.2 遠端服務

遠端服務用于應用程式之間(被另一個apk調用)。

可以定義接口并把接口暴露出來,以便其他應用進行操作,比如一個天氣預報服務。用戶端建立到服務對象的連接配接,并通過那個連接配接來調用服務。

通過調用bindService()方法建立連接配接并啟動服務,調用 unbindService()關閉連接配接。多個用戶端可以綁定至同一個服務。如果服務此時還沒有加載,bindService()會先加載它。

遠端服務提供給可被其他應用複用。

3. service啟動方式與生命周期

安卓基礎學習之服務元件及應用服務元件及應用
  • 非綁定式服務:

    使用

    startService()

    啟動service,調用

    onCreate()

    onStartCommand()

    2兩個方法。該service隻能建立一次,也就是說

    onCreate()

    隻會執行一次。(但每次調用

    startService()

    時,

    onStartCommand()

    方法都會被調用。)

    使用

    stopService()

    來結束服務,調用

    onDestroy()

    方法即可終止服務。
  • 綁定式服務:

    第一次執行

    bindService()

    時,會調用

    onCreate()

    onBind()

    方法建立服務并将service與activity綁定。(但是多次執行

    bindService()

    時,并不會多次建立服務和綁定服務,也就不會再次調用這兩個方法。)

    當調用

    unbindService()

    方法或者調用者Context不存在時,比如綁定的activity調用

    finish()

    方法,就會調用

    onUnbind()

    onDestroy()

    來結束服務。
非綁定式服務和綁定式服務的差別
1、startService啟動的服務預設無限期執行,bindService啟動的服務的生命周期與其綁定者相關。
2、若使用startService啟動服務,則建立後将無法再與其傳遞消息;若使用bindService啟動服務,則可以通過IBinder接口實作靈活的互動。

4. 兩種方法的簡單執行個體

4.1 非綁定式的服務

4.1.1 效果

界面如下圖所示:

安卓基礎學習之服務元件及應用服務元件及應用

logcat中輸出:

2020-04-17 16:10:52.756 24674-24674/com.example.myapplication_service V/MyService_unbind: service is created!
2020-04-17 16:10:52.756 24674-24674/com.example.myapplication_service V/MyService_unbind: service is started!
2020-04-17 16:11:01.823 24674-24674/com.example.myapplication_service V/MyService_unbind: service is destroyed!
           

4.1.2 代碼

首先在項目中new一個Service(注意,Service中又兩個選項,new的是Service而不是Service(IntentService))

建立初始會自帶

onBind()

方法,由于此處寫的是非綁定式的方法,直接在裡面

return null;

即可。

MyService_unbind.java

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MyService_unbind extends Service {
    public MyService_unbind() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return null;
    }

	//建立服務
    @Override
    public void onCreate() {
        super.onCreate();
        Log.v("MyService_unbind","service is created!");
    }

	//開始服務
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.v("MyService_unbind","service is started!");
        return START_STICKY;
    }

	//結束服務
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.v("MyService_unbind","service is destroyed!");
    }
}
           

由于服務是不與使用者互動的,此處為了使代碼更簡單且示範效果更明顯,就在每個方法中用logcat列印出一句話表明服務的生命周期,并在布局檔案中寫兩個用于啟動服務和終止服務的button。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.403" />

    <Button
        android:id="@+id/btn_ubs"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="啟動非綁定式服務"
        app:layout_constraintBottom_toTopOf="@+id/btn_ubd"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintVertical_bias="0.676" />

    <Button
        android:id="@+id/btn_ubd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="204dp"
        android:text="關閉非綁定式服務"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
           

在MainActivity中寫兩個button的觸發事件,在啟動服務按鈕中使用

startService()

啟動service,在終止按鈕中使用

stopService()

來結束服務。

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    Button btn_ubs;
    Button btn_ubd;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //擷取程式界面的兩個按鈕
        btn_ubs = (Button)findViewById(R.id.btn_ubs);
        btn_ubd = (Button)findViewById(R.id.btn_ubd);

        btn_ubs.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this,MyService_unbind.class);
                startService(intent);
            }
        });

        btn_ubd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this,MyService_unbind.class);
                stopService(intent);
            }
        });

    }
}
           

需要注意的問題:

1、若沒有直接new一個service,則要手動在

AndroidManifest.xml

檔案裡面注冊自己寫的service。若是new的service,則會在清單檔案中自動注冊,如下所示。

<service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"></service>
           

2、布局檔案中的button的text最好寫在strings.xml中,可以根據布局檔案中的警告進一步修改。

4.2 綁定式的服務

4.2.1 效果

界面如下圖所示:

每點一次按鈕,textview就會重新整理成自第一次點選按鈕後到這次點選按鈕所經曆的秒數。

安卓基礎學習之服務元件及應用服務元件及應用

4.2.2 代碼

new一個service,命名為BinderService來寫綁定式服務。

綁定式服務需要重寫

onBind()

方法,傳回一個IBinder的對象。IBinder是一個接口,一般我們會繼承實作了IBinder類的Binder類,在其中實作自己的方法。

BinderService.java

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class BinderService extends Service {
    public BinderService() {
    }

    private int count = 0;

    class Mybinder extends Binder{
        public int getcount(){return count;}
    }

    Mybinder binder = new Mybinder();

    @Override
    public void onCreate() {
        super.onCreate();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    try{
                        //線程啟動後每過1s,count+1
                        Thread.sleep(1000);
                        count++;
                    }catch (Exception e){e.printStackTrace();}
                }
            }
        }).start();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return binder;
    }
}
           

在布局檔案中定義一個button來啟動綁定式服務,textview顯示目前秒數。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".MainActivity">
    
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.179" />

    <Button
        android:id="@+id/btn_bs"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/btn_bs_text"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

</androidx.constraintlayout.widget.ConstraintLayout>
           

在MainActivity中寫button的觸發事件,在啟動服務按鈕中用

bindService()

來綁定服務。

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    Button btn_bs;
//    建立綁定服務下的binder的執行個體
    BinderService.Mybinder mybinder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btn_bs = (Button)findViewById(R.id.btn_bs);

        final TextView textView = (TextView)findViewById(R.id.textView);

//        給出activity要調的服務BinderService,是bindService的第一個參數
        Intent binderintent = new Intent(MainActivity.this,BinderService.class);

//        bindService的第二個參數
        ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
//                這裡的service就是onBind()方法傳回的binder對象
                mybinder = (BinderService.Mybinder)service;
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
//                服務連接配接結束時,設定其為null
                mybinder = null;
            }
        };

//        綁定服務,有三個參數:intent、ServiceConnection和int
//        其中的第三個參數是指明綁定效果
        bindService(binderintent,connection, Context.BIND_AUTO_CREATE);

        btn_bs.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                textView.setText(mybinder.getcount()+"");
            }
        });
    }
}
           
  1. Android四大元件分别為:Activity/Service/BroadCast Recevicer/Content provider ↩︎
  2. Service的onStart方法在API 5時被廢棄,替代它的是onStartCommand方法。 ↩︎