Android Service

20lxj666 / 2024-09-18 / 原文

Android Service

参考:https://blog.csdn.net/javazejian/article/details/52709857

1、Service简单概述

Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。服务可由其他应用组件启动(如Activity),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行,Service基本上分为两种形式:

  • 启动状态

当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,除非手动调用才能停止服务, 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。

  • 绑定状态

当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

Service在清单文件中的声明

前面说过Service分为启动状态和绑定状态两种,但无论哪种具体的Service启动类型,都是通过继承Service基类自定义而来,也都需要在AndroidManifest.xml中声明,那么在分析这两种状态之前,我们先来了解一下Service在AndroidManifest.xml中的声明语法,其格式如下:

<service android:enabled=["true" | "false"]
    android:exported=["true" | "false"]
    android:icon="drawable resource"
    android:isolatedProcess=["true" | "false"]
    android:label="string resource"
    android:name="string"
    android:permission="string"
    android:process="string" >
    . . .
</service>12345678910
  • android:exported:代表是否能被其他应用隐式调用,其默认值是由service中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。为false的情况下,即使有intent-filter匹配,也无法打开,即无法被其他应用隐式调用。
  • android:name:对应Service类名
  • android:permission:是权限声明
  • android:process:是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。
  • android:isolatedProcess :设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start)。
  • android:enabled:是否可以被系统实例化,默认为 true因为父标签 也有 enable 属性,所以必须两个都为默认值 true 的情况下服务才会被激活,否则不会激活。

2、示例

1. 最简单的Service

让我们从最基本的Service开始:

1.1 MyService.java

public class MyService extends Service {
    private static final String TAG = "MyService";
    private Handler handler;
    private Runnable runnable;
    private int count = 0;

    @Override
    public void onCreate() {
        super.onCreate();
        // 初始化Handler和Runnable,用于每秒递增count并打印日志
        handler = new Handler(Looper.getMainLooper());
        runnable = new Runnable() {
            @Override
            public void run() {
                count++;
                Log.d(TAG, "Count: " + count);
                handler.postDelayed(this, 1000);
            }
        };
        handler.post(runnable); // 启动任务
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY; // 服务在被系统杀掉后会重启
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        handler.removeCallbacks(runnable); // 停止任务
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null; // 这个简单的服务不使用绑定
    }
}

1.2 注册服务

<service android:name=".service.MyService"/>

1.3 activity

public class MainActivity extends AppCompatActivity {

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

        Intent intent = new Intent(this, MyService.class);
        startService(intent);
    }
}

1.4 解释

  • onCreate():初始化服务,启动一个每秒递增count的任务,并打印日志。
  • onStartCommand():定义服务的启动行为,这里返回START_STICKY,表示如果服务被系统杀掉,系统会尝试重新启动它。
  • onDestroy():停止任务,清理资源。
  • onBind():返回null,表示这是一个简单的、不可绑定的服务。

2. 绑定服务(Bound Service)

绑定服务允许客户端(如Activity)绑定到服务,并与之交互。绑定服务通常用于与应用组件进行长时间的交互。

2.1 MyBoundService.java

public class MyBoundService extends Service {
    private final IBinder binder = new LocalBinder();
    private int count = 0;
    private String logData = "Initial Data";

    public class LocalBinder extends Binder {
        public MyBoundService getService() {
            return MyBoundService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder; // 返回IBinder实例
    }

    public String getServiceData() {
        return logData + count;
    }

    public void setServiceData(String data) {
        this.logData = data;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        // 计数任务
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
            count++;
            Log.d("MyBoundService", "Count: " + count);
        }, 1000);
    }
}

2.2 注册服务

<service android:name=".service.MyBoundService" />

2.3 activity

public class MainActivity extends AppCompatActivity {

    private MyBoundService myBoundService;
    private boolean isBound = false;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            MyBoundService.LocalBinder binder = (MyBoundService.LocalBinder) service;
            myBoundService = binder.getService();
            isBound = true;
            Log.d("MainActivity", "Service Connected: " + myBoundService.getServiceData());
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            isBound = false;
        }
    };

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

        Intent intent = new Intent(this, MyBoundService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);

        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                myBoundService.setServiceData("Service finish");
                Log.d("MainActivity", "Service getLogData: " + myBoundService.getServiceData());
                // 执行延时后的UI更新任务
                System.exit(0);
            }
        }, 5000); // 延时5秒


    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (isBound) {
            unbindService(connection);
            isBound = false;
        }
    }
}

2.4 解释

  • LocalBinder:定义一个内部类,用于客户端绑定服务时获取服务实例。
  • getServiceData():提供获取服务数据的接口。
  • setServiceData():提供设置服务数据的接口。

3.使用 Messenger 进行进程间通信(IPC)

Messenger需要结合Handler一起使用。需要一个handler,一个Messenger对象。handler最终会负责发送和接收消息。它不仅能够在后台执行任务,还能通过 Messenger 发送和接收消息,从而与客户端进行双向交互。

3.1 MyComplexService

public class MyComplexService extends Service {
    private static final String TAG = "MyComplexService";
    private final Messenger serviceMessenger = new Messenger(new IncomingHandler());
    private Messenger clientMessenger;
    private boolean isRunning = false;
    int count = 0;

