Android --- Service

news2024/11/19 4:27:30

出自于此,写得很清楚。
关于Android Service真正的完全详解,你需要知道的一切_android service-CSDN博客
出自【zejian的博客】

什么是Service?

Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。

服务可由其他应用组件启动(如Activity),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响。

此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

Service基本上分为两种形式:

  • 启动状态

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

  • 绑定状态

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

如何使用Service?

清单文件声明

使用Service前会在清单文件中声明配置。

<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>
  • 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 的情况下服务才会被激活,否则不会激活。

创建Service子类

首先要创建服务,必须创建 Service 的子类(或使用它的一个现有子类如IntentService)。

在实现中,我们需要重写一些回调方法(其中onBind()方法必须重写),以处理服务生命周期的某些关键过程。

package com.example.androidstudiostudy.service;

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

//创建 Service 的子类(或使用它的一个现有子类如IntentService),重写一些回调方法
public class OneService extends Service {
    public OneService() {
    }

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

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

    // 每次通过startService()方法启动Service时都会被回调。
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("服务","onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

   // 服务销毁时回调
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("服务","销毁服务");
    }
}
  •  onBind()

  当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通信。无论是启动状态还是绑定状态,此方法必须重写,但在启动状态的情况下直接返回 null。

  • onCreate()

  首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或onBind() 之前)。如果服务已在运行,则不会调用此方法,该方法只调用一次

  • onStartCommand()

  当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果自己实现此方法,则需要在服务工作完成后,通过调用 stopSelf() 或 stopService() 来停止服务。(在绑定状态下,无需实现此方法。)

  • onDestroy()

  当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。

启动Service

使用 startService(intent);

停止Serviece

使用 stopService(intent);

 通过Demo测试一下Service启动状态方法的调用顺序,依次点击启动和停止。StudyService 代码如下:

public class StudyService extends AppCompatActivity {

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

    }

    public void serviceAction(View view) {
        int id = view.getId();
        Intent intent = new Intent(this,OneService.class);
        if(id == R.id.bindService){
            // 绑定service

        } else if (id == R.id.stopService) {
            stopService(intent); // 停止服务
        } else {
            startService(intent); // 启动服务
        }
    }
}

 此时的清单文件:

 <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/study"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_security_config"
        android:roundIcon="@mipmap/study"
        android:supportsRtl="true"
        android:theme="@style/Theme.AndroidStudioStudy"
        tools:targetApi="31">
        <activity
            android:name=".service.StudyService"
            android:exported="false" />
        <activity
            android:name=".service.studyService"
            android:exported="false" />

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

日志打印:

绑定Service

绑定服务是Service的另一种变形,当Service处于绑定状态时,其代表着客户端-服务器接口中的服务器。

当其他组件(如 Activity)绑定到服务时,组件(如Activity)可以向Service(也就是服务端)发送请求,或者调用Service(服务端)的方法,此时被绑定的Service(服务端)会接收信息并响应,甚至可以通过绑定服务进行执行进程间通信 。

有时我们可能需要从Activity组件中去调用Service中的方法,此时Activity以绑定的方式挂靠到Service后,我们就可以轻松地方法到Service中的指定方法

与启动服务不同的是绑定服务的生命周期通常只在为其他应用组件(如Activity)服务时处于活动状态,不会无限期在后台运行,也就是说宿主(如Activity)解除绑定后,绑定服务就会被销毁。

那么在提供绑定的服务时,该如何实现呢?

实际上我们必须提供一个 IBinder接口的实现类,该类用以提供客户端用来与服务进行交互的编程接口,该接口可以通过三种方法定义接口:

  • 扩展 Binder 类

如果服务是提供给自有应用专用的,并且Service(服务端)与客户端相同的进程中运行(常见情况),则应通过扩展 Binder 类并从 onBind() 返回它的一个实例来创建接口。

客户端收到 Binder 后,可利用它直接访问 Binder 实现中以及Service 中可用的公共方法。如果我们的服务只是自有应用的后台工作线程,则优先采用这种方法。

不采用该方式创建接口的唯一原因是,服务被其他应用或不同的进程调用。

  1. 在Service子类中创建一个扩展 Binder 的类(OneServiceBinder),在类中声明了一个getService方法,客户端可访问该方法获取 Service子类 对象的实例,只要客户端获取到 OneServiceBinder 对象的实例就可调用服务端的公共方法。
  2. 创建一个实现IBinder 接口的实例对象并提供公共方法给客户端调用
  3. 从 onBind() 回调方法返回此 Binder 实例。
