Android在kts中简单使用AIDL

发布于:2025-03-24 ⋅ 阅读:(132) ⋅ 点赞:(0)

Android在kts中简单使用AIDL

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

1.在groovy创建aidl文件:

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

在这里插入图片描述

2.生成的aidl文件如下:

在这里插入图片描述

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

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.注册服务:

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

在这里插入图片描述

5.客户端代码:

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.布局文件:

<?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 服务端代码如下:

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 注册服务:

<service android:name=".server.MyTestService"
    android:process=":remote"
    android:enabled="true"
    android:exported="true">

9.3 客户端代码:

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服务:

// 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列表数据接口:

// 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接口:

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.注册服务:

<service android:name=".server.BookManagerService"
    android:process=":remote"
    android:enabled="true"
    android:exported="true">
</service>

14.客户端Book测试代码:

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.普通实体类序列化:

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目录添加插件配置:

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    id ("kotlin-parcelize")
}

在这里插入图片描述

16.2 实体类使用Parcelize方式:

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

@Parcelize
data class Book(
    val id: Int,
    val name:String
):Parcelable

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

jetbrains-kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }

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

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配置:

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添加以下配置:

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    id ("kotlin-parcelize")
}

18.5 使用Version Catalog方式:

[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" }
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.jetbrains.kotlin.android) apply false
    alias(libs.plugins.jetbrains.kotlin.parcelize) apply false
}
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    alias(libs.plugins.jetbrains.kotlin.parcelize)
}

19.实现的效果如下:

在这里插入图片描述

在这里插入图片描述

20.日志打印如下:

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.源码如下:

https://gitee.com/jackning_admin/aidl-test-demo


网站公告

今日签到

点亮在社区的每一天
去签到