Android在kts中简单使用AIDL

Android在kts中简单使用AIDL

AIDL相信做Android都有所了解,跨进程通信会经常使用,这里就不展开讲解原理跨进程通信的方式了,最近项目换成kts的方式,于是把aidl也换成了统一的方式,其中遇到了很多问题,这里记录一下,直接上代码.

1.在groovy创建aidl文件:

在Groovy中是可以直接创建aidl文件的

2.生成的aidl文件如下:

arduino 复制代码
interface IMyService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
​
    int add(int a, int b);
}

3.创建一个服务端Service:

MyService

java 复制代码
package com.example.aidltestdemo;
​
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
​
import androidx.annotation.Nullable;
​
​
/**
 * @author: njb
 * @date: 2025/3/20 17:47
 * @desc: 描述
 */
public class MyService extends Service {
    private final IMyService.Stub mBinder = new IMyService.Stub() {
​
​
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
​
        }
​
        @Override
        public int add(int a, int b) throws RemoteException {
            return a + b;
        }
    };
​
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

4.注册服务:

ini 复制代码
<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.aidltestdemo.IMyService" />
    </intent-filter>
</service>

5.客户端代码:

typescript 复制代码
package com.example.aidltestdemo;
​
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.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
​
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
​
import com.google.android.material.snackbar.Snackbar;
​
public class MainActivity extends AppCompatActivity {
​
    private IMyService mService;
    private TextView textView;
    private static final  String TAG = "AIDlDemo";
​
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = IMyService.Stub.asInterface(service);
            try {
                int result = mService.add(10086, 10010);
                Log.d(TAG,"---获取到的数据为---: " + result);
                Snackbar.make(textView,"获取到的数据为: " + result,Snackbar.LENGTH_SHORT).show();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
​
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mService = null;
        }
    };
​
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }
​
    private void initService() {
        Intent intent = new Intent();
        intent.setAction("com.example.aidltestdemo.IMyService");
        intent.setPackage("com.example.aidltestdemo");
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }
​
    private void initView() {
        textView = findViewById(R.id.textView);
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                initService();
            }
        });
    }
​
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
    }
}

6.布局文件:

ini 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
​
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="aidl简单测试"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
​
</androidx.constraintlayout.widget.ConstraintLayout>

7.在kts中使用aidl:

如果是使用kts的方式默认创建aidl文件时提示没有配置创建不了,这就和之前有很大的区别。

8.在build.gradle添加配置:

再次创建aidl发现按钮是可以点击的,配置是有效果的。

9.新创建后的aidl文件:

9.1 服务端代码如下:

kotlin 复制代码
package com.example.aidldemo.server
​
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.os.RemoteException
import com.example.aidldemo.aidl.IMyTestServer
​
/**
 * @author: njb
 * @date: 2025/3/19 23:24
 * @desc: 描述
 */
class MyTestService : Service() {
    private val mBinder: IMyTestServer.Stub = object : IMyTestServer.Stub() {
        @Throws(RemoteException::class)
        override fun basicTypes(
            anInt: Int,
            aLong: Long,
            aBoolean: Boolean,
            aFloat: Float,
            aDouble: Double,
            aString: String
        ) {
        }
​
        @Throws(RemoteException::class)
        override fun add(a: Int, b: Int): Int {
            return a + b
        }
    }
​
    override fun onBind(intent: Intent): IBinder? {
        return mBinder
    }
}

9.2 注册服务:

ini 复制代码
<service android:name=".server.MyTestService"
    android:process=":remote"
    android:enabled="true"
    android:exported="true">

9.3 客户端代码:

kotlin 复制代码
package com.example.aidldemo.aidl
​
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.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.example.aidldemo.R
import com.example.aidldemo.server.MyTestService
import com.google.android.material.snackbar.Snackbar
​
​
class MainActivity : AppCompatActivity(){
    private lateinit var mTvBind: TextView
    private val TAG = MainActivity::class.java.name
    private var testServer: IMyTestServer ?=null
​
    private val mConnection: ServiceConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            testServer = IMyTestServer.Stub.asInterface(service)
            try {
                testServer?.let {
                    val result: Int = it.add(188, 288)
                    Log.d(TAG, "--结果为--: $result")
                    Snackbar.make(mTvBind,"--获取到的数据为--: $result",Snackbar.LENGTH_SHORT).show()
                }
​
            } catch (rme: RemoteException) {
                rme.printStackTrace()
            }
        }
