当前位置: 首页 > news >正文

aidl跨进程通讯

Activity如果想要调用Service中的方法就需要绑定服务,然后才能获取服务的代理对象。进一步调用服务中的方法。类似这样的代码:

首先去创建一个服务:

public class MyService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }

    class MyBinder extends Binder implements IService {
        @Override
        public void showToast() {
            Show();
        }
    }

    private void Show() {
        Toast.makeText(this, "我是服务中的方法", Toast.LENGTH_SHORT).show();
    }
}复制代码

这里Iservice是为了解耦去定义的接口:

interface IService {
    void showToast();
}复制代码

我们在Activity中需要去获取binder对象,然后调用服务中的方法:

package remote.zz.remoteservice;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;

import java.util.List;

/**
 * Created by Administrator on 2018/6/27.
 */

public class MainActivity extends AppCompatActivity {

    private MyServiceConnection conn;
    private IService ser;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //绑定服务
        Intent service = new Intent(this, MyService.class);
        conn = new MyServiceConnection();
        bindService(service, conn, Context.BIND_AUTO_CREATE);
        //点击对象
        findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //调用服务中的方法
                ser.showToast();
            }
        });

    }

    //内部类
    class MyServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取服务中代理对象
            ser = (IService) service;
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    }

    @Override
    protected void onDestroy() {
        //取消绑定,防止报错
        unbindService(conn);
        super.onDestroy();
    }
}复制代码

上边的代码是同一个应用中Activity调用Service的基本代码。

假如有这样一个需求,A应用绑定B应用的服务,并互相之间传递数据,这样的需求呢?这个时候你首先会想到用intent的bundle共享文件或者sp对象(并发会有问题,不建议使用),Messenger(它是封装aidl的,不好用)或者广播,当然这些手段是可以的。但是除了这些之外,我们还可以使用aidl实现跨应用之间的数据传递和调用。为了方便叙述,A应用称为本地应用,B应用成为远程服务应用。跨应用之间通讯其实内部也是使用Binder去做代理对象。只不过这个binder是系统帮我们生成的代码,但是我们需要按照步骤去创建需要的文件:

步骤:

1.先在远程服务应用B工程的main目录下建立一个名为aidl的文件夹,再右键该文件夹,在该文件夹下面新建一个名字与AndroidManifest.xml中的package相同的包,再右键该包,新建你所需的AIDL文件;

创建一个aidl文件,其实就是一个接口:

 IRemoteService.aidl

package remote.zz.remoteservice;
//注意这里需要自己导入包
import remote.zz.remoteservice.Book;

interface IRemoteService {

    List<Book> getData();
    //注意这里的in必须写上,表示传递的方向
    void setData(in Book book);

}复制代码

还需要创建一个Book数据类型,并且实现序列化:

package remote.zz.remoteservice;

import android.os.Parcel;
import android.os.Parcelable;
//这个写到java文件夹下即可
public class Book implements Parcelable {

    public int bookId;
    public String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(bookId);
        out.writeString(bookName);
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.
            Creator<Book>() {
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    private Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    @Override
    public String toString() {
        return "Book{" +
                "bookId=" + bookId +
                ", bookName='" + bookName + '\'' +
                '}';
    }
}复制代码

同时需要创建一个Book.aidl文件

package remote.zz.remoteservice;//这里的包名就是book的包名

parcelable Book;//被序列化的数据类
复制代码

2.当你进入你的AIDL文件并编写好了之后,点击AS上方菜单栏中的Build->Make Project,之后便可以在当前工程的app/build/generated/source/aidl/debug中找到系统为我们生成的.java文件了。这个对用生成的java文件就是系统为你创建binder类。

对应的服务我们应该这样写:

package remote.zz.remoteservice;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2018/6/27.
 */

public class RemoveService extends Service {
    private ArrayList arr;

    @Override
    public void onCreate() {
        super.onCreate();
        arr = new ArrayList<Book>();
        arr.add(new Book(1001, "我与地坛"));
        arr.add(new Book(1002, "钢铁是怎样练成的"));
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }

    //系统为我们生成的binder,名字叫Stub, 可点进去查看源码
    class MyBinder extends IRemoteService.Stub {

        @Override
        public List<Book> getData() throws RemoteException {
            return arr;
        }

