Android Service知识(上)

本文详细解析了Android Service的基本用法,包括Service的生命周期、与Activity的通信、销毁方式以及Service与Thread的关系。重点阐述了如何通过Intent启动Service、Service与Activity之间的通信方式,并介绍了Service的前台与后台概念。同时,文章还讨论了Service与Thread的区别及使用场景,为开发者提供了清晰的指导。

Service的基本用法

新建一个MyService继承自Service

public class MyService extends Service {

	private final String TAG = MyService.class.getSimpleName();

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

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.i(TAG, TAG + " onStartCommand");
		return super.onStartCommand(intent, flags, startId);
	}

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

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

}

记住需要在Manifest.xml中注册这个服务

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        ...
        
        <service android:name="com.example.testservice.MyService"></service>
    </application>

Service最基本的启动方法,类似于启动Activity,需要用到Intent

Intent serviceStart=new Intent(this, MyService.class);
startService(serviceStart);
这样就启动了我们刚刚写的MyService

我们看看输出信息,可以发现当我们这样启动一个Service的时候,会调用该Service中的onCreate()和onStartCommand()方法


我们想一下,如果已经启动的这个Service,我们调用startService来再次启动这个Service,这时候会输出什么呢?


这时候,这个Service只调用了onStartCommand()方法,而没有调用onCreate(),因此我们可以知道,当再次通过startService来再次启动已经启动当时还没有销毁的Service,这个Service就只会调用onStartCommand()


Service与Activity的通信

有时候我们需要Service与Activity进行通信,让Activity指挥Service在合适的时候做合适的事情,这时候我们可以将Service绑定到Activty,这样它们就可以进行简单的通信了。

我们改写一下上面的MyService

public class MyService extends Service {

	private final String TAG = MyService.class.getSimpleName();

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

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.i(TAG, TAG + " onStartCommand");
		return super.onStartCommand(intent, flags, startId);
	}

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

	@Override
	public IBinder onBind(Intent intent) {
		Log.i(TAG, TAG + " onBind");
		return new MyBinder();
	}

	@Override
	public boolean onUnbind(Intent intent) {
		Log.i(TAG, TAG + " onUnbind");
		return super.onUnbind(intent);
	}

	public class MyBinder extends Binder {
		public void startDownLoad() {
			Log.i(TAG, "MyBinder" + " startDownLoad");
		}
	}

}
在MyService里面,写了一个继承自Binder的MyBinder类,这个MyBinder类有一个startDownLoad()的方法,然后在onBind()方法里面返回一个MyBinder的实例

接着,我们就需要将MyService绑定到Activity里面

首先,在Activity在创建一个ServiceConnection的匿名类,实现onServiceConnected()和onServiceDisconnected(),这两个方法会在绑定和解绑Service的时候回调。

private ServiceConnection conn=new ServiceConnection() {
		
		@Override
		public void onServiceDisconnected(ComponentName name) {
			
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder binder) {
			((MyBinder)binder).startDownLoad();
		}
	};
然后我们这个将Myservice绑定到Activity中

Intent serviceBind=new Intent(this, MyService.class);
bindService(serviceBind, conn,Service. BIND_AUTO_CREATE );	
在Activity中,我们把ServiceConnection中onServiceConnected方法的IBinder实例向下转型成MyBinder,为什么可以这样转型呢,因为MyBinder是Binder的子类,而Binder又实现了IBinder接口,所以这样我们就可以得到了MyService中,在onBind()方法返回的MyBinder实例,这样我们就可以在Activity中指挥MyService执行相应的任务了

当我们这样启动Service之后,我们看一下输出信息


可以看到,这样的方式启动Service的时候,只会回调onCreate()方法,然后回调onBind()方法

这里还有一个疑问就是,但我们通过这样绑定的方式再次通过bindService再次启动已经绑定但是还没有销毁的Service的时候,会出现什么情况的?

我们再次启动这个已经启动的Service的时候,看一下输出就会发现,这个方式再次启动的Service的时候,Service是不会回调onCreate或者onBind了


