摘要: 版权声明:本文出自汪磊的博客,转载请务必注明出处。

在上一篇中我们学习了Android Service相关的许多基础但是重要的内容,基本涵盖大部分平日里的开发工作。今天我们继续学习一下稍微高级一点的用法,即:远程Service用法,使用远程Service可以实现安卓跨进程通信的功能。下面我们就开始学习一下吧。

什么是远程Service?

所谓的远程Service就是与调用者不在同一进程的Service即可叫做远程Service。那是不是也有近程Service?其实不叫近程Service,专业叫法叫做本地Service,就是调用者与Service在同一进程。

调用远程Service的桥梁-AIDL接口定义语言

由于远程服务与调用者不在同一进程,我们不能再像调用本地服务一样的方式去调用远程服务,

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

AIDL(Android Interface Definition Language)是Android接口定义语言的意思,它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。(本篇着重讲解与远程服务通信的过程,不涉及AIDL接口定义语言的细节讲解)

废话少说,下面我们通过一个简单的Demo来讲解一下与远程Service通信的过程。

首先我们新建一个远程服务Demo的工程,新建RemoteService.aidl文件,代码如下:

 package com.wl.remoteservice;
interface RemoteService{
int call(int a,int b);
}

我们点击完保存之后,在gen目录下会生成一个与之对应的java文件,如下:

关于文件中内容我们暂时不细究,会在下篇的时候在研究一下。

然后编写Service类,新建WLService继承Service,如下:

 public class WLService extends Service {

     private static final String TAG = "HEART";
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind");
return new MyBinder();
} private int methodInRemoteService(int a,int b){ return a+b;
} private class MyBinder extends RemoteService.Stub{ @Override
public int call(int a,int b) { return methodInRemoteService(a,b);
}
} @Override
public void onCreate() {
Log.i(TAG, "onCreate");
super.onCreate();
} @Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind");
return super.onUnbind(intent); }
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy");
super.onDestroy();
} }

这里我们定义了一个内部类MyBinder继承自RemoteService.Stub,那这个stub是什么玩意呢?点进去看一下,如下

public static abstract class Stub extends android.os.Binder implements com.wl.remoteservice.RemoteService

看定义我们就明白了,原来MyBinder就是Binder子类并且实现了RemoteService接口,所以在onBind的时候返回其实例也就容易理解了。

接下来,在清单文件注册Service。这里我们需要在另一个应用程序启动WLService,但是在另一个应用程序中去绑定Service的时候并没有WLService这个类,这时就必须使用到隐式Intent了。现在修改AndroidManifest.xml中的代码,给WLService加上一个action,如下所示:

         <service android:name="com.wl.remoteservice.WLService">
<intent-filter >
<action android:name="com.wanglei.remoteservice"/>
</intent-filter>
</service>

然后我们新建一个项目用以调用上面项目中的远程服务,新项目就叫做:调用远程服务。

首先需要将RemoteService.aidl拷贝过来,记住需要连同原包路径一同拷贝过来,如下:

接下来,修改布局文件:

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" > <Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="bind"
android:text="绑定服务" /> <Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="unbind"
android:text="解除绑定服务" /> <Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="click"
android:text="调用远程服务方法" /> </LinearLayout>

然后编写MainActivity中代码,如下:

 public class MainActivity extends Activity {
private static final String TAG = "HEART";
private RemoteService mRemoteService;
private MyConn conn; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} public void bind(View view){
Intent intent = new Intent();
intent.setAction("com.wanglei.remoteservice");
conn = new MyConn();
bindService(intent, conn, BIND_AUTO_CREATE);//异步的操作
} public void unbind(View view){
unbindService(conn);
} public void click(View view){ if(null != mRemoteService){
try {
int result = mRemoteService.call(10,20);
Log.i(TAG, "result = "+result);
} catch (RemoteException e) {
e.printStackTrace();
}
}
} private class MyConn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) { mRemoteService = RemoteService.Stub.asInterface(service);
} @Override
public void onServiceDisconnected(ComponentName name) { }
}
}

会发现和上篇讲的bind服务差别不大,这里我们需要在另一程序里面绑定服务所以需要用隐式意图开启。

这里我们需要注意一下,在安卓5.0出来以后,服务意图必须用显式调用,调用bind服务的时候会报如下警告

我们找一下源码,看看哪里报的这个警告,终于在 sdk/sources/android-21/android/app/ContextImpl.Java目录下找到报警告的代码:

     private void validateServiceIntent(Intent service) {
if (service.getComponent() == null && service.getPackage() == null) {
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
IllegalArgumentException ex = new IllegalArgumentException(
"Service Intent must be explicit: " + service);
throw ex;
} else {
Log.w(TAG, "Implicit intents with startService are not safe: " + service
+ " " + Debug.getCallers(2, 3));
}
}
}

好了找到报错的原因了,那怎么解决呢?观察源码发现最外层if判断的时候有个service.getPackage() == null,我们只需要使其不为null不就可以了,这也是谷歌推荐的解决方法

我们将绑定服务的方法改为如下:

 public void bind(View view){
Intent intent = new Intent();
intent.setAction("com.wanglei.remoteservice");
intent.setPackage("com.wl.remoteservice");//设置为Service所在包名
conn = new MyConn();
bindService(intent, conn, BIND_AUTO_CREATE);//异步的操作
}

