四大組件——Service基礎

Service是什麼

定義

服務可由其餘應用組件啓動,並且即便用戶切換到其餘應用,服務仍將在後臺繼續運行。此外,組件可經過綁定到服務與之進行交互,甚至是執行進程間通訊 (IPC)android

  • 後臺運行,不可見,沒有界面
  • 優先級高於Activity

用途:

  • 服務可在後臺處理網絡事務、播放音樂,執行文件 I/O 或與內容提供程序進行交互等...

注意

  • Service運行在主線程中,不能直接作耗時操做
  • 能夠在服務中開啓一個線程,在線程中作耗時操做

AndroidManifest.xml中註冊Service

<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true" />
複製代碼
  • enable爲true表示容許Service運行
  • exported爲true表示容許其餘應用隱式調用該服務
public class MyService extends Service {
    private static final String TAG = "MyService";

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

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

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "--------onBind: ");
        // TODO: Return the communication channel to the service.
        return new InnerBinder();
    }

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

    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
        Log.d(TAG, "--------onRebind: ");
    }

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

}
複製代碼

Service類型

本地服務(Local Service)

  • 應用程序內部

遠程服務(Remote Service)

  • Android系統內部的應用程序之間
  • 定義IBinder接口
啓動服務方式

1.startServicebash

  • 關閉方法:stopService、stopSelf

2.bindService網絡

  • 關閉方法:unbindService
兩種啓動方式的區別
  • startService方式啓動的服務能夠長期在後臺運行,而bindService方式啓動的服務沒法長時間在後臺運行
  • startService方式啓動的服務,不能夠和服務進行通信,而bindService方式啓動的服務,能夠和服務進行通信(經過實現IBinder接口)

service_actiivity.png

以startService方式啓動的Service

service.png

實例代碼:

佈局文件:app

<string name="button">啓動服務</string>
<string name="button2">中止服務</string>
<string name="button3">調用服務內部方法</string>
<string name="button4">綁定服務</string>
<string name="button5">解綁服務</string>
複製代碼
<?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">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="64dp"
        android:onClick="startServiceClick"
        android:text="@string/button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:onClick="stopServiceClick"
        android:text="@string/button2"
        app:layout_constraintStart_toStartOf="@+id/button"
        app:layout_constraintTop_toBottomOf="@+id/button" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="188dp"
        android:onClick="callService"
        android:text="@string/button3"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button2" />

    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:onClick="bindService"
        android:text="@string/button4"
        app:layout_constraintEnd_toStartOf="@+id/guideline"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@+id/guideline"
        app:layout_constraintTop_toBottomOf="@+id/button2" />

    <Button
        android:id="@+id/button5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="unBindService"
        android:text="@string/button5"
        app:layout_constraintBottom_toTopOf="@+id/button3"
        app:layout_constraintEnd_toStartOf="@+id/guideline"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@+id/guideline"
        app:layout_constraintTop_toBottomOf="@+id/button4"
        app:layout_constraintVertical_bias="0.298" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
複製代碼

接口定義:ide

public interface ICommunication {
    void callServiceInnerMethod();
}
複製代碼

Service實現:佈局

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

    //自定義一個Binder內部類實現ICommunication接口實現服務的通信
    private class InnerBinder extends Binder implements ICommunication{

        @Override
        public void callServiceInnerMethod() {
            sayHello();
        }
    }

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

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

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "--------onBind: ");
        // TODO: Return the communication channel to the service.
        return new InnerBinder();
    }

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

    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
        Log.d(TAG, "--------onRebind: ");
    }

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

    private void sayHello(){
        Toast.makeText(this,"Hello World!",Toast.LENGTH_SHORT).show();
    }
}
複製代碼

