基础概念
服务常被用作去执行不需要与用户交互而且需要长期运行的任务。服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者打开了另一个应用程序,服务依旧可以保持正常运行。
android多线程
当我们执行一些耗时操作,比如发送网络请求,如果不把这类操作放到子线程里去执行,会导致主线程被阻塞。
基本使用方法
new Thread(new Runnable(){
@Override
public void run(){
//处理逻辑
}
}).start();
在子线程中更新UI
在android里面如果需要更新UI元素,则必须要在主线程中进行,否则会出现异常。对于这一问题android给出了一套异步消息处理机制。
//用于接受消息,并在主线程里面进行处理
private Handler handler=new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
case UPDATE_TEXT:
//进行相关UI操作
break;
}
}
}
//在子线程里面发送消息
public void test(){
new Thread(new Runnable(){
@Override
public void run(){
//逻辑处理
Message message=new Message();
message.what=UPDATE_TEXT;
handler.sendMessage(message); //将message发送出去
}
})
}
异步消息处理主要由四个部分组成:
-
Message 在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程间交换数据。
-
Handler主要用于发送消息和处理消息。发送消息用Handler的sendMessage()方法,最终会传递到Handler的handlerMessage()方法中进行处理
-
MessageQueue消息队列,主要用于存放所有通过Handler发送的消息。每个线程中只有一个MessageQueue对象
-
Looper是每个线程中MessageQueue的管家,调用Looper loop()方法后会进入到一个无限的循环中,每当发现MessageQueue中存在一条消息,就会将它去除,并传递到Handler的handlerMessage方法中。每个线程中只有一个Looper对象。
使用AsyncTask
AsyncTask背后实现原理也是基于异步消息处理机制的,只是Android做了封装。
public class DownloadImageTask extends AsyncTask<Void, Integer, Boolean> {
//第一个Void是执行AsyncTask需要传入的参数
//Integer是进度
//Boolean是执行结果
@Override
onPreExecute()
//在后台任务开始执行前调用,用于进行一些页面初始化工作
@Override
doInBackground()
//这个方法中所有代码在子线程中执行,处理一些耗时的任务。return返回执行结果,如果第三个泛型参数设置Void则不返回执行结果
@Override
onProgressUpdate(...)
//该方法携带的参数就是后台任务中传递过来的,在这个方法里面可以对UI进行操作,利用参数中传来的数值对界面元素进行更新
@Override
onPostExecute(Result)
//后台任务执行完毕后通过return返回,这个方法就会被调用,可以根据返回的数据做一些UI操作,比如关闭加载框之类的
}
服务的基本用法
创建服务时两个关键字
exported表示是否允许除了当前程序以外其他程序访问这个服务
Enabled表示是否启用这个服务
public class MyService extends Service{
}
生命周期
onCreate():在服务创建的时候调用
onStartCommand():在服务启动的时候调用
-
START_STICKY:若服务进程被杀死,系统会自动重建服务并重新调用 onStartCommand()。如果无待处理的 Intent,onStartCommand() 的 intent 参数为 null,需做判空处理。
-
START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务;
-
START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入;
-
START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
onBind():绑定服务 bindService时调用
onDestroy():在服务销毁的时候调用
启动、关闭服务
Intent startIntent=new Intent(this,MyService.class);
startService(startIntent);
Intent stopIntent=new Intent(this,MyService.class);
stopService(stopIntent);
每个服务只会存在一个实例,所以不管执行了几遍startService,只用执行一次stopService即可停止服务
活动和服务间的通信
使用场景:比如service提供一个下载功能,在活动中可以决定何时开始这个功能,以及随时查看进度
public class MyService extends Service{
private DownLoadBinder mBinder=new DownLoadBinder();
class DownLoadBinder extends Binder{
//提供两个方法 开始下载 查看下载进度
public void startDownLoad{
}
public int getProgress{
return 0;
}
}
@Override
public IBinder onBind(Intent intent){
return mBinder;
}
}
public MainActivity extends AppCompatActivity{
private MyService.DownLoadBinder downloadBinder;
private ServiceConnection connection=new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
downloadBinder=(MyService.DownLoadBinder)service;
downloadBinder.startDownLoad();
downloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
//在某一时机绑定服务
Intent bindIntent=new Intent(this,MyService.class);
bindService(bindIntent,connection,BIND_AUTO_CREATE);
//在活动和服务绑定后自动创建服务,调用服务的onCreate()方法
//在某一时机解绑服务
unbindService(connection)
}
activity中执行bindService->Service回调 onBind 返回一个IBinder对象->可以跟上述例子一样向下转型拿到实例
startService->stopService bindService->unbindService onDestroy调用
但如果既调用了startService 又调用了bindService 此时要调用stopService和unbindService onDestroy才会调用
前台服务
前台服务会有一个正在运行的图标在系统的状态栏。
public static void showServiceNotice(Service service) {
Intent intent = new Intent(AMApp.getInstance(), MainActivity.class);
PendingIntent pendingIntent= PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
//PendingIntent.FLAG_UPDATE_CURRENT 如果已存在同类型的 PendingIntent,则保留该 PendingIntent,但将其附加的 Intent 数据替换为最新的。
//PendingIntent.FLAG_IMMUTABLE声明该 PendingIntent 不可被接收方修改
builder.setContentTitle(noticeTitle)
.setContentText(noticeContent)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setWhen(System.currentTimeMillis())
.setOngoing(true)
.setSilent(true)
.setAutoCancel(false);
Notification notification = builder.build();
int notificationID = 1111;
notificationManager.notify(notificationID, notification);
service.startForeground(notificationID, notification);
}
IntendService
服务中耗时的部分最好在子线程中处理
onHandleIntent()就是在子线程中运行了,可以处理一些耗时操作。并且在服务运行结束后可以自动停止,onDestroy会被调用。