private OneServiceBinder oneServiceBinder = new OneServiceBinder();


// 当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。
// 在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通信。
// 无论是启动状态还是绑定状态,此方法必须重写,但在启动状态的情况下直接返回 null。
@Override
public IBinder onBind(Intent intent) {
   return oneServiceBinder;
 }

/**
 * 创建Binder对象,返回给客户端即Activity使用,提供数据交换的接口
 */
 public class OneServiceBinder extends Binder {
    // 声明一个方法,getService。(提供给客户端调用)
    OneService getService() {
    // 返回当前对象LocalService,这样我们就可在客户端端调用Service的公共方法了
     return OneService.this;
   }
 }

完整service代码

package com.example.androidstudiostudy.service;

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

//创建 Service 的子类(或使用它的一个现有子类如IntentService),重写一些回调方法
public class OneService extends Service {
    private OneServiceBinder oneServiceBinder = new OneServiceBinder();
    private Thread thread;

    // 当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。
    // 在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通信。
    // 无论是启动状态还是绑定状态,此方法必须重写,但在启动状态的情况下直接返回 null。
    @Override
    public IBinder onBind(Intent intent) {
        return oneServiceBinder;
    }

    /**
     * 创建Binder对象,返回给客户端即Activity使用,提供数据交换的接口
     */
    public class OneServiceBinder extends Binder {
        // 声明一个方法,getService。(提供给客户端调用)
        OneService getService() {
            // 返回当前对象LocalService,这样我们就可在客户端端调用Service的公共方法了
            return OneService.this;
        }
    }

    // 首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或onBind() 之前)。
    // 如果服务已在运行,则不会调用此方法,该方法只调用一次
    private int count = 0;
    private boolean quit = false;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("服务", "首次创建服务调用此方法来执行一次性设置程序,该方法只调用一次");
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!quit) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count++;
                }
            }
        });
        thread.start();
    }

    // 当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。
    // 一旦执行此方法,服务即会启动并可在后台无限期运行。 如果自己实现此方法,则需要在服务工作完成后,通过调用 stopSelf() 或 stopService() 来停止服务。
    // (在绑定状态下,无需实现此方法。)
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("服务", "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    // 当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。
    @Override
    public void onDestroy() {
        super.onDestroy();
        this.quit = true;
        Log.d("服务", "销毁服务");
    }

    //--------------------公共方法------------------
    public int getCount() {
        return count;
    }
    //--------------------解除绑定时调用------------------

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d("服务", "解除绑定");
        return super.onUnbind(intent);
    }
}

客户端绑定到服务步骤:

1.ServiceConnection代表与服务的连接,它只有两个方法,实现ServiceConnection,重写这两个回调方法。

  • onServiceConnected()—系统会调用该方法以传递服务的onBind()返回的IBinder;
  • onServiceDisconnected()—Android系统会在服务崩溃或被杀死导致的连接中断时调用(或者随着activity 的生命周期stop)时调用该方法,当客户端取消绑定的时候,不会回调该方法
private ServiceConnection serviceConnection;
    private OneService myService;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_study_service);

        serviceConnection = new ServiceConnection() {
            // 绑定成功时调用
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                Log.d("绑定服务","成功绑定服务");
                OneService.OneServiceBinder oneServiceBinder = (OneService.OneServiceBinder) iBinder;
                myService = oneServiceBinder.getService();
            }
            // Android 系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                Log.d("绑定服务","与服务的连接意外中断");
                myService = null;
            }
        };

    }

2.调用bindService(),传递ServiceConnection

3.当系统调用onServiceConnected()的回调方法时,可以使用接口定义的方法开始调用服务

4.要断开与服务的连接,请调用unBindService()

如果应用在客户端与服务仍然绑定的状态下被销毁了,则销毁会导致客户端取消绑定。

Activity代码         

public class StudyService extends AppCompatActivity {