Activity實現:ui

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private boolean mIsServiceBind;
    private ICommunication mICommunication;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "--------onCreate: ");
    }

    /**
     * 開啓服務
     */
    public void startServiceClick(View view) {
        Intent intent = new Intent();
        intent.setClass(this, MyService.class);
        startService(intent);
    }

    /**
     * 中止服務
     */
    public void stopServiceClick(View view) {
        Intent intent = new Intent();
        intent.setClass(this, MyService.class);
        stopService(intent);
    }

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

    public void callService(View view) {
        mICommunication.callServiceInnerMethod();
    }

    /**
     * 綁定服務
     */
    public void bindService(View view) {
        Intent intent = new Intent();
        intent.setClass(this, MyService.class);
        mIsServiceBind = bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "--------onServiceConnected: ");
            mICommunication = (ICommunication) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "--------onServiceDisconnected: ");
            mICommunication = null;
        }
    };

    /**
     * 解綁服務
     */
    public void unBindService(View view) {
        if (mConnection != null && mIsServiceBind) {
            unbindService(mConnection);
        }
    }
}
複製代碼
  • ServiceConnection的onServiceConnected()在綁定Service時調用
  • onServiceDisconnected()只有在異常解綁中止服務時調用,正常解綁不會調用
  • BIND_AUTO_CREATE參數表示綁定Service時建立Service,解綁Service時中止Service

生命週期

回調方法

  • onCreate() 最初建立服務時(在調用onStartCommand()或onBind()以前),系統將調用此方法以執行一次性設置過程。若是服務已經在運行,則不會調用此方法。
  • onStartCommand() 當另外一個組件(例如活動)請求啓動服務時,系統經過調用startService()來調用此方法。執行此方法時,服務將啓動並能夠無限期在後臺運行。若是執行此操做,則有責任經過調用stopSelf()或stopService()在服務完成後中止該服務。若是隻想提供綁定,則不須要實現此方法。
  • onBind() 當另外一個組件想要與服務綁定時,系統經過調用bindService()來調用此方法。在此方法的實現中,必須提供一個接口,客戶端能夠經過返回IBinder使用該接口與服務進行通訊。您必須始終實現此方法。可是,若是不想容許綁定,則應返回null。
  • onDestroy() 當再也不使用該服務並將其銷燬時,系統將調用此方法。您的服務應實現此目的,以清理全部資源,例如線程,註冊的偵聽器或接收器。這是服務收到的最後一個回調。

回調調用

  • startService ——> stopService的生命週期
onCreate ——> onStartCommand ——> onDestory
複製代碼
  • bindService ——> unBindService的生命週期
onCreate ——> onBind ——> onUnbind ——> onDestory
複製代碼

service_lifecycle.png

前臺服務ForegroundService

前臺服務是用戶主動意識到的服務,不是內存不足時系統要殺死的候選對象。前臺服務必須爲狀態欄提供一個通知,該通知位於「正在進行」標題下。這意味着除非服務中止或從前臺刪除,不然沒法取消該通知。this

注意:使用時須要添加權限spa

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
複製代碼
public class MyForegroundService extends Service {
    public MyForegroundService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
        //後面一個參數爲渠道參數,Android8.0新增要求
        Notification notification = new NotificationCompat.Builder(this, "foreground")
                .setContentTitle("這是標題")
                .setContentText("這是內容")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentIntent(pi)
                .build();
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        NotificationChannel channel = null;
        //Android8.0要求設置通知渠道
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            channel = new NotificationChannel("foreground", "foregroundName", NotificationManager.IMPORTANCE_HIGH);
            if (notificationManager != null) notificationManager.createNotificationChannel(channel);
        }
        //給startForeground()的整數ID不能爲0
        startForeground(1, notification);
    }

    @Override
    public void onDestroy() {
        stopForeground(true);// 中止前臺服務 參數:表示是否移除以前的通知
        super.onDestroy();
    }
}
複製代碼

注意:給startForeground()的整數ID不能爲0線程

Activity中啓動此服務:

/**
     * 啓動前臺服務
     */
    public void startFServiceClick(View view) {
        Intent intent = new Intent();
        intent.setClass(this, MyForegroundService.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(intent);
        }
    }
複製代碼
相關文章
相關標籤/搜索