Android中的service



简介 : 

创建一个服务 


 class TestService extends Service {

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

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

} 

第一个方法onCreate只有在Service被创建的时刻被调用。如果Service已经在运行中,这个方法将不会被调用。我们不能直接调用它,它是由系统负责调用的。

OnStartCommand方法是最重要的方法,因为它在我们需要启动Service的时候被调用。在这个方法中,我们拥有在运行Service 时传递进来的Intent,这样就可以与Service交换一些信息。在这个方法中,我们实现自己的逻辑:如果不是耗时的操作可以直接在这个方法中执行, 否则可以创建一个线程。正如你看到的那样,这个方法需要返回一个整型值。这个整型代表系统应该怎么样处理这个Service:

  • START_STICKY:使用这个返回值,如果系统杀死我们的Service将会重新创建。但是,发送给Service的Intent不会再投递。这样Service是一直运行的。
  • START_NOT_STICKY:如果系统杀死了Service,不会重新创建,除非客户端显式地调用了onStart命令。
  • START_REDELIVER_INTENT:功能与START_STICKY类似。另外,在这种情况下Intent会重新传递给Service。

OnDestory是在Service将被销毁时系统调用的方法。

一旦有了自定义的Service类,就要在Manifest.xml中声明,这样我们就可以使用了。



service的生命周期


被启动的服务的生命周期:

如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务

被绑定的服务的生命周期:

如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用 bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。


被启动又被绑定的服务的生命周期:

如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。

 

 当服务被停止时清除服务:

当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。



service使用的时候应当注意的事情


1、bindService 绑定到Service的时候,你就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自解除,并且Service会自动停止);

2、使用 startService 启动服务之后,一定要使用 stopService停止服务,不管你是否使用bindService; 

3、同时使用 startService 与 bindService 要注意到,Service 的终止,需要unbindService与stopService同时调用,才能终止 Service,不管 startService 与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService 之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 之前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止;

4、当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。

5、在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了 onStartCommand,不过之前的 onStart 任然有效。这意味着,如果你开发的应用程序用的 sdk 为 2.0 及其以后的版本,那么你应当使用 onStartCommand 而不是 onStart


在 AndroidManifest.xml 里 Service 元素的常见选项

android:name  -------------  服务类名

android:label  --------------  服务的名字,如果此项不设置,那么默认显示的服务名则为类名

android:icon  --------------  服务的图标

android:permission  -------  申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务

android:process  ----------  表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字

android:enabled  ----------  如果此项设置为 true,那么 Service 将会默认被系统启动,不设置默认此项为 false

android:exported  ---------  表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为 false

  • 本地服务 Local Service 用于应用程序内部。

  •   它可以启动并运行,直至有人停止了它或它自己停止。在这种方式下,它以调用Context.startService()启动,而以调用Context.stopService()结束。它可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。不论调用了多少次startService()方法,你只需要调用一次stopService()来停止服务。
      用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好。
  • 远程服务 Remote Service 用于android系统内部的应用程序之间。
      它可以通过自己定义并暴露出来的接口进行程序操作。客户端建立一个到服务对象的连接,并通过那个连接来调用服务。连接以调用Context.bindService()方法建立,以调用 Context.unbindService()关闭。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。
      可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。


播放音乐的实现方法 - 简单实现逻辑



一、首先定义一个接口,将要暴露出来操作的方法声明出来

//把想暴露的方法都定义在这里 
public interface Iservice {

        // 播放音乐的方法
	public void callPlayMusic();

      //暂停音乐播放的方法
	public void callPauseMusic();

      // 重新播放音乐的方法
	public void callRePlayMusic();
}

二、定义一个服务,并在清单文件中进行相关配制

public class MusicService extends Service {

	//(2)把我们定义的中间人对象在这里返回
	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

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

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

	// 播放音乐的方法
	public void playMusic() {
		System.out.println("音乐播放了 ...");
		
	}

	// 暂停音乐的方法
	public void pauseMusic() {
		System.out.println("音乐暂停了 ...");
	}

	// 继续播放音乐的方法
	public void replayMusic() {
		System.out.println("音乐继续播放了 ...");
	}
	

三、定义中间人对象


我们要进行Activity中操作服务中的方法,所以要定义一个中间人对象,继承Binder并实现我们定义的接口,并在相关方法中操作服务中的相关方法,



	//(1)定义中间人对象 
	private class MyBinder extends Binder implements Iservice{