销毁Service

通过startService方式来启动service,是这样来销毁service的

Intent serviceStop=new Intent(this, MyService.class);
stopService(serviceStop);

通过bindService方式来启动service,是这样来销毁service的

unbindService(conn);


如果先通过startService启动service,有通过bindservice来再次启动

看一下输出,来了解一下service的回调方法的执行情况


可以发现,当先通过startService来启动service的时候,会回调onCreate和onStartCommand,再次通过bindService来启动这个service的时候,就只会执行回调onBind了

这是如何销毁service呢?我们需要先后调用stopService和unbindService才能彻底销毁这个Service

Intent serviceStop=new Intent(this, MyService.class);
stopService(serviceStop);
unbindService(conn);
因为stopService只会让service停止运行,unbindService只会让service解除绑定,而一个service只有在既没有和任何Activity关联且停止运行的时候,才会彻底销毁


Service与Thread的关系

Service和Thread到底有什么关系呢?什么时候应该用Service,什么时候又应该用Thread?答案可能会有点让你吃惊,因为Service和Thread之间没有任何关系!

之所以有不少人会把它们联系起来,主要就是因为Service的后台概念。Thread我们大家都知道,是用于开启一个子线程,在这里去执行一些耗时操作就不会阻塞主线程的运行。而Service我们最初理解的时候,总会觉得它是用来处理一些后台任务的,一些比较耗时的操作也可以放在这里运行,这就会让人产生混淆了。但是,如果我告诉你Service其实是运行在主线程里的,你还会觉得它和Thread有什么关系吗?让我们看一下这个残酷的事实吧。

我们分别在MainActivity和MyService的onCreate()中打印当前线程的id

Log.i(TAG, TAG + " thread id is "+Thread.currentThread().getId());
然后我们看一下输出



这就证明了Service是运行在主线程的,如果在Service中执行耗时的操作,是会出现ANR的情况的。


你可能会惊呼,这不是坑爹么!?那我要Service又有何用呢?其实大家不要把后台和子线程联系在一起就行了,这是两个完全不同的概念。Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。你可能又会问,前面不是刚刚验证过Service是运行在主线程里的么?在这里一直执行着心跳连接,难道就不会阻塞主线程的运行吗?当然会,但是我们可以在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。


额,既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

因此我们比较标准的Service可以写成这样

        @Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				// 执行耗时的后台操作				
			}
		}).start();
		return super.onStartCommand(intent, flags, startId);
	}
	

	public class MyBinder extends Binder {
		public void startDownLoad() {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					// 执行耗时的后台操作				
				}
			}).start();
		}
	}
创建前台Service
Service几乎都是运行在后台的,但是Service的系统优先级还是比较低的,当系统内存不足需要回收资源的时候,就会把后台的Service销毁。

如果希望Service可以一直保存运行,但是又不被系统回收,可以考虑前台Service。前台Service与普通Service最大的不同点在于,前台Service会一直有一个运行的标志在状态栏中,下拉通知栏之后可以看到关于这个Service的更具体信息。

下面就是创建一个前台Service了,其实只需要在简单修改一下MyService的代码就可以实现了。

        @Override
	public void onCreate() {
		super.onCreate();
		Notification nf=new Notification(R.drawable.ic_launcher, "一个前台Service创建了", System.currentTimeMillis());
		Intent ni=new Intent(this, MainActivity.class);
		PendingIntent pi=PendingIntent.getActivity(this, 0, ni, 0);
		nf.setLatestEventInfo(this, "通知的标题", "通知的内容", pi);
		startForeground(1, nf);
	}
然后我们看一下效果图



判断一个Service是否还在运行

有时候我们需要判断Service是否还在运行,来决定是否执行相应的任务,我们可以这样处理

private boolean isServiceRunning(){
		
		ActivityManager am=(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
		List<RunningServiceInfo> services=am.getRunningServices(30);
		for (RunningServiceInfo info : services) {
			if (info.service.getClassName().toString().equals("com.example.testservice.MyService")) {
				return true;
			}
		}
		return false;
	}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值