Android - 服务Service

简介

一种可在后台执行长时间运行操作而不提供界面的应用组件。不会自己创建线程和进程(除非指定运行进程),只在启动该服务的进程中执行,通常情况下是主线程,因此降低应用正在运行的任何 Activity 的性能,所以如果需要在服务中执行耗时操作或者CPU密集型操作,就需要在服务内创建新线程来处理,从而降低发生ANR风险。

分类

服务类型不在于自身,主要依赖外部组件调用它的方式,其他组件通过bindService启动就是绑定服务,用startService启动就是后台服务,如果内部有常驻通知就是前台服务。

  • 绑定服务
    组件通过调用 bindService() 绑定一个服务,该服务就是绑定服务了,当有一个组件开始绑定时该服务创建,当所有组件解除绑定时服务自动销毁
  • 后台服务
    执行用户不会直接注意到的操作,组件通过startService() 启动,生命周期独立于启动他的组件。当执行任务完成后需要内部调用stopSelf()来自行销毁,或者其他组件调用stopService()
  • 前台服务
    执行一些用户能注意到的操作,在通知栏有常驻通知

简单使用

  1. 创建服务
  • 直接继承自基类Service:必须创建用于执行所有服务工作的新线程,因为服务默认使用应用的主线程,这会降低应用正在运行的任何 Activity 的性能
