转自:http://blog.csdn.net/guolin_blog/article/details/9797169

转载请注册出处:http://blog.csdn.net/guolin_blog/article/details/9797169

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

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

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

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

    public class MyService extends Service {  ......  @Override  public void onCreate() {  super.onCreate();  Log.d(TAG, "onCreate() executed");  try {  Thread.sleep(60000);  } catch (InterruptedException e) {  e.printStackTrace();  }  }  ......  }  

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

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

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

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

    <?xml version="1.0" encoding="utf-8"?>  <manifest xmlns:android="http://schemas.android.com/apk/res/android"  package="com.example.servicetest"  android:versionCode="1"  android:versionName="1.0" >  ......  <service  android:name="com.example.servicetest.MyService"  android:process=":remote" >  </service>  </manifest>  

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

为什么将MyService转换成远程Service后就不会导致程序ANR了呢?这是由于,使用了远程Service后,MyService已经在另外一个进程当中运行了,所以只会阻塞该进程中的主线程,并不会影响到当前的应用程序。
为了证实一下MyService现在确实已经运行在另外一个进程当中了,我们分别在MainActivity的onCreate()方法和MyService的onCreate()方法里加入一行日志,打印出各自所在的进程id,如下所示:

    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文件,代码如下所示:

    package com.example.servicetest;  interface MyAIDLService {  int plus(int a, int b);  String toUpperCase(String str);  }  

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

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

    public class MyService extends Service {  ......  @Override  public IBinder onBind(Intent intent) {  return mBinder;  }  MyAIDLService.Stub mBinder = new Stub() {  @Override  public String toUpperCase(String str) throws RemoteException {  if (str != null) {  return str.toUpperCase();  }  return null;  }  @Override  public int plus(int a, int b) throws RemoteException {  return a + b;  }  };  }  

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

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

    public class MainActivity extends Activity implements OnClickListener {  private Button startService;  private Button stopService;  private Button bindService;  private Button unbindService;  private MyAIDLService myAIDLService;  private ServiceConnection connection = new ServiceConnection() {  @Override  public void onServiceDisconnected(ComponentName name) {  }  @Override  public void onServiceConnected(ComponentName name, IBinder service) {  myAIDLService = MyAIDLService.Stub.asInterface(service);  try {  int result = myAIDLService.plus(3, 5);  String upperStr = myAIDLService.toUpperCase("hello world");  Log.d("TAG", "result is " + result);  Log.d("TAG", "upperStr is " + upperStr);  } catch (RemoteException e) {  e.printStackTrace();  }  }  };  ......  }  

我们只是修改了ServiceConnection中的代码。可以看到,这里首先使用了 MyAIDLService.Stub.asInterface()方法将传入的IBinder对象传换成了MyAIDLService对象,接下来就可 以调用在MyAIDLService.aidl文件中定义的所有接口了。这里我们先是调用了plus()方法,并传入了3和5作为参数,然后又调用了 toUpperCase()方法,并传入hello world字符串作为参数,最后将调用方法的返回结果打印出来。

现在重新运行程序,并点击一下Bind Service按钮,可以看到打印日志如下所示:

由此可见,我们确实已经成功实现跨进程通信了,在一个进程中访问到了另外一个进程中的方法。

不过你也可以看出,目前的跨进程通信其实 并没有什么实质上的作用,因为这只是在一个Activity里调用了同一个应用程序的Service里的方法。而跨进程通信的真正意义是为了让一个应用程 序去访问另一个应用程序中的Service,以实现共享Service的功能。那么下面我们自然要学习一下,如何才能在其它的应用程序中调用到 MyService里的方法。

在上一篇文章中我们已经知道,如果想要让Activity与Service之间建立关联,需要调用bindService()方法,并将Intent作为参数传递进去,在Intent里指定好要绑定的Service,示例代码如下:

    Intent bindIntent = new Intent(this, MyService.class);  bindService(bindIntent, connection, BIND_AUTO_CREATE);  