    private ServiceConnection serviceConnection;
    private OneService myService;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_study_service);

        serviceConnection = new ServiceConnection() {
            // 绑定成功时调用
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                Log.d("绑定服务","成功绑定服务");
                OneService.OneServiceBinder oneServiceBinder = (OneService.OneServiceBinder) iBinder;
                myService = oneServiceBinder.getService();
            }
            // Android 系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                Log.d("绑定服务","与服务的连接意外中断");
                myService = null;
            }
        };

    }

    public void serviceAction(View view) {
        int id = view.getId();
        Intent intent = new Intent(this,OneService.class);
        if(id == R.id.bindService){
            // 绑定service
            bindService(intent,serviceConnection,Service.BIND_AUTO_CREATE);
            if (myService != null) {
                // 通过绑定服务传递的Binder对象,获取Service暴露出来的数据

                Log.d("获取绑定数据", "从服务端获取数据:" + myService.getCount());
            } else {

                Log.d("获取绑定数据", "还没绑定呢,先绑定,无法从服务端获取数据");
            }
        } else if (id == R.id.stopService) {
            stopService(intent);
        } else {
            startService(intent);
        }
    }
}

打印数据

  • 使用 Messenger

前面了解到应用内同一进程的通信可以使用IBinder,而不同进程间的通信,最简单的方式则是使用 Messenger 服务提供通信接口,利用此方式,我们无需使用 AIDL 便可执行进程间通信 (IPC)。Messenger底层也是通过aidl实现,不过封装了一层,AIDL 支持多线程并发。messenger是同步,如果没有多线程并发要求,就可以使用轻量级的Messenger。

以下是使用 Messenger 绑定Service的主要步骤:

主要步骤

1.创建一个服务子类(MessengerService )并在里面实现一个 Handler,由其接收来自客户端的每个调用的回调

// 用于接收从客户端传递过来的数据
    class ServiceReciveHandle extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Log.i(TAG, "thanks,Service had receiver message from client!");
                    break;
                default:
                    super.handleMessage(msg);
            }

        }
    }

2.将该Handler 用于创建 Messenger 对象(对 Handler 的引用)

3.Messenger 会创建一个 IBinder,MessengerService 通过 onBind() 返回这个Messenger对象的底层Binder。

final Messenger messenger = new Messenger(new ServiceReciveHandle());

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "服务绑定");
        return messenger.getBinder();
    }

4.客户端使用 IBinder 将 Messenger(引用MessengerService 的 Handler)实例化,然后使用Messenger将 Message 对象发送给服务。

// 实现与服务端链接的对象
    private final ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            // 通过服务端传递的IBinder对象,创建相应的Messenger
            // 通过该Messenger对象与服务端进行交互
            Log.i(TAG, "服务链接绑定");
            myService = new Messenger(iBinder);
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.i(TAG, "服务链接绑定取消");
            myService = null;
            mBound = false;
        }
    };

5.MessengerService 在其 Handler 中(在 handleMessage() 方法中)接收每个 Message。

完整Service代码:

package com.example.androidstudiostudy.service;

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

import androidx.annotation.NonNull;

public class MessengerService extends Service {
    static final int MSG_SAY_HELLO = 1;
    private static final String TAG = "MessengerService";

    // 用于接收从客户端传递过来的数据
    class ServiceReciveHandle extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Log.i(TAG, "服务器接收到来自客户端的消息");

                    break;
                default:
                    super.handleMessage(msg);
            }

        }
    }

    final Messenger messenger = new Messenger(new ServiceReciveHandle());

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "服务绑定");
        return messenger.getBinder();
    }

    @Override
    public void onCreate() {
        Log.i(TAG, "服务onCreate");
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "服务Destroy");
        super.onDestroy();
    }
}

Activity代码

package com.example.androidstudiostudy.service;


public class MessengerServiceActivity extends AppCompatActivity {
    private static final String TAG = "MessengerService-Activity";

    // 与服务端交互的Messenger
    private Messenger myService = null;
   
    // 是否绑定
    boolean mBound = false;
   
