Service
非常适合用于去执行那些不需要和用户交互而且还要求长期运行的任务。服务的运行不依赖于任何用户界面,即使当程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。服务并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。另外,服务并不会自动开启线程,所有的代码都是默认运行在主线程当中的。也就是说,我们需要在服务的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞的情况。
定义一个服务(启动时服务)
一致运行在后台,执行单一操作(执行完成之后不会讲获取的数据结果返回给启动者,无交互)
public class MyService extends Service{
//唯一的一个抽象方法 子类必须要实现它 绑定式服务使用
public IBinder onBind(Intent intent){
return null;
}
//服务创建的时候调用 只调用一次
public void onCreate(){
super.onCreate();
}
//每次服务启动的时候调用 每次启动都调用
public int onStartCommand(Intent intent,int flags,int startId){
return super.onStartCommand(intent,flags,startId);
}
//服务销毁的时候调用 回收那些不再使用的资源
public void onDestroy(){
super.onDestroy();
}
}
注:
1. 记得注册
<service android:name=".MyService"/>
2. 根据startCommand()的返回值的类型不同,可分为三种情况:
粘性服务:START_STICKY 执行完onStartCommand()后服务被异常杀死,那么系统会自动重启服务,但服务中的intent意图对象不会重传,为null
非粘性服务:START_NO_STICKY 执行完onStartCommand()后服务被异常杀死,那么系统不会自动重启服务。
自动重新请求: START_REDELIVER_INTENT 执行完onStartCommand()后异常被杀死,那么系统会自动重新启动服务,并且重新传入intent对象
启动和停止服务
public class MainActivity extends Activity implements OnClickListener{
private Button startService;
private Button stopService;
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService =(Button)findViewById(R.id.start_service);
stopService =(Button)findViewById(R.id..stop_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
}
public void onClick(View view){
switch(view.getId()){
case R.id.start_service:
Intent startIntent=new Intent(this,MyService.class);
startService(startIntent);//启动服务
break;
case R.id.stop_service:
Intent intent=new Intent(this,MyService.class);
stopService(stopService);//停止服务
break;
}
}
}
注:
要是在 Service 中停止服务,可以通过 stopSelf() 来停止服务。
绑定式服务
启动式服务,在服务启动之后,活动和服务基本上就没有什么关系了。而绑定式服务,却可以实现活动和服务之间的通信,可以实现活动指挥这服务。
public class MyService extends Service{
private DownLoadBinder mBinder =new DownloadBinder();
class DownloadBinder extends Binder{
public void startDownload(){
//执行下载任务
}
public int getProgress(){
//反馈下载的情况
}
}
public IBinder onBind(Intent intent){
return mBinder;
}
}
activity:
public class MainActivity extends Activity implements OnClickListener{
private Button bindService;
private Button unbindService;
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection=new ServiceConnection(){
public void onServiceDisconnected(ComponentName name){
//活动与服务解除绑定的时候调用
}
public void onServiceConnected(ComponentName name,IBinder service){
//活动与服务成功绑定的时候调用
downloadBinder =(MyService.DownloadBinder)service;
downloadBinder .startDownload();
downloadBinder.getProgress();
}
}
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
bindService=(Button)findViewById(R.id.bind_service);
unbindService = (Button)findViewById(R.id.unbind_service);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
public void onClick(View view){
switch(view.getId()){
case R.id.bind_service:
Intent intent=new Intent(this,MyService.class);
bindService(bindIntent,connection,BIND_AUTO_CREATE); //BIND_AUTO_CREATE 表示活动和服务进行绑定后自动创建服务
break;
case R.id.unbind_service:
unbindService(connection); //解除绑定服务
break;
}
}
}
注:
任何一个服务在整个应用程序范围内都是通用的,即MyService不仅可以和MainActivity绑定,还可以和任何一个其他的活动进行绑定,而且绑定完成后它们都可以获取到相同的DownloadBinder实例。
我们是完全有可能对一个服务既调用startService()方法,又调用bindService()方法的,这种情况下该如何才能让服务销毁呢?根据Android系统的机制,一个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁。所以,这种情况下要同时调用stopService()和unbindService()方法,onDestroy()方法才会执行。
前台服务
服务几乎都是在后台运行的,一直以来它都是默默地做着辛苦的工作。但是服务的系统优先级还是比较低的,当系统出现内存不足的情况时,就有可能会回收掉正在后台运行的服务。如果你希望服务可以一直保持运行状态,而不会由于系统内存不足的原因导致被回收,就可以考虑使用前台服务。前台服务和普通服务最大的区别就在于,它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。
public class MyService extends Service{
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);
}
}
IntentService
为了可以简单地创建一个异步的、会自动停止的服务,Android专门提供了一个IntentService类,这个类就很好地解决了忘记开启线程和关闭服务。
public class MyIntentService extends IntentService{
public MyIntentService(){
super("MyIntentService"); //调用父类的有参构造函数
}
protected void onHandleIntent(Intent intent){
//自启动一个线程来执行耗时操作
}
public void onDestroy(){
super.onDestroy();
}
}
注:
首先要提供一个无参的构造函数,并且必须在其内部调用父类的有参构造函数。然后要在子类中去实现onHandleIntent()这个抽象方法,在这个方法中可以去处理一些具体的逻辑,而且不用担心ANR的问题,因为这个方法已经是在子线程中运行的了。另外根据IntentService的特性,这个服务在运行结束后应该是会自动停止的。
定时任务
Android 中的定时任务一般有两种实现方式,一种是使用Java API 里提供的Timer 类,一种是使用Android 的Alarm 机制。这两种方式在多数情况下都能实现类似的效果,但Timer有一个明显的短板,它并不太适用于那些需要长期在后台运行的定时任务。我们都知道,为了能让电池更加耐用,每种手机都会有自己的休眠策略,Android 手机就会在长时间不操作的情况下自动让CPU 进入到睡眠状态,这就有可能导致Timer 中的定时任务无法正常运行。而Alarm 机制则不存在这种情况,它具有唤醒CPU 的功能,即可以保证每次需要执行定时任务的时候CPU 都能正常工作。
Alarm机制
AlarmManager manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
//调用AlarmManager的set()方法就可以设置一个定时任务
long triggerAtTime =SystemClock.elapsedRealtime()+10*1000;
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent);
参数:
第一个参数:用于指定AlarmManager的工作类型,有四种值可选,分别是ELAPSED_REALTIME、ELAPSED_REALTIME_WAKEUP、RTC、RTC_WAKEUP。
ELAPSED_REALTIME 表示让定时任务的触发时间从系统开机开始算起,但不会唤醒CPU。
ELAPSED_REALTIME_WAKEUP 同样表示让定时任务的触发时间从系统开机开始算起,但会唤醒CPU。
RTC 表示让定时任务的触发时间从1970 年1月1 日0 点开始算起,但不会唤醒CPU。
RTC_WAKEUP 同样表示让定时任务的触发时间从1970 年1 月1 日0 点开始算起,但会唤醒CPU。
使用SystemClock.elapsedRealtime()方法可以获取到系统开机至今所经历时间的毫秒数,使用System.currentTimeMillis()方法可以获取到1970 年1 月1 日0 点至今所经历时间的毫秒数。
第二个参数:
就是定时任务触发的时间,以毫秒为单位
第三个参数:
PendingIntent,这里我们一般会调用getBroadcast()方法来获取一个能够进行广播的PendingIntent。这样当定时任务被触发的时候,广播接收器的onReceive()方法就可以得到执行。
public class LongRunningService extends Service{
public IBinder onBind(Intent intent){
return null;
}
public int onStartCommand(Intent intent,int flags,int startId){
new Thread(){
public void run(){
Log.e("LongRunningService","executed at"+new Date().toString());
}
}.start();
AlarmManager manager=(AlarmManager)getSystemService(ALARM_SERVICE);
int anHour=60*60*1000;
long triggerAtTime = SystemClock.elapsedRealtime()+anHour;
Intent intent=new Intent(this,AlarmReceiver.class);
PendingIntent pi=PendingIntent.getBroadcast(this,0,i,0);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pi);
return super.onStartCommand(intent,flags,startId);
}
}
public class AlarmReceiver extends BroadcastReceiver{
public void onReceive(Context context,Intent intent){
Intent i=new Intent(context,LongRunningService.class);
context.startService(i);
}
}
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this,LongRunningService.class);
startService(intent);
}
}