这里在构建Intent的时候是使用MyService.class来指定要绑定哪一个Service的,但是在另一个应用程序中去绑定Service的 时候并没有MyService这个类,这时就必须使用到隐式Intent了。现在修改AndroidManifest.xml中的代码,给 MyService加上一个action,如下所示:

    <?xml version="1.0" encoding="utf-8"?>  <manifest xmlns:android="http://schemas.android.com/apk/res/android"  package="com.example.servicetest"  android:versionCode="1"  android:versionName="1.0" >  ......  <service  android:name="com.example.servicetest.MyService"  android:process=":remote" >  <intent-filter>  <action android:name="com.example.servicetest.MyAIDLService"/>  </intent-filter>  </service>  </manifest>  

这就说明,MyService可以响应带有com.example.servicetest.MyAIDLService这个action的Intent。

现在重新运行一下程序,这样就把远程Service端的工作全部完成了。

然后创建一个新的Android项目,起名为ClientTest,我们就尝试在这个程序中远程调用MyService中的方法。

ClientTest中的Activity如果想要和MyService建立关联其实也不难,首先需要将MyAIDLService.aidl文件从ServiceTest项目中拷贝过来,注意要将原有的包路径一起拷贝过来,完成后项目的结构如下图所示:

这就说明,MyService可以响应带有com.example.servicetest.MyAIDLService这个action的Intent。

现在重新运行一下程序,这样就把远程Service端的工作全部完成了。

然后创建一个新的Android项目,起名为ClientTest,我们就尝试在这个程序中远程调用MyService中的方法。

ClientTest中的Activity如果想要和MyService建立关联其实也不难,首先需要将MyAIDLService.aidl文件从ServiceTest项目中拷贝过来,注意要将原有的包路径一起拷贝过来,完成后项目的结构如下图所示:

然后打开或新建activity_main.xml,在布局文件中也加入一个Bind Service按钮:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:orientation="vertical"  >  <Button   android:id="@+id/bind_service"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:text="Bind Service"  />  </LinearLayout>  

接下来打开或新建MainActivity,在其中加入和MyService建立关联的代码,如下所示:

    public class MainActivity extends Activity {  private MyAIDLService myAIDLService;  private ServiceConnection connection = new ServiceConnection() {  @Override  public void onServiceDisconnected(ComponentName name) {  }  @Override  public void onServiceConnected(ComponentName name, IBinder service) {  myAIDLService = MyAIDLService.Stub.asInterface(service);  try {  int result = myAIDLService.plus(50, 50);  String upperStr = myAIDLService.toUpperCase("comes from ClientTest");  Log.d("TAG", "result is " + result);  Log.d("TAG", "upperStr is " + upperStr);  } catch (RemoteException e) {  e.printStackTrace();  }  }  };  @Override  protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  Button bindService = (Button) findViewById(R.id.bind_service);  bindService.setOnClickListener(new OnClickListener() {  @Override  public void onClick(View v) {  Intent intent = new Intent("com.example.servicetest.MyAIDLService");  bindService(intent, connection, BIND_AUTO_CREATE);  }  });  }  }  

这部分代码大家一定会非常眼熟吧?没错,这和在ServiceTest的MainActivity中的代码几乎是完全相同的,只是在让Activity和 Service建立关联的时候我们使用了隐式Intent,将Intent的action指定成了 com.example.servicetest.MyAIDLService。

在当前Activity和MyService建立关联之后,我们仍然是调用了plus()和toUpperCase()这两个方法,远程的MyService会对传入的参数进行处理并返回结果,然后将结果打印出来。

这样的话,ClientTest中的代码也就全部完成了,现在运行一下这个项目,然后点击Bind Service按钮,此时就会去和远程的MyService建立关联,观察LogCat中的打印信息如下所示:

不用我说,大家都已经看出,我们的跨进程通信功能已经完美实现了。

不过还有一点需要说明的是,由于这是在不同的进程之间传递数 据,Android对这类数据的格式支持是非常有限的,基本上只能传递Java的基本数据类型、字符串、List或Map等。那么如果我想传递一个自定义 的类该怎么办呢?这就必须要让这个类去实现Parcelable接口,并且要给这个类也定义一个同名的AIDL文件。这部分内容并不复杂,而且和 Service关系不大,所以就不再详细进行讲解了,感兴趣的朋友可以自己去查阅一下相关的资料。

好了,结合上下两篇,这就是关于Service你所需知道的一切。

转载于:https://www.cnblogs.com/lance-ehf/p/4420840.html

