Service2

Posted Andy__Wu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Service2相关的知识,希望对你有一定的参考价值。

在上一篇文章中,我们学习了android Service相关的许多重要内容,包括Service的基本用法、Service和Activity进行通信、Service的销毁方式、Service与Thread的关系、以及如何创建前台Service。以上所提到的这些知识点,基本上涵盖了大部分日常开发工作当中可能使用到的Service技术。不过关于Service其实还有一个更加高端的使用技巧没有介绍,即远程Service的用法。使用远程Service甚至可以实现Android跨进程通信的功能,下面就让我们具体地学习一下。


如果你还没有看过前面一篇文章,建议先去阅读一下 Android Service完全解析,关于服务你所需知道的一切(上) ,因为本篇文章中涉及到的代码是在上篇文章的基础上进行修改的。


在上篇文章中我们知道了,Service其实是运行在主线程里的,如果直接在Service中处理一些耗时的逻辑,就会导致程序ANR。


让我们来做个实验验证一下吧,修改上一篇文章中创建的ServiceTest项目,在MyService的onCreate()方法中让线程睡眠60秒,如下所示:

[java]  view plain  copy
  1. public class MyService extends Service   
  2.   
  3.     ......  
  4.   
  5.     @Override  
  6.     public void onCreate()   
  7.         super.onCreate();  
  8.         Log.d(TAG, "onCreate() executed");  
  9.         try   
  10.             Thread.sleep(60000);  
  11.          catch (InterruptedException e)   
  12.             e.printStackTrace();  
  13.           
  14.       
  15.       
  16.     ......  
  17.   
  18.   

重新运行后,点击一下Start Service按钮或Bind Service按钮,程序就会阻塞住并无法进行任何其它操作,过一段时间后就会弹出ANR的提示框,如下图所示。



之前我们提到过,应该在Service中开启线程去执行耗时任务,这样就可以有效地避免ANR的出现。


那么本篇文章的主题是介绍远程Service的用法,如果将MyService转换成一个远程Service,还会不会有ANR的情况呢?让我们来动手尝试一下吧。


将一个普通的Service转换成远程Service其实非常简单,只需要在注册Service的时候将它的android:process属性指定成:remote就可以了,代码如下所示:

[html]  view plain  copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.example.servicetest"  
  4.     android:versionCode="1"  
  5.     android:versionName="1.0" >  
  6.   
  7.     ......  
  8.       
  9.     <service  
  10.         android:name="com.example.servicetest.MyService"  
  11.         android:process=":remote" >  
  12.     </service>  
  13.   
  14. </manifest>  

现在重新运行程序,并点击一下Start Service按钮,你会看到控制台立刻打印了onCreate() executed的信息,而且主界面并没有阻塞住,也不会出现ANR。大概过了一分钟后,又会看到onStartCommand() executed打印了出来。


为什么将MyService转换成远程Service后就不会导致程序ANR了呢?这是由于,使用了远程Service后,MyService已经在另外一个进程当中运行了,所以只会阻塞该进程中的主线程,并不会影响到当前的应用程序。


为了证实一下MyService现在确实已经运行在另外一个进程当中了,我们分别在MainActivity的onCreate()方法和MyService的onCreate()方法里加入一行日志,打印出各自所在的进程id,如下所示:


[java]  view plain  copy
  1. Log.d("TAG""process id is " + Process.myPid());  

再次重新运行程序,然后点击一下Start Service按钮,打印结果如下图所示:



可以看到,不仅仅是进程id不同了,就连应用程序包名也不一样了,MyService中打印的那条日志,包名后面还跟上了:remote标识。


那既然远程Service这么好用,干脆以后我们把所有的Service都转换成远程Service吧,还省得再开启线程了。其实不然,远程Service非但不好用,甚至可以称得上是较为难用。一般情况下如果可以不使用远程Service,就尽量不要使用它。


下面就来看一下它的弊端吧,首先将MyService的onCreate()方法中让线程睡眠的代码去除掉,然后重新运行程序,并点击一下Bind Service按钮,你会发现程序崩溃了!为什么点击Start Service按钮程序就不会崩溃,而点击Bind Service按钮就会崩溃呢?这是由于在Bind Service按钮的点击事件里面我们会让MainActivity和MyService建立关联,但是目前MyService已经是一个远程Service了,Activity和Service运行在两个不同的进程当中,这时就不能再使用传统的建立关联的方式,程序也就崩溃了。


那么如何才能让Activity与一个远程Service建立关联呢?这就要使用AIDL来进行跨进程通信了(IPC)。


AIDL(Android Interface Definition Language)是Android接口定义语言的意思,它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。


下面我们就来一步步地看一下AIDL的用法到底是怎样的。首先需要新建一个AIDL文件,在这个文件中定义好Activity需要与Service进行通信的方法。新建MyAIDLService.aidl文件,代码如下所示:

[java]  view plain  copy
  1. package com.example.servicetest;  
  2. interface MyAIDLService   
  3.     int plus(int a, int b);  
  4.     String toUpperCase(String str);  
  5.   

点击保存之后,gen目录下就会生成一个对应的Java文件,如下图所示:


 


然后修改MyService中的代码,在里面实现我们刚刚定义好的MyAIDLService接口,如下所示:

[java]  view plain  copy
  1. public class MyService extends Service   
  2.   
  3.     ......  
  4.   
  5.     @Override  
  6.     public IBinder onBind(Intent intent)   
  7.         return mBinder;  
  8.       
  9.   
  10.     MyAIDLService.Stub mBinder = new Stub()   
  11.   
  12.         @Override  
  13.         public String toUpperCase(String str) throws RemoteException   
  14.             if (str != null)   
  15.                 return str.toUpperCase();  
  16.               
  17.             return null;  
  18.           
  19.   
  20.         @Override  
  21.         public int plus(int a, int b) throws RemoteException   
  22.             return a + b;  
  23.           
  24.     ;  
  25.   
  26.   

这里先是对MyAIDLService.Stub进行了实现,重写里了toUpperCase()和plus()这两个方法。这两个方法的作用分别是将一个字符串全部转换成大写格式,以及将两个传入的整数进行相加。然后在onBind()方法中将MyAIDLService.Stub的实现返回。这里为什么可以这样写呢?因为Stub其实就是Binder的子类,所以在onBind()方法中可以直接返回Stub的实现。


接下来修改MainActivity中的代码,如下所示:

[java]  view plain  copy
  1. public class MainActivity extends Activity implements OnClickListener   
  2.   
  3.     private Button startService;  
  4.   
  5.     private Button stopService;  
  6.   
  7.     private Button bindService;  
  8.   
  9.     private以上是关于Service2的主要内容,如果未能解决你的问题,请参考以下文章

    kubernetes组件_Service_普通Service和无头Service

    Dubbo:Dubbo服务发现

    前两篇转载别人的精彩文章,自己也总结一下python split的用法吧!

    手写dubbo-7手撸了自定义协议远程调用服务动态扩容,梳理一下吧!

    那些你不知道的HTML知识,快来学习一下吧

    面试官:小伙子,你给我讲一下java类加载机制和内存模型吧 发布文章 ### 类加载机制 虚拟