        @Override
        public void setData(Book book) throws RemoteException {
            arr.add(book);
        }
    }


}复制代码

在清单文件别忘了配置过滤器,让其他应用可以开启服务

<!--提供远程服务的服务,5.0之后必须写  android:exported="true"-->
<service
    android:name=".RemoveService"
    android:exported="true">
    <intent-filter>
        <action android:name="zz.remove.service" />
    </intent-filter>
</service>复制代码

那么本地应用A怎么调用呢?

同样先复制远程应用aidl文件夹到本地应用的mian下,然后在mian下创建同远程应用Book类一样的文件夹,把Book类复制到这个文件夹下边,然后去Build->Make Project,同样也会生成一个binder类,这个时候我们就可以调用远程的应用获取服务中的数据:

package local.zz.localapp;

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.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;

import java.util.List;

import remote.zz.remoteservice.Book;
import remote.zz.remoteservice.IRemoteService;

public class MainActivity extends AppCompatActivity {

    private MyConnection conn;
    private IRemoteService remote;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //隐私启动服务,5.0之后需要设置包名,否则报错
        Intent service = new Intent();
        service.setAction("zz.remove.service");
        service.setPackage("remote.zz.remoteservice");
        conn = new MyConnection();
        bindService(service, conn, BIND_AUTO_CREATE);
        findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    //获取远程服务数据
                    List<Book> data = remote.getData();
                    for (Book book : data) {

                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        findViewById(R.id.tv2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    //调用远程方法传递数据
                    remote.setData(new Book(1003, "让子弹飞"));
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

    }


    class MyConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取代理对象
            remote = IRemoteService.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    }


    @Override
    protected void onDestroy() {
        unbindService(conn);
        super.onDestroy();
    }
}
复制代码

到此,aidl的使用完毕!


aidl可以传递aidl声明的接口,所以我们可以做一个观察者模式,动态去监听图书的变化,然后通知注册的观察者。

写这个demo时候遇到一个问题:如果本app的服务没有启动,另外app去绑定获取不到代理对象,还不知道原因是什么!


相关文章:

  • MySQL主从介绍 准备工作 配置主 配置从 测试主从同步
  • 定位多线程内存越界问题实践总结【转】
  • Git远程操作
  • Kafka server.properties配置说明
  • MYSQL的批量插入或更新方法优化
  • Redis 如何分布式,来看京东金融的设计与实践
  • 华为云,有技术,有未来
  • 查看数据库账号
  • 不小心删数据库是一种怎样的体验?
  • 正面反击 Google、FB 等巨头,万维网之父携 Solid 归来
  • cacti 的 “FATAL: Cannot connect to MySQL server on
  • 人机大战引关注 人工智能概念股有望受追捧
  • 0705作业
  • Js模块化之-import和export
  • DevC++连接MySQL可用详细教程
  • #Java异常处理
  • 【Leetcode】104. 二叉树的最大深度
  • FineReport中如何实现自动滚屏效果
  • idea + plantuml 画流程图
  • Java方法详解
  • Java面向对象及其三大特征
  • Js基础知识(一) - 变量
  • Selenium实战教程系列(二)---元素定位
  • Windows Containers 大冒险: 容器网络
  • 从PHP迁移至Golang - 基础篇
  • 基于 Babel 的 npm 包最小化设置
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 判断客户端类型,Android,iOS,PC
  • 十年未变!安全,谁之责?(下)
  • 腾讯优测优分享 | 你是否体验过Android手机插入耳机后仍外放的尴尬?
  • 一起参Ember.js讨论、问答社区。
  • ​业务双活的数据切换思路设计(下)
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (14)Hive调优——合并小文件
  • (C++20) consteval立即函数
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (zhuan) 一些RL的文献(及笔记)
  • (多级缓存)多级缓存
  • (二)换源+apt-get基础配置+搜狗拼音
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (附源码)计算机毕业设计SSM智慧停车系统
  • (新)网络工程师考点串讲与真题详解
  • (一)【Jmeter】JDK及Jmeter的安装部署及简单配置
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • (转载)hibernate缓存
  • *setTimeout实现text输入在用户停顿时才调用事件!*
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .net core 调用c dll_用C++生成一个简单的DLL文件VS2008
  • .net core 实现redis分片_基于 Redis 的分布式任务调度框架 earth-frost
  • .net MVC中使用angularJs刷新页面数据列表
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • @JsonFormat与@DateTimeFormat注解的使用