首页 > 技术文章 > Android 跨进程通信之AIDL

guanxinjing 2022-05-25 20:10 原文

前言

  在Android跨进程通信的方式有很多比如广播,ContentProvider,AIDL等等,它们各自有各自的使用范围。而且AIDL更像是Java的ServerSocket通信机制, 需要一个常驻的服务端与调用它的客户端。AIDL现在的缺点可能也是需要一个服务配合使用。因为目前Android端Server的使用要求越来越多(前台化),使用场景也越来越少(WorkManager替代)。 AIDL更多的是在开发设备应用与系统开发中使用。切记不可随便选择AIDL技术。这样只会给你的项目带来混乱与大量强入侵性的代码。

 

简单实现Demo

服务端

创建AIDL

创建

 

完成后,在这个默认文件里会有一些默认方法,它告诉你了AIDL可以使用那些类型数据

 

 

 

编辑AIDL文件

编辑完成后,ReBuild 重新编译下整个项目,Android stuido会自动在debug里生成一份对应的aidl的java接口类

package com.zh.aidl;

interface IDemoService {
    void setNum(int num);
    int getNum();
}

自动生成的java接口

 

创建服务,将服务绑定AIDL

注册清单

        <!--    android:enabled="true" 与 android:exported="true"  是必要属性 -->

        <!--    android:enabled 定义服务能否被系统实例化的标签,true表示可以实例化,false不能实例化,默认为true。标签也有enabled标签,这个标签适用于application下所有组件。
                只有当和下enabled标签的属性都为true的时候,才可以将广播接受者启动(enabled),否则广播接受者不能开启(disabled),不能被实例化。  -->

        <!--    android:exported 定义服务能否被外部应用的组件调用或者交互,true表示可以,false表示不能。
                如果设置为false,服务只能接收本应用的组件或者是具有相同用户ID的应用所发出的所开启或绑定。  -->
        <service
            android:name=".DemoService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="DemoService" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>

        </service>
    </application>

服务

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

public class DemoService extends Service {
    private static final String CHANNEL_ID = "DemoService";
    private int mNum = 0;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //绑定AIDL
        return iBinder;
    }

    /**
     * 实例化AIDL
     */
    private IBinder iBinder = new IDemoService.Stub() {
        @Override
        public void setNum(int num) throws RemoteException {
            mNum = num;
            Log.e("zh", "服务器端接收数据: Num = " + num);

        }

        @Override
        public int getNum() throws RemoteException {
            return mNum;
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            frontDeskService();
        }
    }

    /**
     * 前台服务
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    private void frontDeskService() {
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "主服务", NotificationManager.IMPORTANCE_HIGH);
        channel.enableLights(true);//设置提示灯
        channel.setLightColor(Color.RED);//设置提示灯颜色
        channel.setShowBadge(true);//显示logo
        channel.setDescription("zh");//设置描述
        channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); //设置锁屏可见 VISIBILITY_PUBLIC=可见
        manager.createNotificationChannel(channel);

        Notification notification = new Notification.Builder(this)
                .setChannelId(CHANNEL_ID)
                .setContentTitle("主服务")//标题
                .setContentText("运行中...")//内容
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)//小图标一定需要设置,否则会报错(如果不设置它启动服务前台化不会报错,但是你会发现这个通知不会启动),如果是普通通知,不设置必然报错
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                .build();
        startForeground(1, notification);
    }
}

启动服务

    private fun startService() {
        val intent = Intent(this, DemoService::class.java)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(intent)
        } else{
            startService(intent)
        }
    }

客户端

将服务端的AIDL文件复制到客户端里,注意包名路径要一致

 

 绑定服务,接收数据,发送数据

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(mBinding.root)

        mBinding.connectService.setOnClickListener {
            //绑定服务
            val intent = Intent().apply {
                setPackage("com.zh.aidl")
                setAction("DemoService")
            }
            bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
        }

        mBinding.sendValue.setOnClickListener {
            //发送数据
            mIDemoService?.num = mBinding.editTextNumber.text.toString().toInt()
        }
        mBinding.getValue.setOnClickListener {
            //接收数据
            Log.e("zh", "客户端接收: num = " + mIDemoService?.num)
        }
    }

自定义数据

服务端

目录结构

 

 

创建自定义Bean

 

 UserBean数据需要添加序列化

 

在服务接口里引入UserBean,在参数前面需要添加 in 关键字。不添加会报错

其他绑定服务跟上面的demo是一样的

客户端

与上面的Demo一样需要把服务端的AIDL文件复制到客户端项目里,这里的UserBean一样有路径保持一致的要求,并且也是需要序列化的。

 

 

其他步骤与上面的Demo一致 

 

 

 

 

End

推荐阅读