    // 实现与服务端链接的对象
    private final ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            // 通过服务端传递的IBinder对象,创建相应的Messenger
            // 通过该Messenger对象与服务端进行交互
            Log.i(TAG, "服务链接绑定");
            myService = new Messenger(iBinder);
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.i(TAG, "服务链接绑定取消");
            myService = null;
            mBound = false;
        }
    };

    private Button sendMsg, bindService, unbindService, createService, destoryService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger_service);
        sendMsg = findViewById(R.id.sendMessageToService);
        sendMsg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!mBound)
                    return;
                Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
                try {
                    // 发送消息
                    myService.send(msg);
                } catch (RemoteException e) {
                    throw new RuntimeException(e);
                }

            }
        });

        Intent intent = new Intent(MessengerServiceActivity.this, MessengerService.class);

        unbindService = findViewById(R.id.unbindMessengerService);
        unbindService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                unbindService(mConnection);
            }
        });

        bindService = findViewById(R.id.bindMessengerService);
        bindService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.i(TAG, "bd");
                bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
            }
        });

        createService = findViewById(R.id.startMessengerService);
        createService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startService(intent);
            }
        });

        destoryService = findViewById(R.id.destoreyMessengerService);
        destoryService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                stopService(intent);
            }
        });
    }
}
 服务器与客户端的双向通信

上述代码能够实现客户端向服务器的通信,如果想要服务器向客户端通信,则需要在客户端也创建一个接收消息的Messenger和Handler,改造 MessengerService 中的handler,在接受到信息时发送信息

MessengerService:

在服务器端的handler中发送返回消息

// 用于接收从客户端传递过来的数据
    private static class ServiceReciveHandle extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            if (msg.what == MSG_SAY_HELLO) {
                Log.i(TAG, "服务器接收到来自客户端的消息");
                Messenger replyMessenger = msg.replyTo;
                Message replyMessenge = Message.obtain(null, MessengerService.MSG_SAY_HELLO);
                Bundle bundle=new Bundle();
                bundle.putString("reply","ok~,I had receiver message from you! ");
                replyMessenge.setData(bundle);
                try {
                    replyMessenger.send(replyMessenge);
                } catch (RemoteException e) {
                    throw new RuntimeException(e);
                }
            } else {
                super.handleMessage(msg);
            }

        }
    }

Activity:

1.创建一个用于接收服务器端消息的Messenger和Handler

2.在发送消息时,把接收服务器端的回复的Messenger通过Message的replyTo参数传递给服务端

 private final Messenger activityRecevierMessenger = new Messenger(new activityRecevierHandler());

    private static class activityRecevierHandler extends Handler{
        @Override
        public void handleMessage(@NonNull Message msg) {
            if (msg.what == MessengerService.MSG_SAY_HELLO) {
                Log.i(TAG, "客户端接收到来自服务的消息" + msg.getData().getString("reply"));
            } else {
                super.handleMessage(msg);
            }

        }
    }

sendMsg = findViewById(R.id.sendMessageToService);
        sendMsg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!mBound)
                    return;
                Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
                // 把接收服务器端的回复的Messenger通过Message的replyTo参数传递给服务端
                msg.replyTo =activityRecevierMessenger;
                try {
                    // 发送消息
                    myService.send(msg);
                } catch (RemoteException e) {
                    throw new RuntimeException(e);
                }

            }
        });

绑定服务的注意点
  

1.多个客户端可同时连接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder。系统随后无需再次调用 onBind(),便可将同一 IBinder 传递至任何其他绑定的客户端。当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非 startService() 也启动了该服务)。

2.通常情况下我们应该在客户端生命周期(如Activity的生命周期)的引入 (bring-up) 和退出 (tear-down) 时刻设置绑定和取消绑定操作,以便控制绑定状态下的Service,一般有以下两种情况:

  • 如果只需要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定。
  • 如果希望 Activity 在后台停止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在 onDestroy() 期间取消绑定。需要注意的是,这意味着 Activity 在其整个运行过程中(甚至包括后台运行期间)都需要使用服务,因此如果服务位于其他进程内,那么当提高该进程的权重时,系统很可能会终止该进程。

  3.通常情况下(注意),切勿在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定,因为每一次生命周期转换都会发生这些回调,这样反复绑定与解绑是不合理的。此外,如果应用内的多个 Activity 绑定到同一服务,并且其中两个 Activity 之间发生了转换,则如果当前 Activity 在下一次绑定(恢复期间)之前取消绑定(暂停期间),系统可能会销毁服务并重建服务,因此服务的绑定不应该发生在 Activity 的 onResume() 和 onPause()中

  4.我们应该始终捕获 DeadObjectException DeadObjectException 异常,该异常是在连接中断时引发的,表示调用的对象已死亡,也就是Service对象已销毁,这是远程方法引发的唯一异常,DeadObjectException继承自RemoteException,因此我们也可以捕获RemoteException异常。

  5.应用组件(客户端)可通过调用 bindService() 绑定到服务,Android 系统随后调用服务的 onBind() 方法,该方法返回用于与服务交互的 IBinder,而该绑定是异步执行的。