		@Override
		public void callPlayMusic() {
			//调用播放音乐的方法 
			playMusic();
			
		}

		@Override
		public void callPauseMusic() {
			//调用暂停音乐的方法
			pauseMusic();
		}

		@Override
		public void callRePlayMusic() {
			//调用继续播放的方法
			replayMusic();
		}
		
	}

四、 定义出来中间人对象后,在服务中的

@Override
	public IBinder onBind(Intent intent) {
		return new MyBinder;
	}

将中间人对象返回;


五、接下来就是在Activity中的使用


首先监听服务,获取中间人对象,当服务连接成功后

//监视服务的状态
	private class MyConn implements ServiceConnection{

		

		//当服务连接成功的时候调用  获取我们定义的中间人对象(IBinder)
		@Override
		public void onServiceConnected(ComponentName name, IBinder iBinder) {
			//获取我们定义的中间人对象 

			iBinder= (Iservice) iBinder;
			
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			
		}
		
	}

那么在Activity的方法中 

@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		//(1)先调用startService() ,开户服务,服务就会在后台长期运行 
		Intent intent = new Intent(this,MusicService.class);
		startService(intent);
		
		//(2)调用bindservice()方法 可以获取我们定义的中间人对象 间接调用服务里面的方法
		
		conn = new MyConn();
		bindService(intent, conn, BIND_AUTO_CREATE);
		
		
		


然后我们就可以通过中间人对象来调用服务中的方法

	// 点击按钮 播放音乐
	public void click1(View v) {

		iBinder.callPlayMusic();
	}

	// 点击按钮 暂停音乐
	public void click2(View v) {
		iBinder.callPauseMusic();

	}

	// 点击按钮 继续播放
	public void click3(View v) {
		iBinder.callRePlayMusic();

	}

最后是要解绑服务的操作

@Override
	protected void onDestroy() {
		//当Activity销毁的时候 解绑服务 
		unbindService(conn);
		super.onDestroy();
	}



播放音乐的具体实现过程



首先创建一个接口,接口里面暴露出我们要在activity中操作音乐播放的方法


//把想暴露的方法都定义在这里 
public interface Iservice {

	//播放音乐的方法
	public void callPlayMusic();
	//暂停音乐的方法
	public void callPauseMusic();
	//重新播放音乐的方法
	public void callRePlayMusic();
	//设置音乐播放的位置
	public void callSetSeekPosition(int position);
	 
}

然后定义一个中间人对象,继承Binder并来实现我们所定义的接口;

这个中间人对象用来让Activity来操作服务中的方法

	//(1)定义中间人对象 
	private class MyBinder extends Binder implements Iservice{

		@Override
		public void callPlayMusic() {
			//调用播放音乐的方法 
			playMusic();
			
		}

		@Override
		public void callPauseMusic() {
			//调用暂停音乐的方法
			pauseMusic();
		}

		@Override
		public void callRePlayMusic() {
			//调用继续播放的方法
			replayMusic();
		}

		@Override
		public void callSetSeekPosition(int position) {
			//设置音乐播放指定位置的方法
			setSeekPosition(position);
			
		}
		
	}


然后定义一个服务,里面实现播放音乐的逻辑,并关联我们定义的中间人对象 


private MediaPlayer player;
	private Timer timer;
	private TimerTask task;

	//(2)把我们定义的中间人对象在这里返回
	@Override
	public IBinder onBind(Intent intent) {
		return new MyBinder();
	}

	//当服务第一次开启的时候回执行这个方法
	@Override
	public void onCreate() {
		//(1)创建meidaplayer 实例 
		player = new  MediaPlayer();
		super.onCreate();
	}

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