​
        override fun onServiceDisconnected(name: ComponentName) {
            try {
                testServer = null
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
        }
    }
​
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mTvBind = findViewById(R.id.tv_bind)
        mTvBind.setOnClickListener{
            val intent = Intent(
                this@MainActivity,
                MyTestService::class.java
            )
            bindService(intent, mConnection, BIND_AUTO_CREATE)
        }
    }
​
    override fun onDestroy() {
        super.onDestroy()
        if(testServer != null && testServer!!.asBinder().isBinderAlive){
            testServer = null
        }
        unbindService(mConnection)
    }
​
}

10.新建一个Book服务:

arduino 复制代码
// ICarManager.aidl
package com.example.aidldemo.aidl;
import com.example.aidldemo.aidl.Book;
import com.example.aidldemo.aidl.IOnNewBookAddListener;
// Declare any non-default types here with import statements

interface IBookManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
    List<Book> getBookList();
    void addNewBook(in Book book);
    void registerListener(IOnNewBookAddListener listener);
    void unregisterListener(IOnNewBookAddListener listener);
}

11.新增Book列表数据接口:

arduino 复制代码
// IOnNewCarAddListener.aidl
package com.example.aidldemo.aidl;
import com.example.aidldemo.aidl.Book;
// Declare any non-default types here with import statements

interface IOnNewBookAddListener {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    void onNewBookAdd(in Book newBook);
}

12.IBook接口:

arduino 复制代码
interface IBook {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

13.注册服务:

ini 复制代码
<service android:name=".server.BookManagerService"
    android:process=":remote"
    android:enabled="true"
    android:exported="true">
</service>

14.客户端Book测试代码:

kotlin 复制代码
package com.example.aidldemo.aidl

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 androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatTextView
import com.example.aidldemo.R
import com.example.aidldemo.databinding.ActivityMainBinding
import com.example.aidldemo.server.BookManagerService

/**
 * @author: njb
 * @date:   2025/3/22 19:27
 * @desc:   描述
 */
class BookTestActivity :AppCompatActivity(){
    private val TAG: String = "TestActivity"
    private var bookManager: IBookManager? = null
    private lateinit var binding:ActivityMainBinding
    private val mConnection: ServiceConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            bookManager = IBookManager.Stub.asInterface(service)
            try {
                bookManager?.let {
                    val books = it.getBookList()
                    if (books != null && books.isNotEmpty()) {
                        Log.d(TAG, " -- onServiceConnected -- book:$books")
                    }
                    it.registerListener(clientListener)
                }

            } catch (e: RemoteException) {
                e.printStackTrace()
            }
        }

        override fun onServiceDisconnected(name: ComponentName) {
            try {
                bookManager?.unregisterListener(clientListener)
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
        }
    }