    @Override
    public IBinder onBind(Intent intent) {
        return serviceMessenger.getBinder(); // 返回Messenger的Binder
    }

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

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "Service destroyed");
        isRunning = false; // 停止任务
    }

    private void startBackgroundTask() {
        if (isRunning) return;
        isRunning = true;

        // 后台线程开始计数任务
        new Thread(() -> {
            while (isRunning) {
                try {
                    Thread.sleep(1000);
                    count++;
                    if (clientMessenger != null) {
                        Message msg = Message.obtain(null, 0);
                        Bundle bundle = new Bundle();
                        bundle.putInt("count", count);
                        msg.setData(bundle);
                        try {
                            clientMessenger.send(msg); // 发送消息给客户端
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    // 处理客户端发送的消息
    private class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1: // 客户端请求启动任务
                    startBackgroundTask();
                    break;
                case 2: // 客户端请求停止任务
                    isRunning = false;
                    stopSelf();
                    break;
                case 3: // 客户端传递自己的Messenger
                    clientMessenger = new Messenger(msg.replyTo.getBinder());
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
}

3.2 注册服务

<service android:name=".service.MyComplexService" />

3.3 activity

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private Messenger serviceMessenger;
    private boolean isBound = false;
    private TextView textView;

    private final Messenger clientMessenger = new Messenger(new IncomingHandler());

    private final ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            serviceMessenger = new Messenger(service);
            isBound = true;

            Message msg = Message.obtain(null, 3);
            msg.replyTo = clientMessenger;
            try {
                serviceMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            serviceMessenger = null;
            isBound = false;
        }
    };

    private class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            int count = msg.getData().getInt("count");
            textView.setText("Count: " + count);
        }
    }

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

        Intent intent = new Intent(this, MyComplexService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);

        findViewById(R.id.startButton).setOnClickListener(v -> startServiceTask());
        findViewById(R.id.stopButton).setOnClickListener(v -> stopServiceTask());
    }

    private void startServiceTask() {
        if (isBound) {
            Message msg = Message.obtain(null, 1);
            try {
                serviceMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    private void stopServiceTask() {
        if (isBound) {
            Message msg = Message.obtain(null, 2);
            try {
                serviceMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (isBound) {
            unbindService(connection);
            isBound = false;
        }
    }
}

3.4 解释

  • Messenger:用于在进程间传递消息。
  • IncomingHandler:处理来自服务的消息。

4. 使用AIDL进行跨进程通信(复杂)

允许多个客户端通过消息通信与服务进行复杂交互。在客户端(如Activity)中,通过ServiceConnection来绑定服务,并使用AIDL接口与服务通信。

4.1 MyBinderService.java

public class MyBinderService extends Service {
    private boolean isRunning = false;
    private int count = 0;

    private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
        @Override
        public void startTask() throws RemoteException {
            startBackgroundTask();
        }

        @Override
        public void stopTask() throws RemoteException {
            stopBackgroundTask();
            stopSelf();
        }

        @Override
        public int getCount() throws RemoteException {
            return count;
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder; // 返回AIDL接口的Binder实例
    }

    private void startBackgroundTask() {
        if (isRunning) return;
        isRunning = true;

        new Thread(() -> {
            while (isRunning) {
                try {
                    Thread.sleep(1000);
                    count++;
                    Log.d("MyBinderService", "Count: " + count);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private void stopBackgroundTask() {
        isRunning = false;
    }
}

4.2 注册服务

 <service
            android:name=".service.MyBinderService"
            android:exported="true"
            android:permission="android.permission.BIND_AUTO_CREATE" />

4.3 activity

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private IMyAidlInterface mService;
    private boolean isBound = false;
    private TextView textView;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = IMyAidlInterface.Stub.asInterface(service);
            isBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mService = null;
            isBound = false;
        }
    };

    private Handler handler;
    private Runnable runnable;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);

        Intent intent = new Intent(this, MyBinderService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

        Button startButton = findViewById(R.id.startButton);
        Button stopButton = findViewById(R.id.stopButton);

        startButton.setOnClickListener(v -> startServiceTask());
        stopButton.setOnClickListener(v -> stopServiceTask());

         handler = new Handler(Looper.getMainLooper());
         runnable = new Runnable() {
            @Override
            public void run() {
                int count = 0;
                try {
                    count = mService.getCount();
                    Log.d(TAG, "run: count-->"+count);
                } catch (Exception e) {
                   e.printStackTrace();
                }
                textView.setText("Count: " + count);
                handler.postDelayed(this, 1000);
            }
        };

    }

    private void startServiceTask() {
        if (isBound) {
            Log.d(TAG, "Start button clicked");
            try {
                mService.startTask();
                handler.removeCallbacks(runnable);
                handler.post(runnable);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    private void stopServiceTask() {
        if (isBound) {
            Log.d(TAG, "Stop button clicked");
            try {
                mService.stopTask();
                handler.removeCallbacks(runnable);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (isBound) {
            unbindService(mConnection);
            isBound = false;
        }
    }
}

4.4 解释

  • AIDL接口:IMyAidlInterface.Stub是AIDL接口的具体实现。它定义了可以由客户端调用的方法,如startTask()stopTask()getCount()

  • 服务在被绑定后,客户端可以通过这个接口与服务通信,执行跨进程任务。

  • ServiceConnection:用于管理客户端与服务的连接状态。

  • startServiceTask()stopServiceTask():通过AIDL接口启动和停止服务任务。

总结

  1. 简单的服务:适用于后台执行不需要与客户端交互的任务。
  2. 绑定服务:适用于需要与客户端交互的任务,客户端可以通过绑定服务获取数据或执行操作。
  3. message:适合于需要在服务和客户端之间传递消息的场景。
  4. AIDL服务:适用于跨进程通信场景,允许客户端与服务进行复杂的数据交互和任务管理。