关于启动服务与绑定服务间的转换问题

通过前面对两种服务状态的分析,相信大家已对Service的两种状态有了比较清晰的了解,那么现在我们就来分析一下当启动状态和绑定状态同时存在时,又会是怎么的场景?
  虽然服务的状态有启动和绑定两种,但实际上一个服务可以同时是这两种状态,也就是说,它既可以是启动服务(以无限期运行),也可以是绑定服务。有点需要注意的是Android系统仅会为一个Service创建一个实例对象,所以不管是启动服务还是绑定服务,操作的是同一个Service实例,而且由于绑定服务或者启动服务执行顺序问题将会出现以下两种情况:

先绑定服务后启动服务

  如果当前Service实例先以绑定状态运行,然后再以启动状态运行,那么绑定服务将会转为启动服务运行,这时如果之前绑定的宿主(Activity)被销毁了,也不会影响服务的运行,服务还是会一直运行下去,指定收到调用停止服务或者内存不足时才会销毁该服务。

先启动服务后绑定服务

  如果当前Service实例先以启动状态运行,然后再以绑定状态运行,当前启动服务并不会转为绑定服务,但是还是会与宿主绑定,只是即使宿主解除绑定后,服务依然按启动服务的生命周期在后台运行,直到有Context调用了stopService()或是服务本身调用了stopSelf()方法抑或内存不足时才会销毁服务。

以上两种情况显示出启动服务的优先级确实比绑定服务高一些。不过无论Service是处于启动状态还是绑定状态,或处于启动并且绑定状态,我们都可以像使用Activity那样通过调用 Intent 来使用服务(即使此服务来自另一应用)。 当然,我们也可以通过清单文件将服务声明为私有服务,阻止其他应用访问。

最后这里有点需要特殊说明一下的,由于服务在其托管进程的主线程中运行(UI线程),它既不创建自己的线程,也不在单独的进程中运行(除非另行指定)。

这意味着,如果服务将执行任何耗时事件或阻止性操作(例如 MP3 播放或联网)时,则应在服务内创建新线程来完成这项工作,简而言之,耗时操作应该另起线程执行。只有通过使用单独的线程,才可以降低发生“应用无响应”(ANR) 错误的风险,这样应用的主线程才能专注于用户与 Activity 之间的交互, 以达到更好的用户体验。

前台服务以及通知发送 

前台服务被认为是用户主动意识到的一种服务,因此在内存不足时,系统也不会考虑将其终止。 前台服务必须为状态栏提供通知,状态栏位于“正在进行”标题下方,这意味着除非服务停止或从前台删除,否则不能清除通知。

例如将从服务播放音乐的音乐播放器设置为在前台运行,这是因为用户明确意识到其操作。 状态栏中的通知可能表示正在播放的歌曲,并允许用户启动 Activity 来与音乐播放器进行交互。如果需要设置服务运行于前台, 我们该如何才能实现呢?Android官方给我们提供了两个方法,分别是startForeground()和stopForeground(),这两个方式解析如下:

  • startForeground(int id, Notification notification)

该方法的作用是把当前服务设置为前台服务,其中id参数代表唯一标识通知的整型数,需要注意的是提供给 startForeground() 的整型 ID 不得为 0,而notification是一个状态栏的通知。

  • stopForeground(boolean removeNotification)

该方法是用来从前台删除服务此方法传入一个布尔值,指示是否也删除状态栏通知,true为删除。 注意该方法并不会停止服务。

但是,如果在服务正在前台运行时将其停止,则通知也会被删除。

服务Service与线程Thread的区别

两者的真正关系 = 没有关系。

两者概念的迥异

Thread 是程序执行的最小单元,它是分配CPU的基本单位,android系统中UI线程也是线程的一种,当然Thread还可以用于执行一些耗时异步的操作。