    private val clientListener: IOnNewBookAddListener = object : IOnNewBookAddListener.Stub() {
        @Throws(RemoteException::class)
        override fun basicTypes(
            anInt: Int,
            aLong: Long,
            aBoolean: Boolean,
            aFloat: Float,
            aDouble: Double,
            aString: String
        ) {
        }

        @Throws(RemoteException::class)
        override fun onNewBookAdd(newBook: Book) {
            Log.d(TAG, " -- onNewBookAdd -- newBook:$newBook")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.tvBind.setOnClickListener{
            val intent = Intent(this@BookTestActivity, BookManagerService::class.java)
            bindService(intent, mConnection, BIND_AUTO_CREATE)
        }
        binding.tvGet.setOnClickListener{
            try {
                val books = bookManager!!.bookList
                if (books != null && books.isNotEmpty()) {
                    Log.d(TAG, " -- onClick -- books:$books")
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        binding.tvAdd.setOnClickListener{
            try {
                bookManager?.let {
                    it.addNewBook(Book(1003, "计算机组成原理"))
                    it.addNewBook(Book(1004, "操作系统"))
                }
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        if (bookManager != null && bookManager!!.asBinder().isBinderAlive) {
            try {
                bookManager?.unregisterListener(clientListener)
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
        }

        unbindService(mConnection)
    }
}

15.普通实体类序列化:

typescript 复制代码
package com.example.aidldemo.aidl;

import android.os.Parcel;
import android.os.Parcelable;

import androidx.annotation.NonNull;

/**
 * @author: njb
 * @date: 2025/3/19 23:22
 * @desc: 描述
 */
public class Book implements Parcelable {

    private int id;
    private String name;

    public Book(int id, String name){
        this.id = id;
        this.name = name;
    }

    protected Book(Parcel in) {
        id = in.readInt();
        name = in.readString();
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

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

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

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
        dest.writeString(name);
    }

    @NonNull
    @Override
    public String toString() {
        return String.format("[id:%d, name:%s]",id, name);
    }

}

16.使用Parcelize序列化:

16.1 在app的build.gradle目录添加插件配置:

scss 复制代码
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    id ("kotlin-parcelize")
}

16.2 实体类使用Parcelize方式:

和普通的方式对比发现代码简洁了很多,而且使用很方便,大大减少了开发人员的工作,当然我们要搞懂其原理才能达到事半功倍的效果,要不然只是会使用,而不知道为啥这么使用及使用他的优势和原理,对自身成长有限.

kotlin 复制代码
@Parcelize
data class Book(
    val id: Int,
    val name:String
):Parcelable

16.3 在libs.versions.toml添加如下插件:

ini 复制代码
jetbrains-kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }

16.4 在项目build.gradle添加配置:

bash 复制代码
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.jetbrains.kotlin.android) apply false
    alias(libs.plugins.jetbrains.kotlin.parcelize) apply false
}

16.5 在app的build.gradle配置:

scss 复制代码
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    alias(libs.plugins.jetbrains.kotlin.parcelize)
}

17.Parcelable的优势如下:

  • 高效性能:相比Serializable,Parcelable在序列化和反序列化过程中更加高效,因为它避免了反射的开销。
  • 跨进程传输:Parcelable适用于在不同进程之间传输数据,例如在Android中,我们可以将Parcelable对象传递给另一个应用程序组件。
  • 灵活性:Parcelable允许我们选择性地序列化对象的某些字段,而不是整个对象,这在某些情况下可以提高性能和减少传输的数据量。

18.遇到问题如下:

18.1 项目编译失败:

创建的Book服务和Book实例不在一个目录导致编译失败

18.2 把两个类放同一目录编译成功:

18.3 添加Parcelize插件依赖报错:

18.4 在build.gradle.kts添加以下配置:

scss 复制代码
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    id ("kotlin-parcelize")
}

18.5 使用Version Catalog方式:

ini 复制代码
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
jetbrains-kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
bash 复制代码
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.jetbrains.kotlin.android) apply false
    alias(libs.plugins.jetbrains.kotlin.parcelize) apply false
}
scss 复制代码
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    alias(libs.plugins.jetbrains.kotlin.parcelize)
}

19.实现的效果如下:

20.日志打印如下:

yaml 复制代码
o                 D  Installing profile for com.example.aidldemo
2025-03-23 20:26:39.912  4722-4722  TestActivity            com.example.aidldemo                 D   -- onServiceConnected -- book:[Book(id=1001, name=Java入门到精通)]
---------------------------- PROCESS STARTED (4797) for package com.example.aidldemo ----------------------------
2025-03-23 20:26:42.911  4722-4741  TestActivity            com.example.aidldemo                 D   -- onNewBookAdd -- newBook:Book(id=1002, name=C++程序设计)
2025-03-23 20:27:24.033  4722-4722  TestActivity            com.example.aidldemo                 D   -- onClick -- books:[Book(id=1001, name=Java入门到精通), Book(id=1002, name=C++程序设计)]
2025-03-23 20:27:39.776  4722-4722  TestActivity            com.example.aidldemo                 D   -- onNewBookAdd -- newBook:Book(id=1003, name=计算机组成原理)
2025-03-23 20:27:39.777  4722-4722  TestActivity            com.example.aidldemo                 D   -- onNewBookAdd -- newBook:Book(id=1004, name=操作系统)

21.总结:

  • 使用kts的方式需要先使用buildFeatures配置aidl.
  • 使用Parcelize序列化数据很简单方便.
  • 要注意创建aidl文件的目录和包名这些在同一目录.
  • 使用Version Catalog方式要主要依赖配置的方式.
  • 感兴趣的小伙伴可以自己尝试一下,在后面的文章中会讲解aidl中如何传输大文件.

22.源码如下:

gitee.com/jackning_ad...

相关推荐
还鮟3 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡4 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi004 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil6 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你6 小时前
Android View的绘制原理详解
android
移动开发者1号9 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号9 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin
ii_best14 小时前
按键精灵支持安卓14、15系统,兼容64位环境开发辅助工具
android
美狐美颜sdk14 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
恋猫de小郭19 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin