四大组件之Service|Android开发系列

303 阅读5分钟

这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战

概述

  Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件,与Activity不同,Activity是显示图形用户界面的,而Service的运行是不可见的。服务可由其他应用组件启动,即使切换到其他的应用,Service仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。

Sercvice类型

前台Service

  前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必须显示Notification。即使用户停止与应用的交互,前台服务仍会继续运行。

后台Service

  后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务。

注意:如果您的应用面向 API 级别 26 或更高版本,当应用本身未在前台运行时,系统会对运行后台服务施加限制。在诸如此类的大多数情况下,您的应用应改为使用计划作业

绑定Service

   绑定Service方式核心在于bindService()绑定服务会提供客户端-服务器接口,以便组件与服务进行交互、发送请求、接收结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

Service生命周期

image.png

public class MyService extends Service {

    /**
     * 首次创建服务时,系统会(在调用 onStartCommand() 或 onBind() 之前)调用此方法来执行一次性设置程序。如果服务已在运行,则不会调用此方法。
     */
    @Override
    public void onCreate() {
        super.onCreate();
    }

    /**
     * 当另一个组件(如 Activity)请求启动服务时,系统会通过调用 startService() 来调用此方法。执行此方法时,服务即会启动并可在后台无限期运行。
     * 如果您实现此方法,则在服务工作完成后,您需负责通过调用 stopSelf() 或 stopService() 来停止服务。(如果您只想提供绑定,则无需实现此方法。)
     * @param intent
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 当另一个组件想要与服务绑定(例如执行 RPC)时,系统会通过调用 bindService() 来调用此方法。在此方法的实现中,您必须通过返回 IBinder 提供一个接口,
     * 以供客户端用来与服务进行通信。请务必实现此方法;但是,如果您并不希望允许绑定,则应返回 null。
     * @param intent
     * @return
     */
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * 取消绑定时调用
     * @param intent
     * @return
     */
    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    /**
     * 当不再使用服务且准备将其销毁时,系统会调用此方法。服务应通过实现此方法来清理任何资源,如线程、注册的侦听器、接收器等。这是服务接收的最后一个调用。
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

创建启动和绑定Service

   常见的需要定义一个Service的子类,重写其生命周期方法,当创建一个Service后,必须将这个Service在AndroidManifest.xml中进行注册。

<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true" />
private ServiceConnection mConnection;
private Intent mServiceIntent;
@Override
public void onClick(View v) {
    switch (v.getId()){
        case R.id.start_service:
            //启动Service
            if(mServiceIntent == null){
                mServiceIntent = new Intent(this,MyService.class);
            }
            startService(mServiceIntent);
            break;
        case R.id.bind_service:
            //绑定Service
            if(mServiceIntent == null){
                mServiceIntent = new Intent(this,MyService.class);
            }
            if(mConnection == null){
                mConnection = new ServiceConnection() {
                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
                    }

                    @Override
                    public void onServiceDisconnected(ComponentName name) {
                    }
                };
            }
            bindService(mServiceIntent, mConnection,BIND_AUTO_CREATE);
            break;
        case R.id.unbind_service:
            //解绑Service
            if(null != mConnection){
                unbindService(mConnection);
                mConnection = null;
            }
            break;
        case R.id.stop_service:
            //停止Service
            if(mServiceIntent == null){
                mServiceIntent = new Intent(this,MyService.class);
            }
            stopService(mServiceIntent);
            break;
    }
}
  • 如果服务存在,binderService方法只能是onBind方法被调用,而unBindService方法只能是onUnbind被调用
  • 如果服务不存在:onCreate->onBind->onUnbind->onDestory

AIDL

Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。

image.png 定义AIDL文件,父级目录与java同级。

interface IMyAIDL {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    int getProcess();
}

定义完之后make project ,AS快捷键Ctrl+ F9 ,之后才可以使用。

make之后

//继承字Binder实现AIDL
public static abstract class Stub extends android.os.Binder implements com.Shixf.androidbasic.IMyAIDL

image.png

public class MyService extends Service {

    private static final String TAG = "MyService";
    MyBinder mBinder;
    private volatile int i;

    public MyService() {
        mBinder = new MyBinder();
    }

    class MyBinder extends Binder{
        public MyService getService(){
            return MyService.this;
        }
        public int getProcess(){
            return i;
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate");
        new Thread(()->{
            while (true){
                i++;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    @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");
        return new IMyAIDL.Stub() {
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

            }

            @Override
            public int getProcess() throws RemoteException {
                Log.d("Shixf","AIDL processs = " + i);
                return i;
            }
        };
    }

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

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

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

新创建一个应用以及AIDL,保持包名一致

image.png

package com.shixf.aidldemo;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.Shixf.androidbasic.IMyAIDL;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private static final String TAG = "MainActivity";
    private Button mStartService;
    private Button mBindService;
    private Button mStopService;
    private Button mUnBindService;

    ServiceConnection connection;
    private Intent i;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate: startService = " + mStartService);
        mStartService = findViewById(R.id.btn_startService);
        mStartService.setOnClickListener(this);
        mBindService = findViewById(R.id.btn_bindService);
        mBindService.setOnClickListener(this);
        mStopService = findViewById(R.id.btn_stopService);
        mStopService.setOnClickListener(this);
        mUnBindService = findViewById(R.id.btn_unbindService);
        mUnBindService.setOnClickListener(this);
    }

    @SuppressLint("NonConstantResourceId")
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_startService:
                Log.d(TAG, "onClick: start Service");
                i = new Intent();
                /*i.setAction("com.shixf.MyService");
                i.setPackage("com.Shixf.androidbasic");*/
                i.setComponent(new ComponentName("com.Shixf.androidbasic",
                                "com.Shixf.androidbasic.Service.MyService"));//第一个参数是包名,第二个是类名
                startForegroundService(i);
                break;
            case R.id.btn_stopService:
                if(i == null){
                    i = new Intent();
                    /*i.setAction("com.shixf.MyService");
                    i.setPackage("com.Shixf.androidbasic");*/
                    i.setComponent(new ComponentName("com.Shixf.androidbasic",
                            "com.Shixf.androidbasic.Service.MyService"));//第一个参数是包名,第二个是类名
                }
                stopService(i);
                break;
            case R.id.btn_bindService:
                if(i == null){
                    i = new Intent();
                    /*i.setAction("com.shixf.MyService");
                    i.setPackage("com.Shixf.androidbasic");*/
                    i.setComponent(new ComponentName("com.Shixf.androidbasic",
                            "com.Shixf.androidbasic.Service.MyService"));//第一个参数是包名,第二个是类名
                }
                if(connection == null){
                    connection = new ServiceConnection() {
                        @Override
                        public void onServiceConnected(ComponentName name, IBinder service) {
                            aidlGetProcess(service);
                        }

                        @Override
                        public void onServiceDisconnected(ComponentName name) {

                        }
                    };
                }
                bindService(i,connection,BIND_AUTO_CREATE);
                break;
            case R.id.btn_unbindService:
                if(null != connection){
                    unbindService(connection);
                    connection = null;
                }
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + v.getId());
        }
    }

    private void aidlGetProcess(IBinder iBinder){
        IMyAIDL aidl = IMyAIDL.Stub.asInterface(iBinder);
        try {
            int process = aidl.getProcess();
            Toast.makeText(MainActivity.this,"process = " + process,Toast.LENGTH_LONG).show();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

image.png 注意如果exported = false 是无法启动Service的。