1.Service概要
- 服务(Service) 是Android中实现程序后台运行的解决方案, 它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务。服务的运行不依赖于任何用户界面, 即使程序被切换到后台, 或者用户打开了另外一个应用程序, 服务仍然能够保持正常运行。
- 服务并不是运行在一个独立的进程当中的, 而是依赖于创建服务时所在的应用程序进程。 当某个应用程序进程被杀掉时, 所有依赖于该进程的服务也会停止运行。
- 服务并不会自动开启线程, 所有的代码都是默认运行在主线程当中的。 也就是说, 我们需要在服务的内部手动创建子线程, 并在这里执行具体的任务, 否则就有可能出现主线程被阻塞住的情况。
2.Android多线程编程
2.1线程基本用法
常用方法是,用Thread类的匿名类的形式并且实现Runnable接口,再调用它的start0方法, 就使
得被重写的run0方法中的耗时操作运行在子线程当中了。代码如下:
new Thread(new Runnable() {
@override
public void run() {
//耗时操作的逻辑
}
}). start();
2.2在子线程更新UI问题及方法
Android的UI是线程不安全的,如果想要更新应用程序里的UI元素,则必须在主线程中进行,否则就会出现异常
2.2.1异步消息处理机制
android开发艺术(八)之android的消息机制
主要用到两个类:Handler(处理者,主要用于发送和处理消息)和Message(信息,可携带少量信息用于在不同线程之间交换)
- Message
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交
换数据。可以使用what、arg1和arg2字段来携带一些整型数据,使用obj字段携带一个object 对象。 - Handler
Handler主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列地辗转处理后, 最终会传递到Handler的handleMessage ()方法中。 - MessageQueue
MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue 对象。 - Looper
Looper是每个线程中的MessageQueue的管家,调用Looper的loop() 方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对象。
大致流程图:
2.2.2使用AsyncTask
启动任务:
new ChangeTask().execute();
3.服务的使用
3.1普通服务
第一步:新建类并继承Service且必须重写onBind()方法,有选择的重写onCreate()、onStartCommand()及onDestroy()方法。
第二步:在配置文件中进行注册。
第三步:在活动中利用Intent可实现Service的启动和停止
3.2前台服务
前台服务和普通服务最大的区别是,前者会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。使用前台服务或者为了防止服务被回收掉,比如听歌,或者由于特殊的需求,比如实时天气状况。
想要实现一个前台服务非常简单,它和之前学过的发送一个通知非常类似,只不过在构建好一个Notification之后,不需要NotificationManager将通知显示出来,而是调用了startForeground()方法。
3.3系统服务
AlarmManager实现定时的原理:
通过服务和广播的循环触发实现定时服务。
3.4IntentService
为了可以简单地创建一个异步的、会自动停止的服务,Android 专门提供了一个IntentService类。
问题:服务运行在主线程,不能有超时操作(解决办法 onStartCommand中开启线程)
服务中的任务执行结束,服务不会自动停止(解决办法 onStartCommand中添加stopSelf())
第一步:新建类并继承IntentService
第二步:在配置文件中进行注册。
第三步:在活动中利用Intent实现IntentService的启动
3.5Service与Activity的通信
第一步:在MyService里自定义一个类MyBinder并继承Binder,在它的内部提供了开始下载以及查看下载进度的方法,在MyService的onBind()方法里返回刚刚定义好的MyBinder类
第二步:在活动中实例化一个ServiceConnection类,并重写它的两个方法,这两个方法分别会在活动与服务成功绑定以及解除绑定的时候调用。在 onServiceConnected()方法中,又通过向下转型得到了MyBinder 的实例,有了它就可以在活动中根据具体的场景来调用MyBinder中的任何非private方法了
第三步:在活动布局里再准备两个按钮用于绑定和解绑服务,在它们的点击事件里利用Intent对象实现活动和服务的绑定和解绑
运行结果:
4.Service生命周期
从上图可看到有两种方法可以启动Service,:
第一种:其他组件调用Context的startService()方法可以启动一个Service,并回调服务中的onStartCommand()。如果该服务之前还没创建,那么回调的顺序是onCreate()->onStartCommand()。服务启动了之后会一直保持运行状态,直到stopService()或stopSelf()方法被调用,服务停止并回调onDestroy()。每调用一次startService()方法,onStartCommand() 就会执行一次,但每个服务都只会存在一个实例。不管调用了多少次startService() 方法, 只需调用一次stopService() 或stopSelf()方法,服务就会停止
第二种:可以调用Context的bindService() 来获取一个服务的持久连接,这时就会回调服务中的onBind() 方法;如果这个服务之前还没有创建过,onCreate() 方法会先于onBind() 方法执行。之后调用方可以获取到onBind() 方法里返回的IBinder 对象的实例,这样就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。直到调用了unbindService()方法服务会停止,回调顺序onUnBind()->onDestroy)。
主要是为了活动和服务之间的通信。
注意:
- 当调用了startService() 方法后,又去调用stopService() 方法,这时服务中的onDestroy() 方法就会执行,表示服务已经销毁了;当调用了bindService()方法后,又去调用unbindService() 方法,onDestroy() 方法也会执行
- 对一个服务既调用了startService()方法,又调用了bindService()方法的,根据Android系统的机制,一个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁。所以,这种情况下要同时调用stopService()和unbindService()方法, onDestroy()方法才会执行
防止Service被杀死
- 在Service的onStartCommand()中设置flages值为START_STICKY,使得Service被杀死后尝试再次启动Service
- 提升Service优先级,比如设置为一个前台服务
- 在Activity的onDestroy()通过发送广播,并在广播接收器的onReceive()中启动Service