Service是Android中长期在后台运行的没有界面的组件,使用服务的优势在于:能够提高进程的优先级,系统不容易回收掉进程,即便回收了,内存充足的时候,会把进程重新创建。
1.服务的简单使用示例:
1.1.定义一个服务:
定义一个服务的方式是定义一个类继承自Service:
1 public class MyService extends Service { 2 3 @Override 4 5 public IBinder onBind(Intent intent) { 6 7 // TODO Auto-generated method stub 8 9 return null; 10 11 } 12 13 }
注意到onBind()方法是个抽象方法,因而必须重写。
1.2.注册服务:
四大组件之一的Service,在使用之前也必须在清单文件中注册。
<service android:name="cn.csc.service.MyService"></service>
1.3.在MainActivity中启动这个服务:
启动服务的方式很简单,可以如同启动Activity一样,通过Intent对象进行服务的启动,启动服务的方法是startService(Intent intent),下面的代码在MainActivity的onCreate()方法中启动服务:
1 protected void onCreate(Bundle savedInstanceState) { 2 3 super.onCreate(savedInstanceState); 4 5 setContentView(R.layout.activity_main); 6 7 Intent intent = new Intent(this, MyService.class); 8 9 startService(intent); 10 11 }
1.4.运行效果:
2.观察服务的生命周期:
2.1修改MyService及MainActivity代码,重写生命周期回调的各个方法,输出标识信息:
MainActivity.java:
1 public class MainActivity extends ActionBarActivity { 2 3 @Override 4 5 protected void onCreate(Bundle savedInstanceState) { 6 7 super.onCreate(savedInstanceState); 8 9 setContentView(R.layout.activity_main); 10 11 Log.i("MainActivity","onCreate()"); 12 13 Intent intent = new Intent(this, MyService.class); 14 15 startService(intent); 16 17 } 18 19 @Override 20 21 protected void onStop() { 22 23 // TODO Auto-generated method stub 24 25 super.onStop(); 26 27 Log.i("MainActivity","onStop()"); 28 29 } 30 31 @Override 32 33 protected void onDestroy() { 34 35 // TODO Auto-generated method stub 36 37 super.onDestroy(); 38 39 Log.i("MainActivity","onDestroy()"); 40 41 } 42 43 @Override 44 45 protected void onPause() { 46 47 // TODO Auto-generated method stub 48 49 super.onPause(); 50 51 Log.i("MainActivity","onPause()"); 52 53 } 54 55 @Override 56 57 protected void onResume() { 58 59 // TODO Auto-generated method stub 60 61 super.onResume(); 62 63 Log.i("MainActivity","onResume()"); 64 65 } 66 67 @Override 68 69 protected void onStart() { 70 71 // TODO Auto-generated method stub 72 73 super.onStart(); 74 75 Log.i("MainActivity","onStart()"); 76 77 } 78 79 }
MyService.java:
1 public class MyService extends Service { 2 3 @Override 4 5 public IBinder onBind(Intent intent) { 6 7 // TODO Auto-generated method stub 8 9 Log.i("MyService","onBind()"); 10 11 return null; 12 13 } 14 15 @Override 16 17 public void onCreate() { 18 19 // TODO Auto-generated method stub 20 21 super.onCreate(); 22 23 Log.i("MyService","onCreate()"); 24 25 } 26 27 @Override 28 29 public int onStartCommand(Intent intent, int flags, int startId) { 30 31 // TODO Auto-generated method stub 32 33 Log.i("MyService","onStartCommand()"); 34 35 return super.onStartCommand(intent, flags, startId); 36 37 } 38 39 @Override 40 41 public void onDestroy() { 42 43 // TODO Auto-generated method stub 44 45 super.onDestroy(); 46 47 Log.i("MyService","onDestroy()"); 48 49 } 50 51 @Override 52 53 public boolean onUnbind(Intent intent) { 54 55 // TODO Auto-generated method stub 56 57 Log.i("MyService","onUnbind()"); 58 59 return super.onUnbind(intent); 60 61 } 62 63 }
2.2部署安装应用:
运行结果:
2.3退出当前应用:
查看运行中的进程:
服务仍然在运行。
点击Stop之后:
服务才停止运行。
总结:
采用startService()方式开启服务后,先后调用了服务的onCrate()和onStartCommand()方法,Activity被销毁之后,服务仍然继续运行,只有当手动停止掉服务时,才会调用onDestory()方法。
可见,start方式开启服务时, 一旦服务开启跟调用者(开启者)就没有任何关系了。
开启者退出后,服务仍然在后台长期的运行,开启者与服务没有任何关联,因而不能调用服务里面的方法。
2.4在MainActivity中调用bindService()绑定服务:
1 protected void onCreate(Bundle savedInstanceState) { 2 3 super.onCreate(savedInstanceState); 4 5 setContentView(R.layout.activity_main); 6 7 Log.i("MainActivity","onCreate()"); 8 9 Intent intent = new Intent(this, MyService.class); 10 11 bindService(intent, new MyConn(), BIND_AUTO_CREATE); 12 13 }
查看运行结果:
退出应用:
总结:
采用绑定的方式开启服务,会先后调用:onCreate()、onBind(),Activity销毁后,Service紧跟着调用onunbind()、onDestory()也被销毁。
这种方式的服务跟随应用的结束而结束,不会长期驻留在后台,但是,这种方式下,Activity与Service产生了关联,因而可以调用Service提供的方法。那么在Activity中如何调用Service中的方法呢?
3.Activity调用本地服务方法:
主要步骤:
1)在本地服务中添加需要被Activity调用的方法
2)定义一个接口,约定Activity通过该接口的实例调用本地服务的方法,该接口的实例由本地服务的onBind()方法传递给Activity
3) 在服务的内部创建一个内部类实现约定的接口,并在onBind()方法中返回该内部类的实例,由于onBind()方法返回的是IBinder类型的实例,所以该内部类还需要继承一个Ibinder接口的实现类,一般继承Binder类
4)在activity 绑定服务:bindService();
5)在服务成功绑定的时候 会执行一个方法 onServiceConnected 传递过来一个 IBinder对象
5)将传回的IBinder对象强制类型转化成约定接口的实例,从而调用接口里面的方法。
具体编码步骤:
1)修改MyService代码,添加一个供Activity调用的方法:
1 public void serviceMethod(){ 2 3 Toast.makeText(getApplicationContext(), "本地服务方法被调用", Toast.LENGTH_SHORT).show(); 4 5 }
2)定义一个双方约定的接口,用于调用前面添加的serviceMethod()方法:
1 public interface IMiddlePerson { 2 3 public void callServiceMethod(); 4 5 }
3)在MyService中定义一个内部类继承自Binder并实现上面的约定接口:
1 private class MiddlePerson extends Binder implements IMiddlePerson{ 2 3 4 5 @Override 6 7 public void callServiceMethod() { 8 9 // TODO Auto-generated method stub 10 11 serviceMethod(); 12 13 } 14 15 16 17 }
4)修改MyService的onBind()方法,返回上面定义的内部类的实例:
1 public IBinder onBind(Intent intent) { 2 3 // TODO Auto-generated method stub 4 5 Log.i("MyService","onBind()"); 6 7 return new MiddlePerson(); 8 9 }
5)在MainActivity中添加一个存放约定接口的引用的字段:
private IMiddlePerson imp;
6)修改MainActivity的布局文件,添加一个按钮:
1 <Button 2 3 android:layout_width="wrap_content" 4 5 android:layout_height="wrap_content" 6 7 android:onClick="call" 8 9 android:layout_gravity="center_horizontal" 10 11 android:text="@string/call_native_service" />
在MainActivity中添加public void call(View view)方法,用于按钮的点击回调:
1 public void call(View view){ 2 3 imp.callServiceMethod(); 4 5 }
7)修改MyConn的onServiceConnected方法,将IBinder类型的参数赋值给imp:
1 public void onServiceConnected(ComponentName name, IBinder service) { 2 3 // TODO Auto-generated method stub 4 5 Log.i("MainActivity","服务绑定了"); 6 7 imp = (IMiddlePerson) service; 8 9 }
8)查看运行结果:
注: 如果又想服务能长期在后台运行,又想能够调用服务中的方法,则应如下顺序处理:
1)start方式开启服务(保证服务长期后台运行)
2)bind方式绑定服务(保证调用服务的方法)
在需要停止服务时,则需要按如下顺序:
3)unbind解除绑定服务
4)stopService停止服务。
这里,就不在测试了。
4.Activity调用远程服务的方法:
这里简单区分下本地服务和远程服务:
远程服务:调用者和服务在不同的工程代码里面。
本地服务:调用者和服务在同一个工程代码里面。
如果像调用同一个工程中的本地服务方法一样,绑定一个远程服务,直接会报错。这是因为绑定远程服务的方法与绑定本地服务的方法有所不同:
Android定义了aidl:android interface definition language 安卓接口定义语言来实现远程服务的绑定。
与绑定本地服务的主要不同在于:
约定的接口的定义不能带有任何访问修饰符,而且后缀名要改为.aidl,之后会自动生成一个名为Stub的内部类,服务中定义的内部类继承该内部类即可。
具体编码步骤:
4.1定义能远程绑定的服务:
1)定义IMiddlePeson.aidl:
1 package cn.csc.service; 2 3 4 5 interface IMiddlePerson { 6 7 void callServiceMethod(); 8 9 }
将在gen目录下自动生成:
打开自动生成的IMiddlePerson.java:
可以看到自动生成了Stub内部抽象类
2)在服务中添加要被远程调用的方法:
1 public void serviceMethod(){ 2 3 Log.i("MyService","远程服务方法被调用了。。。"); 4 5 }
3)在服务中定义内部类继承IMiddlePerson.Stub:
1 private class MiddlePerson extends IMiddlePerson.Stub{ 2 3 4 5 @Override 6 7 public void callServiceMethod() { 8 9 // TODO Auto-generated method stub 10 11 serviceMethod(); 12 13 } 14 15 16 17 }
4)由于要被远程调用,所以不能使用显示意图了,需要在清单文件中注册服务时添加<intent-filter>生命能响应的动作:
1 <service android:name="cn.csc.service.MyService"> 2 3 <intent-filter > 4 5 <action android:name="cn.csc.service.MyService"/> 6 7 </intent-filter> 8 9 </service>
至此,一个可以被远程绑定的服务就完成了,先把它部署到模拟器上。
4.2在另一个项目中绑定该远程服务:
1)将约定的接口IMiddlePerson.aidl文件连同所在包一同复制到该项目的src目录中:
可以看到,同样自动生成了IMiddlePerson.java文件
2)定义存放接口引用的字段:
private IMiddlePerson imp;
3)使用隐式意图绑定远程服务:
1 Intent intent = new Intent(); 2 3 intent.setAction("cn.csc.service.MyService"); 4 5 bindService(intent, new MyConn(), BIND_AUTO_CREATE); 6 7 4)修改MyConn()中的onServiceConnected()方法: 8 9 public void onServiceConnected(ComponentName name, IBinder service) { 10 11 // TODO Auto-generated method stub 12 13 Log.i("MainActivity","服务绑定了"); 14 15 imp = IMiddlePerson.Stub.asInterface(service); 16 17 }
调用远程服务的应用端也搞定了,然后部署,查看运行结果:
以上,就是今天学到的关于Service的一点知识。