public class MusicService extends Service {
    private static final String LOG_TAG = MusicService.class.getSimpleName();
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(() -> {
            while (true){
                try {
                    Thread.sleep(1000);
                    Log.d(LOG_TAG, "MusicService 正在运行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }
}
  • 继承自IntentService:Service 的子类,其使用工作线程逐一处理所有启动请求。
  1. 创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。
  2. 创建工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样您就永远不必担心多线程问题。
  3. 在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()。
  4. 提供 onBind() 的默认实现(返回 null)。
  5. 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。
public class MusicService extends IntentService {
    private static final String LOG_TAG = MusicService.class.getSimpleName();
    public MusicService() {
        super(LOG_TAG);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        while (true){
            try {
                Thread.sleep(1000);
                Log.d(LOG_TAG, "MusicService 正在运行中");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果,在不同的线程
17:36:35.176 25660-25660/com.mh.smart D/MainActivity: MainActivity启动服务
17:36:51.232 25660-25751/com.mh.smart D/MusicService: MusicService 正在运行中
17:36:52.233 25660-25751/com.mh.smart D/MusicService: MusicService 正在运行中
17:36:53.239 25660-25751/com.mh.smart D/MusicService: MusicService 正在运行中
  1. 在AndroidManifest.xml中声明服务
<manifest ... >
  ...
  <application ... >
      <service android:name=".MusicService"
			//防止其他应用启动本应用服务
			android:exported=false/>
      ...
  </application>
</manifest>
  1. 启动服务
    不用使用隐式Intent,只用显示Intent,这样才能知道启动了哪些服务便于管理,并且在5.0以上用隐式Intent去绑定服务会抛异常
Intent intent = new Intent(this, MusicService.class);
startService(intent);
  1. 停止服务
在服务内部直接调用stopSelf()

在其他组件内调用stopService()
Intent intent = new Intent(this, MusicService.class);
stopService(intent)

前台服务

  1. 前台服务需要额外申请权限
<uses-permission android:name=“android.permission.FOREGROUND_SERVICE” />
  1. 在服务内部发送前台通知
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
	Log.d(LOG_TAG, "服务运行");
	Intent notificationIntent = new Intent(BaseApplication.application, MusicActivity.class);
	PendingIntent pendingIntent = PendingIntent.getActivity(BaseApplication.application, 1, notificationIntent, 0);
	NotificationCompat.Builder builder = new NotificationCompat.Builder(BaseApplication.application, NotifyUtils.NOTIFY_CHANNEL_MUSIC);
	builder.setSmallIcon(R.mipmap.ic_launcher)
			.setContentTitle("通知标题")
			.setContentText("这是一条通知的内容")
			.setPriority(NotificationCompat.PRIORITY_DEFAULT)
			.setContentIntent(pendingIntent);
	startForeground(1, builder.build());
	return super.onStartCommand(intent, flags, startId);
}
  1. 删除通知
1.直接关闭服务
stopService()

2.服务内部调用
stopForeground(1);

绑定服务

上面开启的服务都是在后台默默工作,为应用提供服务,与组件没啥交互。绑定服务就有点像C/S中的服务器了,它作为服务器,当有组件绑定它时,组件就是客户端,多个组件绑定也就是多个客户端,绑定它的组件可以是其他应用的,所以通信方面就用Binder

使用绑定服务有三种方式:

扩展 Binder 类

适用于该服务只会被本应用的组件绑定,并且客户端和服务端所在进程相同

创建可被绑定服务,即创建服务器
public class MusicControlService extends Service {
    private static final String LOG_TAG = MusicControlService.class.getSimpleName();

    private IBinder binder = new MusicControlBinder();

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

    @Override
    public IBinder onBind(Intent intent) {
		//返回一个Binder对象
        return binder;
    }

	/**
	* 该服务拥有的功能
	*/
    public void nextPlay(){
        mAudioPlayerManager.playNext();
    }

    public void prePlay(){
        mAudioPlayerManager.playPre();
    }

    public AudioPlayer getCurrentAudioPlayer(){
        return mAudioPlayer;
    }
	
	//通常使用内部类继承Binder,将本类的实例提供给客户端,客户端用实例直接使用需要的功能
    public class MusicControlBinder extends Binder{
        public MusicControlService getService(){
            return MusicControlService.this;
        }
    }
}

和与一般服务器通信一样需要连接,这边是使用ServiceConnection,在组件中先创建连接
private ServiceConnection serviceConnection = new ServiceConnection() {
	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
		//这边的service就是服务中onBinder返回的IBinder,多个组件一起绑定的时候返回的是相同的IBinder
		musicControlService = ((MusicControlService.MusicControlBinder) service).getService();
	}

	@Override
	public void onServiceDisconnected(ComponentName name) {

	}
}

开始连接
private void bindMusicService(){
	Intent intent = new Intent(this, MusicControlService.class);
	bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}

连接完成后就可以使用服务提供的功能了
musicControlService.getCurrentAudioPlayer();
musicControlService.nextPlay();
musicControlService.prePlay();


记得断开连接
private void unBindMusicService(){
	unbindService(serviceConnection);
}
使用 Messenger

适用于客户端不在当前进程的场景,即其他应用或本应用其他进程绑定。它与经常用的handler发送message内部实现和使用方式基本类似,内部也是一个队列,一次处理一个message,不同的是它是简化版的AIDL,可以进行进程间的通信,比AIDL使用方便。

创建绑定服务
public class MusicControlService extends Service {
    private static final String LOG_TAG = MusicControlService.class.getSimpleName();
    public static final int MUSIC_CONTROL_NEXT = 1;

	//需要一个handler,来处理客户端发来的消息
    public static class MusicControlHandler extends Handler{
        MusicControlHandler(){}

        @Override
        public void handleMessage(@NonNull Message msg) {
			//处理消息
            switch (msg.what){
                case MUSIC_CONTROL_NEXT:
                    Log.d(LOG_TAG, "MusicControlService 收到消息  参数是 :" + msg.obj);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
		//利用Messenger生成IBinder供客户端使用
        return new Messenger(new MusicControlHandler()).getBinder();
    }
}

客户端创建连接
private Messenger musicControlService;
private ServiceConnection serviceConnection = new ServiceConnection() {
	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
		//用传来的Ibinder生成Messenger,后面利用Messenger来发送消息,Messenger中的队列一个一个来处理消息
		musicControlService = new Messenger(service);
	}

	@Override
	public void onServiceDisconnected(ComponentName name) {
		musicControlService = null;
	}
};

开始连接(开始绑定)没有变
private void bindMusicService(){
	Intent intent = new Intent(this, MusicControlService.class);
	bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}

使用服务端的功能方式变了,应使用连接时拿到的Messenger来发送一个message
public void playNext(){
	try {
		Message message = new Message();
		message.what = MusicControlService.MUSIC_CONTROL_NEXT;
		message.obj = "上层参数";
		musicControlService.send(message);
	} catch (RemoteException e) {
		e.printStackTrace();
	}
}

断开连接也没变
private void unBindMusicService(){
	unbindService(serviceConnection);
}

注:

  1. 当第一个客户端绑定服务时,onBind方法会被执行,这时已经产生了IBinder,后续客户端绑定不会执行onBind会延用第一个产生的IBinder,除非该服务没有一个客户端绑定,才会执行onBind
  2. 当没有客户端与服务绑定时,服务会自动销毁,除非还通过startService启动了该服务
  3. 调用stopService时,如果有组件绑定了服务,服务也不会关闭
  4. 调用bindService后,服务内部调用的是onCreate,onBind。调用unBindService后,服务内部调用的是onUnbind和onDestroy
  5. 多次startService,服务的onCreate只会被调用一次
使用 AIDL

和Messanger不同的是可以一次处理多个,这时需要自己处理这个多线程,安全难度大,谷歌不建议用,但是如果是多进程通信并且需要内部多线程处理时还是需要用到AIDL

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值