Android:远程 Service 和 AIDL 跨进程通信

远程 Service#

Service 是运行在主线程里的,如果直接在 Service 中处理一些耗时的逻辑,就会导致程序 ANR。可以在 Service 中开启线程去执行耗时任务,也可以索性将原来的 Service 转换成一个远程 Service
将 Service 转换成远程 Service 只需要在注册 Service 的时候,在 AndroidManifest.xml 文件中将 android:process 属性指定成“:remote”。

Copy Highlighter-hljs
<service android:name=".MyService" android:process=":remote"> </service>

AIDL 跨进程通信#

此时 Bind Service 让 Activity 和 Service 建立关联时,由于 Activity 和 Service 运行在两个不同的进程当中,不能再使用传统方式建立关联。
可以使用 AIDL(Android Interface Definition Language)Android 接口定义语言来进行跨进程通信(IPC),它可以让某个 Service 与多个应用程序组件之间进行跨进程通信,帮助 IPC 之间接口的建立,从而实现多个应用程序共享同一个 Service 的功能。使用的步骤如下:

  1. 定义 AIDL 接口,为 Service 建立接口 IService;
  2. Client 连接 Service 获得 Stub 对象;
  3. 在 IService 中具体实现 IService.Stub;
  4. 直接调用 IService.Stub 里面的方法。


客户端调用远程 Service 时,Android 不是直接返回 Service 对象到客户端,而是通过 onBind() 方法把 Service 的代理对象 IBinder 发送给客户端,所以 AIDL 远程接口的实现类就是 IBinder 的实现类。

远程 Service 样例#

程序需求#

基于 Service 组件技术,编程实现一个可以完成计算任意的两数的加、减、乘运算的远程服务。

功能设计#

首先需要新建一个 aidl,里面写一个接口作为我们要提供的服务,接着写一个该接口的实现类。最后完成 Activity,实现对远程服务的绑定,并且测试提供的加减乘的操作。

代码编写#

IMyAidlInterface.aidl#

Copy Highlighter-hljs
// IMyAidlInterface.aidl package com.example.test5; // Declare any non-default types here with import statements interface IMyAidlInterface { double add(double a, double b); double subtract(double a, double b); double multiply(double a, double b); }

注意我们是把接口写在 .aidl 文件中,此时如果是 eclipse 可以自动生成 IService 文件,但是如果是用 Android studio 的话就需要写完代码后建立一下:

MyService.java#

Copy Highlighter-hljs
package com.example.test5; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class MyService extends Service { //定义内部类MyServiceImpl继承AIDL文件自动生成的内部类,并且实现定义的接口方法 private class MyServiceImpl extends IMyAidlInterface.Stub{ @Override public double add(double a, double b) throws RemoteException { // TODO Auto-generated method stub return a + b; } @Override public double subtract(double a, double b) throws RemoteException { // TODO Auto-generated method stub return a - b; } @Override public double multiply(double a, double b) throws RemoteException { // TODO Auto-generated method stub return a * b; } } @Override public IBinder onBind(Intent arg0) { return new MyServiceImpl(); //返回AIDL实现 } @Override public void onDestroy(){ super.onDestroy(); } }

MainActivity#

注意不能在没有绑定服务的时候解绑服务,否则调用方法会导致系统崩溃。此时应该设置一个信号量来确定状态,只有当服务绑定了才可以解绑。

Copy Highlighter-hljs
package com.example.test5; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.View; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends Activity { static final String TAG = "IMyAidlInterface"; IMyAidlInterface iService = null; EditText num1Text, num2Text; TextView textView; double num1, num2; boolean flag = false; //信号量,防止在没有绑定服务的情况下解绑 private ServiceConnection conn = new ServiceConnection(){ @Override public void onServiceConnected(ComponentName name, IBinder service) { // 返回AIDL接口对象,然后可以调用AIDL方法 iService = IMyAidlInterface.Stub.asInterface(service); textView.setText("绑定 Service 成功!"); flag = true; //状态设置为已绑定服务 } @Override public void onServiceDisconnected(ComponentName name) { Log.i(TAG, "释放Service"); } }; @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //定位xml中的2个操作数和输出文本的位置 num1Text = (EditText) findViewById(R.id.etnum1); num2Text = (EditText) findViewById(R.id.etnum2); textView = (TextView) findViewById(R.id.textView); } public void doClick(View v) { double result = 0.0; //运算结果 switch (v.getId()) { // 绑定服务 case R.id.button1: if(!flag){ Intent bindIntent = new Intent(this, MyService.class); startService(bindIntent); bindService(bindIntent, conn, BIND_AUTO_CREATE); } break; // 解绑服务 case R.id.button5: if(flag) { unbindService(conn); textView.setText("解除绑定 Service 成功!"); flag = false; } break; // 运行服务提供的操作 default: if(getTwoNum()){ try { switch (v.getId()) { case R.id.button2: result = iService.add(num1, num2); break; case R.id.button3: result = iService.subtract(num1, num2); break; case R.id.button4: result = iService.multiply(num1, num2); break; } } catch (RemoteException e) { Log.e(TAG,"调用出错!"); e.printStackTrace(); } textView.setText("" + result); } else{ // 提示用户正确输入 textView.setText("非法输入,请重新输入!"); } break; } } public boolean getTwoNum(){ try{ //获取两个操作数 num1 = Double.parseDouble(num1Text.getText().toString()); num2 = Double.parseDouble(num2Text.getText().toString()); }catch (Exception e) { //数据非法或者没输入,返回false return false; } return true; } }

Activity_main.xml#

Copy Highlighter-hljs
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:id="@+id/tvnum1" android:textSize="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="操作数1:" android:layout_weight="1"/> <EditText android:id="@+id/etnum1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/tvhigh" android:layout_alignParentRight="true" android:hint="请输入数字" android:gravity="center" android:layout_weight="2"/> </LinearLayout> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:id="@+id/tvnum2" android:textSize="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignRight="@+id/tvhigh" android:layout_below="@+id/tvhigh" android:text="操作数2:" android:layout_weight="1"/> <EditText android:id="@+id/etnum2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@+id/tvweight" android:layout_alignLeft="@+id/ethigh" android:hint="请输入数字" android:gravity="center" android:layout_weight="2"/> </LinearLayout> <Button android:id="@+id/button1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Bind Service" android:textSize="20dp" android:onClick="doClick" /> <Button android:id="@+id/button2" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="add" android:textSize="20dp" android:onClick="doClick"/> <Button android:id="@+id/button3" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="subtract" android:textSize="20dp" android:onClick="doClick"/> <Button android:id="@+id/button4" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="multiply" android:textSize="20dp" android:onClick="doClick"/> <Button android:id="@+id/button5" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Unbind Service" android:textSize="20dp" android:onClick="doClick"/> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20sp" android:gravity="center" android:text="result" /> </LinearLayout>

运行效果#

绑定服务。

非法输入。


加减乘操作。



解除绑定。

参考资料#

《Android 移动应用开发》,杨谊 主编、喻德旷 副主编,人民邮电出版社

posted @   乌漆WhiteMoon  阅读(1198)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示
CONTENTS