这样修改就好了,当然还有另外一种解决方法,将隐式意图变为显示意图,方法如下:

 public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}

好了,通过以上两种方式就可以解决了,到此我们两个项目都已经编写完毕,先运行远程服务demo项目,在运行调用远程服务项目,点击绑定远程服务按钮,然后点击调用远程服务按钮,打印如下:

到此为止,我们成功调用了远程服务中的方法,跨进程通信成功实现,不过有些刚接触的同学可能还有些混乱,怎么觉得那么混乱,比平时用的组建多那么多步骤,那我们总结一下调用远程服务的步骤:

1.编写aidl文件

2.创建远程服务类,在服务的内部创建一个内部类提供一个方法,可以间接调用服务的方法

3.实现服务的onbind方法,返回服务内部类实例

4.拷贝aidl文件到另一工程联同包名

5.在activity 绑定服务。bindService();

6.在服务成功绑定的时候 会执行一个方法 onServiceConnected 传递过来一个 IBinder对象进行类型转换

7.调用远程服务里面的方法。

好了,到这里关于服务的所有最重要的部分都已经讲解完了,通过(上)(中)两篇相信你对服务有了一个全面的认识。

安卓Service完全解析(中)的更多相关文章

  1. 安卓Service完全解析(上)

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 关于安卓Service相信很多安卓开发者都听说过,作为安卓四大组件之一,即使不经常用也应该听说过,但并不是每一个人都掌握的特别详细,全面.那么今天我 ...

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

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

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

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

  4. 【转】Android Service完全解析,关于服务你所需知道的一切(下) ---- 不错

    原文网址:http://blog.csdn.net/guolin_blog/article/details/9797169 转载请注册出处:http://blog.csdn.net/guolin_bl ...

  5. WebGIS实现在线要素编辑之ArcGIS Server 发布Feature Service 过程解析

    WebGIS实现在线要素编辑之ArcGIS Server 发布Feature Service 过程解析 FeatureService也称要素服务,其最大的好处就是支持在线要素编辑,并将编辑同步更新到后 ...

  6. [转]Android Service完全解析,关于服务你所需知道的一切

      目录(?)[+] Android Service完全解析,关于服务你所需知道的一切(上) 分类: Android疑难解析2013-10-31 08:10 6451人阅读 评论(39) 收藏 举报 ...

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

    相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的Android程序员如果连Service都没听说过的话,那确实也太逊了.Service作为Android四大组件之一,在每一个应用程序 ...

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

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

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

    转自:http://blog.csdn.net/guolin_blog/article/details/11952435 转载请注明出处:http://blog.csdn.net/guolin_blo ...

随机推荐

  1. 使用jQuery操作 DOM

    DOM操作分为三类: 1.DOM Core:任何一种支持DOM的编程语言都可以使用它,如getElementById() 2.HTML-DOM:用于处理HTML文档,如document.forms 3 ...

  2. 总结下Redux

    Redux 和 React 没有直接关系,它瞄准的目标是应用状态管理. 核心概念是 Map/Reduce 中的 Reduce.且 Reducer 的执行是同步,产生的 State 是 Immutabl ...

  3. Asp.net MVC-3-执行过程

    本篇主要讲述MVC处理请求时创建Controller和执行Action的完整过程. 创建Controller 先查看MvcHandler中处理请求的方法BeginProcessRequest: pro ...

  4. java编译出错信息汇总(更新)

    1. java.lang.ClassNotFoundException: com.mysql.jdbc.Driver 原因:缺包mysql-connector-java-5.1.7-bin.jar 2 ...

  5. 【ALB学习笔记】基于.NET环境的高频RFID卡读写设备的基本操作案例

    基于.NET环境的高频RFID卡读写设备的基本操作案例 广东职业技术学院  欧浩源 1.引言 RFID高频卡在我们的日常生活中随处可见,是物联网应用中不可或缺的一个重要部分,也是全国职业院校技能大赛& ...

  6. java Static的使用

    static是一个静态修饰符,用于修饰成员(成员变量,成员函数).<thinking in java>对staic的使用场景有下面2种定义:“一种情形是只想用一个存储区域来保存一个特定的数 ...

  7. CentOS7 搭建Ambari-Server,安装Hadoop集群(一)

    2017-07-05:修正几处拼写错误,之前没发现,抱歉! 第一次在cnblogs上发表文章,效果肯定不会好,希望各位多包涵. 编写这个文档的背景是月中的时候,部门老大希望我们能够抽时间学习一下Had ...

  8. pug模板引擎(原jade)

    前面的话 为什么要引入pug,pug有什么特别之处呢?有一些嵌套层次较深的页面,可能会出现巢状嵌套,如下图所示 在后期维护和修改时,一不小心少了一个尖括号,或者某个标签的开始和闭合没有对应上,就会导致 ...

  9. MyBatis+mysql 简单分页

    注意:limit不能跟动态内容 <select id="fenYe" parameterType="int" resultType="com.x ...

  10. 如何写出面试官欣赏的Java单例

    单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中一个类只有一个实例. 今天我们不谈单例模式的用途,只说一说如果在面试的时候面试官让你敲一段代码 ...