Service作为Android四大组件之一,在每一个应用程序中都扮演着非常重要的角色。它主要用于在后台处理一些耗时的逻辑,或者去执行某些需要长期运行的任务。必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态。
Service的启动方法
一、通过startService()方法
1.创建一个MyService类来继承Service,并重写父类的onCreate()、onStartCommand()和onDestroy()方法。
public class Myservice extends Service {
public static final String TAG="Myservice";
public void onCreate(){
super.onCreate();
Log.i(TAG, "Service onCreate");
}
public int onStartCommand(Intent intent,int flags,int startId){
Log.i(TAG, "Service onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
public void onDestory(){
Log.i(TAG, "Service onDestory");
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
2.设置布局文件和MainActivity,如下图所示
MainActivity代码
布局文件代码
另外需要注意,项目中的每一个Service都必须在AndroidManifest.xml中注册才行,所以还需要编辑AndroidManifest.xml文件,代码如下所示:
当点击Start Service按钮时,可以看到打印的Log日志如下:
也就是说,当启动一个Service的时候,会调用该Service中的onCreate()和onStartCommand()方法。在onStartCommand()方法中写需要执行的功能代码。
如果当前Service已经被创建过了,不管怎样调用startService()方法,onCreate()方法都不会再执行。因此你可以再多点击几次Start Service按钮试一次,每次都只会有onStartCommand()方法中的打印日志。如下所示:
我们还可以到手机的应用程序管理界面来检查一下MyService是不是正在运行,如下图所示:
二.通过bindService()方法启动
1.创建一个BinderService类来继承Service,并创建一个MyBinder内部类来扩展Binder。
public class BinderService extends Service{
private static final String TAG="BinderService";
//创建一个Binder实例
private MyBinder binder=new MyBinder();
//创建一个类来扩展Binder类
public class MyBinder extends Binder{
//返回当前Service的方法
public BinderService getService(){
return BinderService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
//返回对象
return binder;
}
//定义自己需要实现的方法
public void Mymethod(){
Log.i(TAG, "BinderService is running");
Toast.makeText(this, "绑定Service成功", Toast.LENGTH_SHORT).show();
}
public void onCreate(){
super.onCreate();
Log.i(TAG, "Service onCreate");
}
public int onStartCommand(Intent intent,int flags,int starId){
Log.i(TAG, "Service onStartCommand");
return START_NOT_STICKY;
}
public void onDestroy(){
super.onDestroy();
Log.i(TAG, "Service onDestroy");
}
}
2.在java中的代码
public class MainActivity extends Activity implements OnClickListener{
private Button bindService;
private Button unbindService;
//设置一个标记检查是否绑定
private Boolean isConnected=false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService=(Button)findViewById(R.id.bindService);
unbindService=(Button)findViewById(R.id.unbindService);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bindService:
Intent intent=new Intent(this,BinderService.class);
bindService(intent, connection, BIND_AUTO_CREATE);
break;
case R.id.unbindService:
if(isConnected){
unbindService(connection);
}
default:
break;
}
}
//实现获得Binder实例方法
private ServiceConnection connection=new ServiceConnection(){
public void onServiceConnected(ComponentName name, IBinder service) {
//通过Binder对象获取service对象,注意与Binderservice中创建的对象不能同名。
MyBinder myBinder=(MyBinder)service;
//通过service对象来获取service中的服务和方法
BinderService bindservice=myBinder.getService();
bindservice.Mymethod();
isConnected=true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
isConnected=false;
}
};
}
由于Android 中的Service使用了onBind 的方法去绑定服务,返回一个Ibinder对象进行操作,而我们要获取具体的Service方法的内容的时候,我们需要Ibinder对象返回具体的Service对象才能操作,所以说具体的Service对象必须首先实现Binder对象,这个样子的话我们才能利用bindService的方法对Service进行绑定,获取Binder对象之后获取具体的Service对象,然后才获取Service中的方法等等。所以我们需要注意的是bindService的方式去绑定服务获取的必定是实现了Binder的对象,所以这是我们必须使用Binder的方式去获取Service的方式而不是直接使用Service的类,这个是Android内部实现所约束的。
三.Service与Thread的关系
一般来说service与activity在同一个线程里,即service线程与主线程的Id相同。如下实例所示:
MainActivity中的部分代码:
Myservice中部分代码:
public int onStartCommand(Intent intent,int flags,int startId){
Log.i(TAG, "Myservice线程Id:"+Thread.currentThread().getId());
return START_STICKY;
}
效果图:
但如果要用service处理比较耗时间的操作时,就必须创建新的线程,让它们在不同的线程里。提高用户体验。实例如下:
MainActivity中代码不变
Myservice中改变的代码:耗时的代码模拟文件上传
效果图:
一直处于僵持状态5秒,5秒后才能继续操作,这大大降低了用户体验。
解决这一问题,可以创建新线程来操作。
MainActivity中代码不变
Myservice中部分代码:
public int onStartCommand(Intent intent,int flags,int startId){
new MyThread().start(); //创建一个新线程
return START_STICKY;
}
//新建一个类来继承线程,然后实现新线程的方法
private class MyThread extends Thread{
public void run(){
try {
Log.i(TAG, "Myser线程Id:"+Thread.currentThread().getId());
Log.i(TAG, "文件上传。。。。");
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
效果图:
主线程与service线程不一样了,便于操作。用户操作时不会处于僵持状态。