Android

利用AIDL实现进程间通讯

2020-12-22  本文已影响0人  超人TIGA

这里只是简单地教大家如何初步认识,如何利用AIDL来实现进程间通讯的代码编写,并非深入了解源码。

实现服务端

1、新建实体类,实现Parcelable接口
package com.tonydash.aidl.bean

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

class Person() : Parcelable {

    var name: String = ""
    var age: Int = 0

    constructor(name: String, age: Int) : this() {
        this.name = name
        this.age = age
    }

    constructor(parcel: Parcel) : this() {
        name = parcel.readString().toString()
        age = parcel.readInt()
    }

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(name)
        parcel.writeInt(age)
    }

    fun readFromParcel(dest: Parcel) {
        name = dest.readString().toString()
        age = dest.readInt()
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<Person> {
        override fun createFromParcel(parcel: Parcel): Person {
            return Person(parcel)
        }

        override fun newArray(size: Int): Array<Person?> {
            return arrayOfNulls(size)
        }
    }
}

大部分都是自动生成的代码,如果有自己需要的构造函数或者方法需要增加,自行增加就好。
这里为什么一定要实现Parcelable接口?
因为binder默认支持的就是8大基础类型,如果要传自己的对象类型的数据,就要实现Parcelable,使得这个对象是可以被打包的数据,从而进行数据传递。

2、新建aidl文件
image.png 先在main文件夹下面新建一个aidl文件夹,然后在aidl文件夹中新建aidl文件,创建成功后就会自动帮你加上包路径,不需要自己建立。
这里需要注意的是,IPerson.aidl的包路径,是com.tonydash.aidl,所以对应下方java的路径,也只是com.tonydash.aidl,但是下方的Person类,却放在了bean文件夹中,这样默认的IPerson.aidl是找不到Person类的,所以我们需要改一下
//默认的包名是com.tonydash.aidl
//但是由于我的实体类Person,存放的位置我放在了com.tonydash.aidl.bean中
//所以这里也需要修改,不然无法关联到
package com.tonydash.aidl.bean;

//声明Person
parcelable Person;

interface IPersonAidlInterface {
    void addPersonIn(in Person person);
}
3、新建一个Service
package com.tonydash.aidl

import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import com.tonydash.aidl.bean.IPersonAidlInterface
import com.tonydash.aidl.bean.Person

class PersonService:Service() {

    private val personList = ArrayList<Person>();

    private val personBinder:IPersonAidlInterface.Stub =object : IPersonAidlInterface.Stub(){
        override fun addPersonIn(person: Person?) {
            Log.d("TAG","我是服务器,我收到一个客户端发过来的人:name = ${person?.name}")
        }

    }

    override fun onCreate() {
        super.onCreate()
        Log.d("PersonService","PersonService onCreate")
//        for (i in 1 .. 5){
//            personList.add(Person("name$i",i))
//        }
    }

    override fun onBind(intent: Intent?): IBinder {
        return personBinder
    }
}
4、MainActivity启动Service
package com.tonydash.aidl

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        startService(Intent(this,PersonService::class.java))
    }
}
5、注册service
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tonydash.aidl">

    <!--自定义权限,name 随便写,对应服务的权限就好-->
    <permission
        android:name="com.permission.aidl"
        android:protectionLevel="normal" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AIDL">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!--
        exported = "true":可让其他 app 使用服务
        permission: 若 exported = "true",则需要给这个服务一个自定义的权限
        使用方若调用该服务就要声明这个权限,否则报错
        -->
        <service android:name=".PersonService"
            android:exported="true"
            android:permission="com.permission.aidl">
            <intent-filter>
                <!--定义action给客户端隐式调用,可随便写-->
                <action android:name="com.tiga.action.aidl" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>
    </application>

</manifest>

注册里会定义一个权限,是给其他APP调用时用的。到这里,服务端就完成了。

实现客户端

1、新建一个model或者project都可以
2、增加服务端定义的权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tonydash.client">

    <permission android:name="com.permission.aidl"/>
    <uses-permission android:name="com.permission.aidl"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AIDL">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
3、把服务端中的IPerson.aidl和Person类,连包路径一起都复制到client中。 image.png

一定要完全一致,不然会有问题

4、编写发送消息的逻辑
package com.tonydash.client

import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.tonydash.aidl.bean.IPersonAidlInterface
import com.tonydash.aidl.bean.Person


class MainActivity : AppCompatActivity() {

    var tvName:TextView? = null
    var iPerson:IPersonAidlInterface? = null

    val connection:ServiceConnection = object : ServiceConnection{
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            iPerson = IPersonAidlInterface.Stub.asInterface(service)
        }

        override fun onServiceDisconnected(name: ComponentName?) {

        }

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        tvName = findViewById(R.id.tvName)
        tvName?.setOnClickListener {
            val person = Person()
            person.name = "奔波儿灞"
            person.age = 500
            iPerson?.addPersonIn(person)
        }
        connectService()
    }

    private fun connectService() {
        val intent = Intent()
        //服务端包名
        intent.setPackage("com.tonydash.aidl")
        //服务端设置的 action
        intent.action = "com.tiga.action.aidl"
        bindService(intent, connection, BIND_AUTO_CREATE)
    }
}
测试步骤

先把服务端APP启动起来,然后退到后台;
再把客户端APP启动起来,然后点击中间的TextView;

之后就可以在Logcat中看到消息了。 image.png

Demo地址:https://github.com/TonyDash/AIDL.git

上一篇下一篇

猜你喜欢

热点阅读