一、服务
适用场景:非常适合执行那些不需要和用户交互而且长期运行的任务。
注意点:1)服务并不是运行在一个独立的进程当中,而是依赖于创建服务时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。
2)服务并不会自动开启线程,所有代码都默认运行在主线程当中。
二、解析异步消息处理机制
Android中的异步消息处理主要有四个部分组成:Message、Handler、MessageQueue和Looper。
1、Message:是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程间交换信息。
2、Handler:可用来处理消息(发送和处理)。
3、MessageQueue:主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
4、Looper:是每个线程中的MessageQueue的管家,调用Looper的loop()后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()中。每个线程中只会有一个Looper对象。
三、AsyncTask
1、参数介绍。1)Params:执行AsyncTask时需传入的参数,可在后台任务中执行。2)Progress:后台任务执行时,需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。3)Result:当任务执行完毕,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
2、方法介绍
1)onPreExecute():在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度对话框等。
2)doInBackground(Params…):子线程中执行,如果AsyncTask第三个泛型参数指定的是Void,就可以不返回任务执行结果。如果需要更新UI元素,比如反馈当前任务的执行进度,可以调用publishProgress(Progress…)来完成。
3)onProgressUpdate(Progress…):当后台执行了publishProgress(Progress…)后,这个方法很快会被调用,方法中携带的参数是后台任务中传递过来的。这个方法中可以对UI进行操作,利用参数中的数值就可对界面元素进行相应更新。
4)onPostExecute(Result):当后台任务执行完成并通过return返回时,这个方法很快会被调用。可进行一些UI操作。
三、服务的基本用法
1、需要在清单文件中注册(四大组件都需注册)。
2、方法介绍
1)onBind():
2)onCreate():在服务第一次创建时调用。
3)onStartCommand():在每次服务启动时调用。
4)onDestroy():在服务销毁时调用。
3、启动和停止服务
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent); // 启动服务
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent); // 停止服务
上面服务的停止是由活动来决定的,如何让服务自己停止呢?只需在服务中调用stopSelf()即可。
4、活动和服务相互通信
1)活动->服务
上述启动服务的方法,在服务启动后,活动与服务基本没关系了。通过onBind(),活动可以指挥服务。
步骤一:服务中定义一个Binder类和对象,在onBind()中返回该Binder对象。
步骤二:活动中取得该Binder对象,就可通过该Binder对象控制服务。
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
};
步骤三:记得绑定服务和解绑服务
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE); // 绑定服务
第一个参数是intent对象;第二个参数是ServiceConnection实例;第三个参数是一个标志位,这里传入BIND_AUTO_CREATE表示在活动和服务绑定后自动创建服务。这会使得服务中的onCreate()执行,按onStartCommand()不会执行。
unbindService(connection); // 解绑服务
解绑服务后,会执行服务的onDestroy()。
2)服务和活动怎么通信呢?
如果通过bindService(),在服务中可通过回调方法来通知活动。
通过广播向活动发送消息。
四、服务的生命周期
onCreate()、onStartCommand()、onBind()、onDestroy()。
虽然每次调用startService(),onStartCommand()就会执行一次,但实际上每个服务都只会存在一个实例。所以不管调用多少次startService(),只需调用一次stopService()或stopSelf(),服务就会停止。
如果对一个服务既调用了startService()又调用了bindService(),此时该如何销毁服务呢?根据Android的机制,一个服务只要被启动或被绑定后,就会一直处于运行状态,必须让两个条件都不满足,服务才能被销毁。所以,要同时调用stopService()和unbindService(),onDestroy()才会被执行。
五、服务的高级技巧
1、使用前台服务
需求:服务的系统优先级比较低,当系统出现内存不足时,有可能回收掉正在后台运行的服务。
解决方案:使用前台服务,它不会由于系统内存不足而被回收。
与后台服务区别:它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态后可看到更加详细的信息,非常类似于通知的效果。当然有些项目会由于特殊需求必须使用前台服务。比如墨迹天气:它的服务在后台更新数据的同时,还会在系统状态栏一直显示当前的天气信息。
用法:通过startForeground()就会让后台服务变成一个前台服务,并在系统状态栏显示出来。第一个参数是id,第二个参数怎是构建出的Notification对象。
public class MyService extends Service {
……
@Override
public void onCreate() {
super.onCreate();
Notification notification = new Notification(R.drawable.ic_launcher, "Notification comes", System. currentTimeMillis());
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, "This is title", "This is
content", pendingIntent);
startForeground(1, notification);
Log.d("MyService", "onCreate executed");
}
……
}
2、使用IntentService
需求:服务中的代码默认运行在主线程当中,如果直接在服务里处理一些耗时操作,容易出现ANR。
解决方案1:在服务的具体方法里开启一个子线程,去处理耗时操作。
@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);
}
如果想让服务在线程执行完后自动停止,只需在上面的run()处理完具体逻辑后调用stopSelf()。
解决方案2:为了可以简单创建一个异步的、会自动停止的服务,Android专门提供了一个IntentService类。
此方案优点:相比方案1来说,它集成开启线程和自动停止。
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService"); // 必须调用父类的有参构造函数
}
@Override
protected void onHandleIntent(Intent intent) {
// 打印当前线程的id
Log.d("MyIntentService", "Thread id is " + Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService", "onDestroy executed");
}
}
onHandleIntent():此方法在子线程中,可执行具体的逻辑。上面重写onDestroy()只是为了验证服务是否停止。