Android从一开始就支持后台功能,这样就是的应用程序及时在关闭的情况下依然可以在后台继续运行。作为Android四大组件之一,它也很重要。
- Service是什么
- Service基本用法
- Service与Activity通信
- Service生命周期
- 更多
- 前台Service
- IntentService
Service是什么
Service是Android实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要长期运行的任务。服务不依赖于任何界面。
Service的运行不依赖于任何用户界面,因此即便程序被切换到后台或者用户打开了另一个应用程序,Service仍能够保持正常运行。但当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。
Service并不会自动开启子线程,所有的代码都是默认运行在主线程当中的。也就是说我们需要在服务的内部手动创建子线程并执行具体的逻辑。否则有可能出现主线程被阻塞(ANR)的情况。关于多线程,请看这篇文章
Service基本用法(定义、启动和停止服务)
定义一个Service
- 右击自己的项目,new==>Service==>Service,勾选Exported、Enabled(默认是勾选着的,Exported表示是否允许除当前程序之外的其他程序访问这个服务;Enabled表示是否启动这个服务)
- 重写oncreate、onStartCommand、onDestroy方法,并在方法中添加打印日志语句
- 在MainActivity中添加两个按钮用于启动和溶质服务,编写单击事件
代码 Myservice.java
public class MyService extends Service {
private static final String TAG = "MyService";
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
Log.d(TAG, "onCreate excuted");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand excuted");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy excuted");
super.onDestroy();
}
}
代码 MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button start,stop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start = (Button) findViewById(R.id.start);
stop = (Button) findViewById(R.id.stop);
start.setOnClickListener(this);
stop.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.stop:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
}
}
}
以上,我们定义了一个服务,并借助Intent来启动和停止服务(如果不点击停止服务按钮,服务就会一直运行。可以在MyService里任意一个位置调用stopSelf()方法来停止这个服务)。
运行程序,点击启动服务,分别打印了日志【onCreate excuted】【onStartCommand excuted】点击停止服务,打印了日志【onDestroy excuted】
其中onCreate方法只在服务第一次创建的时候调用,onStartCommand方法在每次启动服务的时候都会调用。(即启动服务时两个方法会执行,之后如果多次点击启动服务,只有onStartCommand方法得到执行了)
Service与Activity通信
- 重写MyService方法,新建DownLoadBinder匿名类,继承自Binder,虚拟定义两个方法。
- 重写MainActivity,创建ServiceConnection匿名内部类,重写onServiceConnected和onServiceDisconnected方法,这两个方法在绑定服务和解除绑定服务的时候调用。在绑定服务时创建DownLoadBinder实例,分别调用实例的两个方法
- 添加两个按钮用于绑定服务和活动:构建Intent对象,调用bindService方法将MainActivity和MyService绑定(其中方法的三个参数分别是Intent对象、创建的ServiceConnection实例、标志位BIND_AUTO_CREATE(表示活动和服务进行绑定后自动创建服务,会使服务的oncreate方法执行,onStartCommand方法不会执行))
public class MyService extends Service {
private static final String TAG = "MyService";
private DownLoadBinder mBinder = new DownLoadBinder();
class DownLoadBinder extends Binder {
public void startDownload() {
Log.d(TAG, "startDownload excuted");
}
public int getProgress() {
Log.d(TAG, "getProgress excuted");
return 0;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
...
}
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button start, stop, bind, unbind;
private MyService.DownLoadBinder downLoadBinder;
private ServiceConnection connection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downLoadBinder = (MyService.DownLoadBinder) service;
downLoadBinder.startDownload();
downLoadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start = (Button) findViewById(R.id.start);
stop = (Button) findViewById(R.id.stop);
bind = (Button) findViewById(R.id.bind);
unbind = (Button) findViewById(R.id.unbind);
bind.setOnClickListener(this);
unbind.setOnClickListener(this);
start.setOnClickListener(this);
stop.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.stop:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
case R.id.bind:
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
break;
case R.id.unbind:
unbindService(connection);
break;
}
}
}
运行程序,只点击绑定服务按钮,打印了日志【onCreate excuted】【startDownload excuted】【getProgress excuted】说明我们已经在活动里成功调用了服务里提供的方法了。
任何一个服务在程序方位内都是通用的,MyService不仅可以和MainActivity绑定,还可以和其他的活动进行绑定,进而调用活动的方法。
Service生命周期
官方提供的生命周期图如下:
其中涉及到五个方法
onCreate():服务第一次被创建时调用
onStartComand():服务启动时调用
onDestroy():服务停止时调用
onBind():服务被绑定时调用
onUnBind():服务被解绑时调用
说明
调用Context的startService()方法可以启动一个Service,并回调服务中的onStartCommand()。如果该服务之前还没创建,那么回调的顺序是onCreate()==>onStartCommand()。服务启动了之后会一直保持运行状态,直到stopService()或stopSelf()方法被调用,服务停止并回调onDestroy()。另外,无论调用多少次startService()方法,只需调用一次stopService()或stopSelf()方法,服务就会停止了。
其它组件调用Context的bindService()可以绑定一个Service,并回调服务中的onBind()方法。类似地,如果该服务之前还没创建,那么回调的顺序是onCreate()==>onBind()。之后,调用方可以获取到onBind()方法里返回的IBinder对象的实例,从而实现和服务进行通信。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态,直到调用了unbindService()方法服务会停止,回调顺序onUnBind()==>onDestroy()。
注意:当使用startService()启动Service之后,还可再使用bindService()绑定,只不过需要同时调用 stopService()和 unbindService()方法才能让服务销毁掉。
使用前台服务
服务一般都是在后台运行的,但是服务的系统优先级是比较低的,当系统内存不足时,就有可能回收正在后台运行的服务,如果希望服务可以一直保持运行的状态,不会由于系统内存不足而导致被回收,这时就可以考虑使用前台服务了。
前台服务与普通服务最大的区别就是它会有一个正在运行的图标在系统的状态栏显示,下拉状态栏可以看到更加详细的信息(类似于通知的效果)(使用前台服务或者为了防止服务被回收掉,或者由于特殊的需求,比如实时天气状况)
修改MyService里的onCreate方法,构建notification对象,调用startForeground方法来将通知显示出来。方法第一个参数是通知的id,第二个参数是notification对象。调用startForeground方法后就会让MyService成为一个前台服务,并在系统状态栏显示出来。
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate excuted");
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("contentTitle")
.setContentText("contentText")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pi)
.build();
startForeground(1, notification);
}
使用IntentService
服务中的代码是默认运行在主线程中的,如果直接在服务里去处理一些耗时的逻辑,就会很容易出现ANR(Application Not Responding)问题,所以就需要用到多线程编程的技术了,我们应该在服务的每个具体的方法开启一个子线程,然后去处理耗时的逻辑。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand excuted");
new Thread(new Runnable() {
@Override
public void run() {
//添加具体逻辑
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
但是服务一旦启动之后就会一直处于运行状态,必须调用stopService或stopSelf方法才能让服务停止下来。所以要想实现在执行完毕后自动停止的功能就可以这样写。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand excuted");
new Thread(new Runnable() {
@Override
public void run() {
//添加具体逻辑
stopSelf();
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
但有时我们可能会忘记开启线程,忘记调用stopSelf,为了可以简单的创建一个异步、自动停止的服务,Android专门提供了IntentService类。
新建MyIntentService类继承自IntentService类,
- 提供无参的构造函数,在内部调用父类的有参构造函数
- 在子类中实现onHandleIntent方法,这个方法可以处理一些具体的逻辑,而且不用担心ANR问题,打印日志,输出当前线程的id
- 重写onDestroy,添加打印日志,看他是否自动停止了。
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "onHandleIntent: thread id is:"+Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: executed");
}
}
添加相应的按钮单击事件,打印当前主线程的id,启动IntentService
case R.id.intentService:
Log.d("MainActivity", "thread id is "+Thread.currentThread().getId());
Intent intentServvice = new Intent(this, MyIntentService.class);
startService(intentServvice);
break;
运行,启动IntentService,查看打印的日志:【MainActivity: thread id is 1】【onHandleIntent: thread id is:3576】【onDestroy: executed】说明MyIntentService确实在开启线程,运行完毕后自动停止了。