	// 播放音乐的方法
	public void playMusic() {
		System.out.println("音乐播放了 ...");
		try {
			//设置mediaplayer 为 初始化的状态
			player.reset();
			//(2) 设置播放的资源  路径可以 是本地路径 也可以是网络路径 
			player.setDataSource("/mnt/sdcard/xpg.mp3");
			//(3)准备播放 
			player.prepare();
			//(4)开始播放
			player.start();
			
			//(5)更新进度条的进度 
			updateSeekBar();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	//更新进度条的逻辑 
	//通过Handler来传输数据,在Activty中进行seekBar进度条的更新操作
	private void updateSeekBar() {
		//[1]获取歌曲总时长 和 当前播放的进度 
		final int duration = player.getDuration();
	
		//timer 定时器 
		//[2] 一秒钟获取一次当前进度 
		
		timer = new Timer();
		task = new TimerTask() {
			@Override
			public void run() {
				//每隔一秒钟获取一次 当前歌曲的位置 
				int currentPosition = player.getCurrentPosition();
				//创建message对象 
				Message msg = Message.obtain();
				
				//创建bundle 对象 实际底层就是map
				Bundle bundle = new Bundle();
				bundle.putInt("duration", duration);
				bundle.putInt("currentPosition", currentPosition);
				msg.setData(bundle);
				//发送消息 
				MainActivity.handler.sendMessage(msg);
				
				
			}
		};
		timer.schedule(task, 50, 1000); //50毫秒后 每隔一秒钟执行一次 task
		//[3]当歌曲播放完成的时候  把timer 和 tast 取消 
		player.setOnCompletionListener(new OnCompletionListener() {
			
			@Override
			public void onCompletion(MediaPlayer mp) {
			
				System.out.println("歌曲播放完了");
				timer.cancel(); //取消任务
				task.cancel();
				
				
			}
		});
		
	}

	// 暂停音乐的方法
	public void pauseMusic() {
		System.out.println("音乐暂停了 ...");
		player.pause(); //音乐暂停了
		
		
	}

	// 继续播放音乐的方法
	public void replayMusic() {
		System.out.println("音乐继续播放了 ...");
		player.start();
		
	}
	
	//设置音乐播放指定位置的方法
    public void setSeekPosition(int position){
		player.seekTo(position);
		
	}


然后在Activity中进行的操作 

首先创建服务的监听

	//监视服务的状态
	private class MyConn implements ServiceConnection{

		//当服务连接成功的时候调用  获取我们定义的中间人对象(IBinder)
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			//获取我们定义的中间人对象 
			iservice = (Iservice) service;
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			
		}
		
	}
	

然后创建Handler

//定义一个handler  ,来接收服务中传输过来的数据
	public static Handler handler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			//(1)获取我们服务中封装传输的数据 
			Bundle data = msg.getData();
			int duration = data.getInt("duration");
			int currentPosition = data.getInt("currentPosition");
			
			//(2)设置seekbar的最大进度 
			seekBar.setMax(duration);
			//设置当前进度
			seekBar.setProgress(currentPosition); 
		};
	};


然后初始化一些相关数据操作


	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		//(1)先调用startService(),开户服务 ,服务就会在后台长期运行 
		Intent intent = new Intent(this,MusicService.class);
		startService(intent);
		
		//(2)调用bindservice()方法 可以获取我们定义的中间人对象 间接调用服务里面的方法
		//为了获取我们定义的中间人对象 
		conn = new MyConn();
		bindService(intent, conn, BIND_AUTO_CREATE); 
		
		//(3)找到seekbar 设置当前进度 和 总进度(这是我们显示界面上的控件) 
		seekBar = (SeekBar) findViewById(R.id.seekBar1);
		
		//(4)给seekbar设置监听事件 ,用于拖动seekBar来改变音乐播放的进度
		seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
			//当拖动停止的时候调用
			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {
				//调用播放的指定位置的方法 
				iservice.callSetSeekPosition(seekBar.getProgress());
				
				
			}
			//刚开始拖动调用
			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {
				
			}
			//当进度发生改变的时候调用
			@Override
			public void onProgressChanged(SeekBar seekBar, int progress,
					boolean fromUser) {
				
			}
		});
		
		
		
	}


然后实现操作音乐播放的具体操作

	// 点击按钮 播放音乐
	public void click1(View v) {
		iservice.callPlayMusic();
	}

	// 点击按钮 暂停音乐
	public void click2(View v) {
		iservice.callPauseMusic();
	}

	// 点击按钮 继续播放
	public void click3(View v) {
		iservice.callRePlayMusic();
	}


最后解绑服务

@Override
	protected void onDestroy() {
		//当Activity销毁的时候 解绑服务 
		unbindService(conn);
		super.onDestroy();
	}
	






早起的年轻人 CSDN认证博客专家 移动开发 项目管理 Java
只要用心去做,每一件事情还是有可能成功的,当然成功是没有界限的,只不过是达到自己心里的那个目标,公众号:我的大前端生涯,一个爱喝茶的程序员,通常会搞搞SpringBoot 、Herbinate、Mybatiys、Android、iOS、Flutter、Vue、小程序等.
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页