Service是Android的一种机制,服务是运行在主线程上的,它是由系统进程托管。它与其他组件之间的通信类似于client和server,是一种轻量级的IPC通信,这种通信的载体是binder,它是在linux层交换信息的一种IPC,而所谓的Service后台任务只不过是指没有UI的组件罢了

两者的执行任务迥异

在android系统中,线程一般指的是工作线程(即后台线程),而主线程是一种特殊的工作线程,它负责将事件分派给相应的用户界面小工具,如绘图事件及事件响应,因此为了保证应用 UI 的响应能力主线程上不可执行耗时操作。如果执行的操作不能很快完成,则应确保它们在单独的工作线程执行。

Service 则是android系统中的组件,一般情况下它运行于主线程中,因此在Service中是不可以执行耗时操作的,否则系统会报ANR异常,之所以称Service为后台服务,大部分原因是它本身没有UI,用户无法感知(当然也可以利用某些手段让用户知道),但如果需要让Service执行耗时任务,可在Service中开启单独线程去执行。

两者使用场景

当要执行耗时的网络或者数据库查询以及其他阻塞UI线程或密集使用CPU的任务时,都应该使用工作线程(Thread),这样才能保证UI线程不被占用而影响用户体验。

在应用程序中,如果需要长时间的在后台运行,而且不需要交互的情况下,使用服务。比如播放音乐,通过Service+Notification方式在后台执行同时在通知栏显示着。

两者的最佳使用方式

在大部分情况下,Thread和Service都会结合着使用:

  • 比如下载文件:一般会通过Service在后台执行+Notification在通知栏显示+Thread异步下载;
  • 再如应用程序会维持一个Service来从网络中获取推送服务。

在Android官方看来也是如此,所以官网提供了一个Thread与Service的结合来方便我们执行后台耗时任务,它就是IntentService,当然 IntentService并不适用于所有的场景,但它的优点是使用方便、代码简洁,不需要我们创建Service实例并同时也创建线程,某些场景下还是非常赞的!由于IntentService是单个worker thread,所以任务需要排队,因此不适合大多数的多任务情况。

管理服务生命周期 

  • 左图显示了使用 startService() 所创建的服务的生命周期。
  • 右图显示了使用 bindService() 所创建的服务的生命周期。

通过图中的生命周期方法,我们可以监控Service的整体执行过程,包括创建,运行,销毁

  • 服务的整个生命周期从调用 onCreate() 开始起,到 onDestroy() 返回时结束。与 Activity 类似,服务也在 onCreate() 中完成初始设置,并在 onDestroy() 中释放所有剩余资源。例如,音乐播放服务可以在 onCreate() 中创建用于播放音乐的线程,然后在 onDestroy() 中停止该线程。
  • 无论服务是通过 startService() 还是 bindService() 创建,都会为所有服务调用 onCreate() 和 onDestroy() 方法。
  • 服务的有效生命周期从调用 onStartCommand() 或 onBind() 方法开始。每种方法均有 Intent 对象,该对象分别传递到 startService() 或 bindService()。
  • 对于启动服务,有效生命周期与整个生命周期同时结束(即便是在 onStartCommand() 返回之后,服务仍然处于活动状态)。对于绑定服务,有效生命周期在 onUnbind() 返回时结束。

  从执行流程图来看,服务的生命周期比 Activity 的生命周期要简单得多。但是,我们必须密切关注如何创建和销毁服务,因为服务可以在用户没有意识到的情况下运行于后台。管理服务的生命周期(从创建到销毁)有以下两种情况:

  • 启动服务

该服务在其他组件调用 startService() 时创建,然后无限期运行,且必须通过调用 stopSelf() 来自行停止运行。此外,其他组件也可以通过调用 stopService() 来停止服务。服务停止后,系统会将其销毁。

  • 绑定服务

该服务在另一个组件(客户端)调用 bindService() 时创建。然后,客户端通过 IBinder 接口与服务进行通信。客户端可以通过调用 unbindService() 关闭连接。多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务。 (服务不必自行停止运行)

  • 启动服务与绑定服务的结合体

我们可以绑定到已经使用 startService() 启动的服务。例如,可以通过使用 Intent(标识要播放的音乐)调用 startService() 来启动后台音乐服务。随后,可能在用户需要稍加控制播放器或获取有关当前播放歌曲的信息时,Activity 可以通过调用 bindService() 绑定到服务。在