Android Service完全解析,关于服务你所需知道的一切(下) (转载)相关推荐

  1. Android Service完全解析,关于服务你所需知道的一切(下)

    转载请注册出处:http://blog.csdn.net/guolin_blog/article/details/9797169 在上一篇文章中,我们学习了Android Service相关的许多重要 ...

  2. Android--Service完全解析,关于服务你所需知道的一切(下)

    在上一篇文章中,我们学习了Android Service相关的许多重要内容,包括Service的基本用法.Service和Activity进行通信.Service的销毁方式.Service与Threa ...

  3. Android Service完全解析,关于服务你所需知道的一切(上)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的A ...

  4. (转载) android项目大全,总有你所需的

    目录视图 摘要视图 订阅 赠书 | 异步2周年,技术图书免费选      程序员8月书讯      项目管理+代码托管+文档协作,开发更流畅 [置顶] android项目大全,总有你所需的 标签: 源 ...

  5. android 项目大全,总有你所需的

    android项目大全,总有你所需的 目录(?)[+] 注:打开请贴网址,有些直接通过链接打开的不正确. 1.相对布局实例 http://kukuqiu.iteye.com/blog/1018396 ...

  6. 简单音乐播放实例的实现,Android Service AIDL 远程调用服务

    2019独角兽企业重金招聘Python工程师标准>>> Android Service是分为两种: 本地服务(Local Service): 同一个apk内被调用 远程服务(Remo ...

  7. Android Service AIDL 远程调用服务 【简单音乐播放实例】

    Android Service是分为两种: 本地服务(Local Service): 同一个apk内被调用 远程服务(Remote Service):被另一个apk调用 远程服务需要借助AIDL来完成 ...

  8. Android Service 全解析

    Service的种类    按运行地点分类: 类别 区别 优点 缺点 应用 本地服务(Local) 该服务依附在主进程上, 服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Loca ...

  9. Android Service学习之本地服务

    Service是在一段不定的时间运行在后台,不和用户交互应用组件.每个Service必须在manifest中 通过<service>来声明.可以通过contect.startservice ...

  10. android技术服务,Android Service学习之本地服务

    Service是在一段不定的时间运行在后台,不和用户交互应用组件.每个Service必须在manifest中 通过来声明.可以通过contect.startservice和contect.bindse ...

最新文章

  1. Shiro 那点事儿
  2. CentOS源码下载和Windows平台下解压rpm包
  3. linux卸载python3.6,当python3.6位于/usr/local/bin/python3.6时,如何在ubuntu上卸载它
  4. 地图按照自己规定路线进行移动
  5. 守卫者的挑战(guard)
  6. CIFAR-10 dataset 的下载与使用
  7. 【Flink】运行Flink 1.6.2 程序偶然报错 Premature end of GET request
  8. event.type 事件属性
  9. 查看html代码来下载mp4视频的一次记录
  10. 一文读懂沃尔玛、腾讯、京东、浙商银行在供应链领域的区块链应用实例
  11. 闲聊机器人实例二:python实现小姜机器人(检索式chatbot_fuzzywuzzy)
  12. 双语电子书epub格式
  13. 华为U-SYS系统力助运营商实施转型
  14. 何时是PNE(纯策略纳什均衡)?何时是MNE(混合策略纳什均衡)?
  15. 相机跟频闪灯(LAMP-S25)、爆闪灯(LAMP-F25)、频爆一体灯(LAMP-SF25)信号线如何连接?
  16. 密集子图挖掘算法的相关知识
  17. 摸鱼加速小能手,实用笔刷快拿走
  18. element form表单validateField对部分表单字段进行校验时触发多次校验
  19. 我的武功太争气,居然能自己修炼(一)
  20. teamviewer的安装和卸载

热门文章

  1. 【Cocoa(mac) Application 开发系列之二】总结一些常用控件及自定义View
  2. Resteasy配置及其使用
  3. Linux 超级漂亮的 Shell
  4. 关于序列化的 10 几个问题,你肯定不知道
  5. 为什么 Redis 默认 16 个库?90%以上程序员不知道!
  6. 【性能优化实战】4次版本迭代,我们将项目性能提升了360倍!
  7. 动画演绎Java常用数据结构
  8. 滴滴巨亏109亿后,裁员2000人,补偿方案已出,员工争着被裁
  9. 疫情之后,有哪些巨大的商业机会?
  10. H5 新特性之 fileReader 实现本地图片视频资源的预览