AIDL 跨进程通讯简单应用

AIDL 跨进程通讯简单应用

关于多进程

在Android中,每一个应用都是一个独立的进程。有时候部分模块处于特殊需求,也需要在独立的进程中运行。同时多进程可以增加应用的可用内存空间(分配给单独进程的空间不变)

Android中常用的进程间通讯(IPC)有以下几种

  • AIDL
  • Messenger
  • Bundle
  • ContentProvider
  • BroadcastReceiver
  • 文件共享
  • Socket

本篇笔记的重点在AIDL上,对其它方法不做记录

简单步骤

接下来的步骤将记录如何创建一个记录注释和时间的服务,并通过另一个app进行操作。

创建服务

自定义类型

新建一个项目AIDLTEST0作为服务端接下来右键项目新建aidl

在AS中新建模板aidl时,会创建一个aidl包。aidl仅支持基本数据类型,以及“String”、“CharSequence”、“ArrayList”、“HashMap”(内部元素也需支持aidl)、实现了Parcelable接口的对象,以及其它AIDL类型的接口(非常规java接口)。

首先新建一个Comment.java作为需要保存的数据的类型,并实现Parcelable接口。该类只有两个属性 内容content和 时间time。都是String类型。

time的格式为“HH:mm:ss”,为了方便我直接用new SimpleDateFormater("HH:mm:ss", Locale.China).formate(new Date());进行初始化。在此说明一下

为了能在aidl中使用这个类型, 还需创建一个Comment.aidl进行声明

// Comment.aidl
package com.cloud_hermits.aidltest0;

// Declare any non-default types here with import statements
parcelable Comment;

在Android Studio中创建aidl时,AS会自动创建一个aidl包。为了方便移植,我们会将写好的Comment.java也放在这个包里,这会导致AS编译aidl文件时找不到这个自定义类型而发生错误。为了避免该情况,应在项目的build.gradleandroid{}代码块内添加以下内容

sourceSets {
       main {
           manifest.srcFile 'src/main/AndroidManifest.xml'
           java.srcDirs = ['src/main/java', 'src/main/aidl']
           resources.srcDirs = ['src/main/java', 'src/main/aidl']
           aidl.srcDirs = ['src/main/aidl']
           res.srcDirs = ['src/main/res']
           assets.srcDirs = ['src/main/assets']
       }
   }
创建aidl接口

定义好类型后,就该着手创建接口了
新建ICommentManager.aidl

// ICommentManager.aidl
package com.cloud_hermits.aidltest0;
import com.cloud_hermits.aidltest0.Comment;
// Declare any non-default types here with import statements

interface ICommentManager {
    //获取全部Comment
    List<Comment> getAllComment();
    //添加一条Comment
    void addComment(in Comment comment);
}

有几条需要注意

  • 使用自定义类型需要导入该类,而即使这个数据类在同一个包内,依旧需要填写完整包名
  • 定向tag 所有非基本参数都需要一个定向tag指出数据流通方式,而基本参数的定向tag默认且只能是in。在这里我们用in作为Comment参数的定向tag。关于定向tag的区别如下
    • in:服务端会收到完整的数据,而服务端对收到的数据进行的修改不会导致客户端的数据变化
    • out:服务端会收到参数的数据类型的空数据,而服务端对该数据进行的修改会导致客户端的参数变化
    • inout:上述两条的合一,既会收到来自客户端的完整数据,也会将修改同步到客户端
  • 接口内不能有中文注释,否则会导致报错“错误: 解析时已到达文件结尾”而无法生成相应类

编写完接口,记得进行一次编译,以便让IDE自动生成所需的类。

编写服务类

接下来需要创建实现这个接口的服务。新建服务CommentService.java

//CommentService.java 完整内容

package com.cloud_hermits.aidltest0;

import android.app.ActivityManager;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class CommentService extends Service {
    private static final String TAG = "CommentService";
    private final CopyOnWriteArrayList<Comment> mList = new CopyOnWriteArrayList<>();   //线程读写安全列表

    private IBinder mBinder = new ICommentManager.Stub() {
        @Override
        public List<Comment> getAllComment() throws RemoteException {
            Log.d(TAG, "getAllComment: comments = " + mList.toString());
            return mList;
        }

        @Override
        public void addComment(Comment comment) throws RemoteException {
            mList.add(comment);
            Log.d(TAG, "addComment: comments = " + mList.toString());
        }
    };

    public CommentService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();

        //打印当前进程
        int processId = Process.myPid();
        ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        assert manager != null;
        for (ActivityManager.RunningAppProcessInfo info : manager.getRunningAppProcesses())
            if (info.pid == processId) Log.d(TAG, "onCreate: current process = " + info.processName);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

当服务启动、新增Comment或者获取Comment的时候,都会触发打印。记得在Manifest.xml中注册该Sercice,建议为其添加<intent-filter>以便隐式调用。

同应用客户端

完成了服务端的编写,现在就可用试试使用同应用客户端进行修改了

在该AIDLTEST0的主页面上添加一个按钮,并在onCreate(Bundle savedInstanceState)中绑定该服务

// AIDLTEST0 MainActivity.java
public class MainActivity extends AppCompatActivity{
    private ICommentManager manager;
    private static final String TAG = "MainActivity";
    private int index = new Random().nextInt();

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            manager = ICommentManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //...

        bindCommentService();

        findViewById(R.id.button).setOnClickListener(v -> addComment());
    }
    
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
    }

    /**
     * 绑定服务
     * @author binze 2019/12/31 11:30
     */
    private void bindCommentService() {
        Intent intent = new Intent("com.cloud_hermits.comment.SERVICE");
        //android 5.0后,隐式绑定服务必须设置包名
        intent.setPackage("com.cloud_hermits.aidltest0");
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    /**
     * 添加Comment
     * @author binze 2019/12/31 11:31
     */
    private void addComment() {
        if (manager == null) {
            Log.e(TAG, "addComment: 获取服务失败");
            return;
        }
        Comment comment = new Comment(String.format("条目%s", index++));
        try {
            manager.addComment(comment);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

运行app,点击按钮后会在logcat中打印日志。

跨应用调用

新建工程AIDLTEST1,并将AIDLTEST0中的aidl包复制到新工程的main目录下。在主页面和AIDLTEST0一样即可,隐式绑定服务,并操作,观察两个工程的打印情况,你会发现在工程AIDLTEST1中的操作成功的修改了AIDLTEST0中的数据。

通知数据更新

既然已经能跨应用调用服务方法,自然可用在该方法中发送全局广播。接下来各个app处理该广播即可

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值