这种情况下,除非所有客户端均取消绑定,否则 stopService() 或 stopSelf() 不会真正停止服务。

如何保证服务不被杀死

  • 因内存资源不足而杀死Service

这种情况比较容易处理,可将onStartCommand() 方法的返回值设为 START_STICKY或START_REDELIVER_INTENT ,该值表示服务在内存资源紧张时被杀死后,在内存资源足够时再恢复。也可将Service设置为前台服务,这样就有比较高的优先级,在内存资源紧张时也不会被杀掉。

  • 用户通过 settings -> Apps -> Running -> Stop 方式杀死Service

这种情况是用户手动干预的,不过幸运的是这个过程会执行Service的生命周期,也就是onDestory方法会被调用,这时便可以在 onDestory() 中发送广播重新启动。这样杀死服务后会立即启动。这种方案是行得通的,但为程序更健全,我们可开启两个服务,相互监听,相互启动。服务A监听B的广播来启动B,服务B监听A的广播来启动A。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1896537.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI技术在招聘行业的应用

大模型AI技术在招聘行业的应用正变得越来越广泛&#xff0c;以下是一些关键领域的应用实例。大模型AI技术在招聘行业的应用不仅提高了效率和精确度&#xff0c;还帮助企业在竞争激烈的人才市场中获得优势。随着技术的不断发展&#xff0c;预计AI将在招聘领域扮演更加重要的角色…

grpc-go客户端接口添加

【1】 proto相关文件同服务端&#xff0c;如已经生成&#xff0c;可以直接使用服务端的文件&#xff08;包&#xff09; 【2】新建一个目录“WHG_CLIENT”&#xff0c;目录下新建一个main.go文件 package mainimport ("context""log""grpc-go-maste…

小程序新版获取用户头像、昵称调整方案

目录 前言调整背景调整说明低版本兼容处理参考代码WXMLWXSSJS补充及优化图像上传Java版图像上传接口.Net版图像上传接口前言 调整背景 由于 PC/macOS 平台「头像昵称填写能力」存在兼容性问题,对于来自低于2.27.1版本的访问,小程序通过 wx.getUserProfile 接口将正常返回用户…

23.【C语言】循环结构之while

接第11篇 格式&#xff1a;while (表达式)//加括号代表如果表达式为真&#xff0c;执行循环 { } 根据需要&#xff0c;可以在代码块中加break;提前跳出循环&#xff08;永久终止&#xff09; 而continue;是跳出本次循环&#xff0c;进入while循环的判断部分&#xff0c;决定是…

c++ 设计模式 的课本范例(下)

&#xff08;19&#xff09; 桥接模式 Bridge&#xff0c;不是采用类继承&#xff0c;而是采用类组合&#xff0c;一个类的数据成员是类对象&#xff0c;来扩展类的功能。源码如下&#xff1a; class OS // 操作系统负责绘图 { public:virtual ~OS() {}virtual void draw(cha…

C++ thread线程库

thread库 std::thread 是 C 标准库中的一个类&#xff0c;用于管理和控制单个执行线程。线程允许程序并行执行多个函数&#xff0c;从而提高性能和响应速度。std::thread 类提供了一种便捷的方式来创建和操作线程。 1、用途 并行执行任务&#xff1a; 通过 std::thread&…

守护家庭的安全卫士:家用可燃气体探测器

在这个追求智能与安全并重的时代&#xff0c;每一个细微之处的防护都显得尤为重要&#xff0c;尤其是在我们最为依赖的家庭空间里。当谈及家居安全&#xff0c;燃气安全无疑占据着至关重要的位置。据统计&#xff0c;每年因燃气管老化、连接处松动等问题引发燃气泄漏的事故不在…

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——4.预后相关外泌体基因确定单因素cox回归(2)

内容如下&#xff1a; 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的…

STM32-HAL-FATFS(文件系统)(没做完,stm32f103zet6(有大佬的可以在评论区说一下次板子为什么挂载失败了))

1STM32Cube配置 1-1配置时钟 1-2配置调试端口 1-3配置uart 1-4配置SDIO&#xff08;注意参数&#xff09;&#xff08;其中他的初始化的异常函数给注释&#xff0c;SD卡文件写了&#xff09; 配置了还要打开中断和DMA可在我的其他文章中看一样的 1-5配置FatFs (只改了图选中…

