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...

相关推荐
故事与他6451 小时前
Thinkphp(TP)框架漏洞攻略
android·服务器·网络·中间件·tomcat
每次的天空3 小时前
项目总结:GetX + Kotlin 协程实现跨端音乐播放实时同步
android·开发语言·kotlin
m0_748233175 小时前
SQL之delete、truncate和drop区别
android·数据库·sql
CYRUS_STUDIO6 小时前
OLLVM 增加 C&C++ 字符串加密功能
android·c++·安全
帅次8 小时前
Flutter 输入组件 Radio 详解
android·flutter·ios·kotlin·android studio
&有梦想的咸鱼&9 小时前
Android Compose 框架的状态与 ViewModel 的协同(collectAsState)深入剖析(二十一)
android
开开心心就好9 小时前
高效PDF翻译解决方案:多引擎支持+格式零丢失
android·java·网络协议·tcp/ip·macos·智能手机·pdf
路上阡陌10 小时前
docker 安装部署 canal
android·adb·docker
&有梦想的咸鱼&11 小时前
入剖析 Android Compose 框架的关键帧动画(keyframes、Animatable)(二十三)
android
thinkMoreAndDoMore11 小时前
android音频概念解析
android·音视频