观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

前言

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

  这里提一下在设备开发中使用AIDL重要性。 在设备开发中,如果是自定义launcher就会大量涉及到系统功能开发,比如亮度调节、音量调节等等。当然一部分功能也可以反射代码(在MtkSettings工程里的大量功能都可以反射实现)与架framework包解决,但是有些系统功能已经是跨进程了(比如监听更底层的按键事件,触控点击事件无法在应用进程中反射与调用),无法依靠反射与架framework包解决。当然你也可以使用广播机制解决跨进程的调用,但是广播机制的使用过于零散。在系统中内置一个调用framework系统进程代码的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文件复制到客户端里

注意!包名路径要一致

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

请注意,这里的setAction需要与上面清单中注册的一致

    private var  mIDemoService : IDemoService? = null
    private var mIsBindService = false

    private val mServiceConnection = object :ServiceConnection{
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            mIsBindService = true
            mIDemoService = IDemoService.Stub.asInterface(service)

        }

        override fun onServiceDisconnected(name: ComponentName?) {
            mIsBindService = false
            mIDemoService = null
        }
    }

    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一致 

AIDL实现接口回调

服务端

创建AIDL文件

IMyAidlInterface.aidl

// IMyAidlInterface.aidl
package com.zh.aidldemo;

//请注意,这里是容易疏漏的地方,需要用import将IMyListener引入
import com.zh.aidldemo.IMyListener;

interface IMyAidlInterface {

    void setListener(IMyListener listener);
}

IMyListener.aidl

这个是接口回调

// IMyListener.aidl
package com.zh.aidldemo;

interface IMyListener {
    void onEvent(boolean type);
}

创建服务

请注意!在清单中添加服务,也与上面的例子一致,这里不在重复

import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.IBinder

class MyService : Service() {
    var mIMyListener: IMyListener? = null

    companion object {

        /**
         * 启动服务
         */
        fun startService(context: Context) {
            val intent = Intent(context, MyService::class.java)
            context.startService(intent)
        }
    }

    private val mIBinder = object : IMyAidlInterface.Stub() {
        override fun setListener(listener: IMyListener?) {
            //这里处理接口
            mIMyListener = listener
        }
    }

    override fun onBind(intent: Intent?): IBinder? {
        return mIBinder
    }
}

客户端

导入aidl文件与上面的例子一样(需要保持路径与文件的一致),不在重复举例

绑定AIDL服务,添加接口回调

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import android.util.Log

class ClientAIDL {
    private var mIMyAidlInterface: IMyAidlInterface? = null
    private var mIsBindService = false
    //请注意,这里一定要让IMyListener.Stub成为全局变量被持有,如果是方法内部的局部变量可能会出现 java.lang.RuntimeException: java.lang.reflect.InvocationTargetException 的错误
    private var mIMyListener: IMyListener.Stub? = null

    companion object {
        val instance by lazy { ClientAIDL() }
    }

    private val mServiceConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            Log.e("zh", "触发绑定服务 onServiceConnected: ")
            mIsBindService = true
            mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service)
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            mIsBindService = false
            mIMyAidlInterface = null
        }
    }

    /**
     * 绑定服务
     */
    fun bindService(context: Context) {
        val intent = Intent().apply {
            setPackage("com.zh.aidldemo")
            setAction("aidldemo")
        }
        context.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
    }

    /**
     * 设置监听
     */
    fun setListener() {
        mIMyListener = object : IMyListener.Stub() {
            override fun onEvent(type: Boolean) {
                //处理回调
            }
        }
        mIMyAidlInterface?.setListener(mIMyListener)
    }
}

 

 

End

posted on 2022-05-25 20:10  观心静  阅读(429)  评论(0编辑  收藏  举报