Unity 资源 之 Sweet Cakes Icon套装,110个高品质蛋糕图标分享

Sweet Cakes Icon 套装 - 为 Unity 开发者带来甜蜜惊喜 前言资源包内容领取兑换码 前言 亲爱的 Unity 开发者们&#xff0c;今天要向你们介绍一款令人心动的图标套装 - Sweet Cakes Icon。 Sweet Cakes Icon 套装包含了超过 110 种高品质的蛋糕和纸杯蛋糕图标&#xff0c;这无…

累积分布函数的一些性质证明

性质1&#xff1a; E [ X ] ∫ 0 ∞ ( 1 − F ( x ) ) d x − ∫ − ∞ 0 F ( x ) d x ( 1 ) E[X]\int_0^{\infty}(1-F(x))dx - \int_{-\infty}^0F(x)dx\quad (1) E[X]∫0∞​(1−F(x))dx−∫−∞0​F(x)dx(1) 证明&#xff1a; E [ X ] ∫ − ∞ ∞ x p ( x ) d x E[X] …

一个人的开发团队:前后端与调动AI

作为一名大数据开发者&#xff0c;我对 AI 的应用感兴趣&#xff0c;但平常都是处理数据&#xff0c;对应前后端代码不擅长&#xff0c;幸好有了 AI&#xff0c;在 AI 的帮助下能写出很多前后端代码了。 目录 前端开发后端开发调用AI整合前后端与AI 本文将通过一个简单的项目…

GO语言入门之准备

一、Go的简介 1.什么是Go Go 是一个开源的编程语言&#xff0c;最早起源于2007年&#xff0c;在2009年正式对外发布&#xff0c;Go 语言被设计成一门应用于搭载 Web 服务器&#xff0c;存储集群或类似用途的巨型中央服务器的系统编程语言。它能让构造简单、可靠且高效的软件变…

国产麒麟v10、UOS系统在线比较两个Word文件的内容差异

调用PageOffice的WordCompare方法&#xff0c;同时在线打开两个Word文档&#xff0c;可以切换显示其中的一个文档&#xff0c;或者显示两个文档的对比结果&#xff0c;即可实现在线的文档内容比较功能。此功能可以应用在以下方面&#xff1a; 文档管理中&#xff0c;比较两个版…

Day05-01-jenkins进阶

Day05-01-jenkins进阶 10. 案例07: 理解 案例06基于ans实现10.1 整体流程10.2 把shell改为Ansible剧本10.3 jk调用ansible全流程10.4 书写剧本 11. Jenkins进阶11.1 jenkins分布式1&#xff09;概述2&#xff09;案例08&#xff1a;拆分docker功能3&#xff09;创建任务并绑定到…

AI绘画擦边变现赛道怎么玩?新手小白必看教程!

今天给大家介绍一个用 AI 搞擦边的变现赛道 而且可以说是0 成本变现的 现在真的越来越多的人都想 0 成本变现&#xff0c;那么 0 成本到底能不能变现&#xff0c;变现的上下限又是多少&#xff1f; 今天这个案例就可以很好的进行说明 可以说 AI 是现在第一生产力&#xff0…

代码随想录——柠檬水找零(Leetcode860)

题目链接 贪心 class Solution {public boolean lemonadeChange(int[] bills) {if(bills[0] 10 || bills[0] 20 || bills[1] 20){return false;}int count5 1;int count10 0;for(int i 1; i < bills.length; i){if(bills[i] 5){count5;}if(bills[i] 10){count10;…

VBA常用的字符串内置函数

前言 在VBA程序中&#xff0c;常用的内置函数可以按照功能分为字符串函数、数字函数、转换函数等等&#xff0c;本节主要会介绍常用的字符串的内置函数&#xff0c;包括Len()、Left()、Mid()、Right()、Split()、String()、StrConV()等。 本节的练习数据表以下表为例&#xff…

Mybatis实现RBAC权限模型查询

RBAC权限模型 Role-Based Access Control&#xff0c;中文意思是&#xff1a;基于角色&#xff08;Role&#xff09;的访问控制。这是一种广泛应用于计算机系统和网络安全领域的访问控制模型。 简单来说&#xff0c;就是通过将权限分配给➡角色&#xff0c;再将角色分配给➡用…