一、Service,服务是没有界面而在后台长期运行的程序,可以看做是后台的Activity。
1、在Android中按返回键退出一个应用并不会(内存充足时)直接销毁一个进程,所以其中的子线程也可以在后台运行,这种线程和服务的区别是优先级不同,如果内存不足,被销毁的顺序不同。而且即使服务所在进程被销毁,如果内存充足会自动重新运行。
作为Android四大组件之一,Service只要继承Service类,并在清单文件中配置即可使用。
!!!2、有两种方式开启Service,①使用startService();②使用bindService()。两者的区别在于
①前者创建一个服务之后,就与这个服务没有关联了,因为没有方法可以在获取这个服务的实例了。但是进行联系还需要复杂的操作,并且还要区分进程内绑定本地服务和进程外绑定远程服务
②通过绑定开启的service,一旦调用这个服务的activity被销毁则这个服务也被销毁。即服务不能长期存在。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import android.app.Activity; 2 import android.content.ComponentName; 3 import android.content.Intent; 4 import android.content.ServiceConnection; 5 import android.os.Bundle; 6 import android.os.IBinder; 7 import android.view.View; 8 9 public class MainActivity extends Activity { 10 private MyConn conn ; 11 private IMiddlePerson mp; 12 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 super.onCreate(savedInstanceState); 16 setContentView(R.layout.activity_main); 17 } 18 19 @Override 20 protected void onDestroy() { 21 System.out.println("啊啊啊,我是activity,我挂了"); 22 super.onDestroy(); 23 } 24 25 //绑定服务 26 public void bind(View view){ 27 //3.activity采用绑定的方式去开启服务。 28 //激活一个服务的意图 29 Intent intent = new Intent(this,MyService.class); 30 //保持联系的连接对象 31 conn = new MyConn(); 32 bindService(intent, conn, BIND_AUTO_CREATE); 33 34 } 35 //解除绑定服务 36 public void unbind(View view){ 37 unbindService(conn); 38 } 39 40 private class MyConn implements ServiceConnection{ 41 //4. 当服务被连接的时候调用,服务被成功绑定的时候调用。IBinder就是绑定的Service的onBind()的返回值, 42 //!!!这就是联系所在 43 @Override 44 public void onServiceConnected(ComponentName name, IBinder service) { 45 System.out.println("在activity里面成功得到了中间人"); 46 mp = (IMiddlePerson) service; 47 } 48 //当服务失去连接的时候调用(一般进程挂了,服务被异常杀死) 49 @Override 50 public void onServiceDisconnected(ComponentName name) { 51 52 } 53 } 54 55 //调用服务里面的方法。 56 public void call(View view){ 57 //5.通过中间人调用服务里面的方法。 58 mp.callMethodInService(55); 59 } 60 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import android.app.Service; 2 import android.content.Intent; 3 import android.os.Binder; 4 import android.os.IBinder; 5 import android.widget.Toast; 6 7 public class MyService extends Service { 8 9 //2.实现服务成功绑定的代码 ,返回一个中间人(一个符合IBinder要求的类,这里就是下面的内部类)。 10 @Override 11 public IBinder onBind(Intent arg0) { 12 System.out.println("服务被成功绑定了。。。。"); 13 return new MiddlePerson(); 14 } 15 16 @Override 17 public boolean onUnbind(Intent intent) { 18 System.out.println("onunbind"); 19 return super.onUnbind(intent); 20 } 21 22 @Override 23 public void onCreate() { 24 System.out.println("oncreate"); 25 super.onCreate(); 26 } 27 28 @Override 29 public int onStartCommand(Intent intent, int flags, int startId) { 30 System.out.println("onstartcommand"); 31 return super.onStartCommand(intent, flags, startId); 32 } 33 34 @Override 35 public void onDestroy() { 36 System.out.println("ondestory"); 37 super.onDestroy(); 38 } 39 40 /** 41 * 这是服务里面的一个方法 42 */ 43 public void methodInService(){ 44 Toast.makeText(this, "哈哈,服务给你办好了暂住证。", 0).show(); 45 } 46 47 //1.第一步服务要暴露方法 必须要有一个中间人 48 private class MiddlePerson extends Binder implements IMiddlePerson{ 49 /** 50 * 代办暂住证 51 * @param money 给钱 50块钱以上才给办。 52 */ 53 public void callMethodInService(int money){ 54 if(money>=50){ 55 methodInService(); 56 }else{ 57 Toast.makeText(getApplicationContext(), "多准备点钱。", 0).show(); 58 } 59 } 60 /** 61 * 陪领导打麻将 62 */ 63 public void playMajiang(){ 64 System.out.println("陪领导打麻将。"); 65 } 66 } 67 }
注意:上面的service方的中间人内部类是私有的,所以向外传递时不能直接将其抛出,所以在这里使用了一个接口作为中转,将需要暴露的方法通过接口暴露。
为了解决上面两种开启服务的缺点,可以使用混合的启动方式,即先startService()然后bindService(),此时关闭同样需要混合关闭。
参考:http://blog.csdn.net/ryantang03/article/details/7770939。
3、Service和Activity的异同点:
Service虽然叫服务,运行在后台,但是并不是新开了一个线程,而是依然运行在Activity的线程中,即UI线程中,所以如果在Service中执行耗时的操作,也会出现ANR异常。所以,Service和Activity一样,需要在子线程中进行耗时操作。
那Service和Activity的区别在哪里?为什么需要有Service呢?
这是因为Activity退出之后,其创建的子线程都会销毁,而Service可以继续运行在后台,而且通过绑定,当Activity再次启动时,可以获取一个Binder实例来获取后台运行数据。
4、其他类型的Service:
①前台Service:Service通常运行在后台,但是其优先级较低,在系统内存不足时可能会被回收。这时可以考虑使用前台Service来确保Service不会由于内存不足而回收。前台Service并没有什么特别的,只是在启动时需要创建一个notification在通知栏上进行显示。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 Notification.Builder localBuilder = new Notification.Builder(this); 2 localBuilder.setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0)); 3 localBuilder.setAutoCancel(false); 4 localBuilder.setSmallIcon(R.drawable.pi); 5 localBuilder.setTicker("Foreground Service Start"); 6 localBuilder.setContentTitle("Socket服务端"); 7 localBuilder.setContentText("正在运行..."); 8 9 //调用这一句开启前台服务 10 startForeground(1, localBuilder.getNotification());
②IntentServvice:自动创建新消息线程HandlerThread的Service,将消息需要处理的数据通过Intent进行传递。具体的处理逻辑写在onHandleIntent()中,这是对外的接口。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class TestService3 extends IntentService { 2 private final String TAG = "hehe"; 3 //必须实现父类的构造方法 4 public TestService3() 5 { 6 super("TestService3"); 7 } 8 9 //必须重写的核心方法 10 @Override 11 protected void onHandleIntent(Intent intent) { 12 //Intent是从Activity发过来的,携带识别参数,根据参数不同执行不同的任务 13 String action = intent.getExtras().getString("param"); 14 if(action.equals("s1"))Log.i(TAG,"启动service1"); 15 else if(action.equals("s2"))Log.i(TAG,"启动service2"); 16 else if(action.equals("s3"))Log.i(TAG,"启动service3"); 17 18 //让服务休眠2秒 19 try{ 20 Thread.sleep(2000); 21 }catch(InterruptedException e){e.printStackTrace();} 22 } 23 24 //重写其他方法,用于查看方法的调用顺序 25 @Override 26 public IBinder onBind(Intent intent) { 27 Log.i(TAG,"onBind"); 28 return super.onBind(intent); 29 } 30 31 @Override 32 public void onCreate() { 33 Log.i(TAG,"onCreate"); 34 super.onCreate(); 35 } 36 37 @Override 38 public int onStartCommand(Intent intent, int flags, int startId) { 39 Log.i(TAG,"onStartCommand"); 40 return super.onStartCommand(intent, flags, startId); 41 } 42 43 44 @Override 45 public void setIntentRedelivery(boolean enabled) { 46 super.setIntentRedelivery(enabled); 47 Log.i(TAG,"setIntentRedelivery"); 48 } 49 50 @Override 51 public void onDestroy() { 52 Log.i(TAG,"onDestroy"); 53 super.onDestroy(); 54 } 55 56 }
这是android自带的一种对原始Service进行封装的类,直接使用的Service的优点是:
a、创建独立线程,可以直接安全的进行操作,而无需创建线程。线程使用的是HandlerThread,针对HandlerThread,IntentServvice内部实现了一个ServiceHandler类继承自Handler,将消息源与生成的子线程进行绑定。对此具体介绍见http://www.cnblogs.com/songfeilong2325/p/5412424.html。
b、在该消息处理完成之后,自动调用Service的stopSelf()将该Service进行销毁。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private final class ServiceHandler extends Handler { 2 public ServiceHandler(Looper looper) { 3 super(looper); 4 } 5 6 @Override 7 public void handleMessage(Message msg) { 8 //调用这个方法处理消息,!!!所以这个方法是必须要继承实现 9 onHandleIntent((Intent)msg.obj); 10 //结束Service 11 stopSelf(msg.arg1); 12 } 13 }
③远程Service:就是在清单文件中<service>的android:process=":remote"进行设置,此时开启服务时就是在一个新的进程中。而此时使用start方式开启Service和原来一样,但是bind开启服务进行交互的方式需要改变,需要使用android的跨进程通讯机制。
a、使用Messenger:这是和Handler不同的进程间消息机制。同样使用Handler进行消息处理,但是使用Messenger来发送消息和获取Binder对象。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class MessengerService extends Service { 2 /** Command to the service to display a message */ 3 static final int MSG_SAY_HELLO = 1; 4 5 /** 6 * 通过handler处理消息 7 */ 8 class IncomingHandler extends Handler { 9 @Override 10 public void handleMessage(Message msg) { 11 switch (msg.what) { 12 case MSG_SAY_HELLO: 13 Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show(); 14 break; 15 default: 16 super.handleMessage(msg); 17 } 18 } 19 } 20 21 /** 22 *!!! 一个Messenger 对象,在Service端就是用于在onBind()中获取BInder对象 23 */ 24 final Messenger mMessenger = new Messenger(new IncomingHandler()); 25 26 @Override 27 public IBinder onBind(Intent intent) { 28 Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); 29 return mMessenger.getBinder(); 30 } 31 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class ActivityMessenger extends Activity { 2 /** Messenger for communicating with the service. */ 3 Messenger mService = null; 4 5 boolean mBound; 6 7 /** 8 * 创建一个连接对象,用于和Service组件进行连接 9 */ 10 private ServiceConnection mConnection = new ServiceConnection() { 11 public void onServiceConnected(ComponentName className, IBinder service) { 12 // !!!在这里初始化Messenger对象,用于下面发送消息 13 mService = new Messenger(service); 14 mBound = true; 15 } 16 17 public void onServiceDisconnected(ComponentName className) { 18 mService = null; 19 mBound = false; 20 } 21 }; 22 23 public void sayHello(View v) { 24 if (!mBound) return; 25 // Create and send a message to the service, using a supported 'what' value 26 Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); 27 try { 28 mService.send(msg); 29 } catch (RemoteException e) { 30 e.printStackTrace(); 31 } 32 } 33 34 @Override 35 protected void onCreate(Bundle savedInstanceState) { 36 super.onCreate(savedInstanceState); 37 setContentView(R.layout.main); 38 } 39 40 @Override 41 protected void onStart() { 42 super.onStart(); 43 // Bind to the service 44 bindService(new Intent(this, MessengerService.class), mConnection, 45 Context.BIND_AUTO_CREATE); 46 } 47 48 @Override 49 protected void onStop() { 50 super.onStop(); 51 // Unbind from the service 52 if (mBound) { 53 unbindService(mConnection); 54 mBound = false; 55 } 56 } 57 }
b、使用AIDL方式:(Android Interface Definition Language)是Android接口定义语言的意思。使用了一个接口文件(类似.java文件但是以.aidl结尾)。关于AIDL的具体使用参考http://blog.csdn.net/lmj623565791/article/details/38461079,http://blog.csdn.net/guolin_blog/article/details/9797169
注意:使用Messenger的内部也是用AIDL的方式实现的,只是进行了简化。参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0723/3216.html
5、注意点:
①如果一个service没有绑定(bind),直接调用unBind()会报错,java.lang.IllegalArgumentException: Service not registered。
②绑定方式需要的ServiceConnection类需要实现两个,一个在Activity和service建立联系时调用,另一个在解除关联时调用。但是后者onServiceDisconnected方法在正常情况下是不被调用的,它的调用时机是当Service服务被异外销毁时,例如内存的资源不足时这个方法才被自动调用。
③为了确保安全,Service应该使用显示Intent来启动,而且只应该被自身应用启动,通过添加 a
ndroid:exported
属性并将其设置为 "false"
,确保服务仅适用于您的应用。
6、深入探究---Binder机制