AIDL 数据类型详解之 C++ 篇

AIDL 中的数据结构

在上文我们说到 AIDL 中支持的数据类型,这里我们再回顾一下:

在 Java 层,AIDL 支持以下多种数据类型:

  • Java 编程语言中的所有的基本类型(如 int、long、char、boolean 等)
  • String 与 CharSequence
  • List:List 中的所有元素必须是 AIDL 支持的数据类型,生成的方法旨在使用 List 接口,但另一方实际接收的具体类始终是 ArrayList
  • Map:Map 中的所有元素必须是 AIDL 支持的数据类型,不支持泛型 Map(如 Map<String,Integer> 形式的 Map),生成的方法旨在使用 Map 接口,但另一方实际接收的具体类始终是 HashMap
  • 生成的方法旨在使用 Map 接口,但另一方实际接收的具体类始终是 HashMap。
  • Parcelable 类型

Native 层支持类似的数据类型,这些类型与 Java 层的对应关系如下:

Native 示例程序

接下来我们就来写一个演示 AIDL 数据类型的 Native层 示例程序:

首先我们在 device/jelly/rice14/ 目录下创建如下的文件与文件夹

AIDLCppTypeDemo
├── Android.bp
├── com
│ └── yuandaima
│ ├── IHello.aidl
│ ├── Student.aidl
│ ├── Student.cpp
│ └── Student.h
├── HelloClient.cpp
└── HelloServer.cpp
AIDLCppTypeDemo

├── Android.bp

├── com

│   └── yuandaima

│       ├── IHello.aidl
│       ├── Student.aidl
│       ├── Student.cpp
│       └── Student.h
├── HelloClient.cpp
└── HelloServer.cpp
AIDLCppTypeDemo ├── Android.bp ├── com │ └── yuandaima │ ├── IHello.aidl │ ├── Student.aidl │ ├── Student.cpp │ └── Student.h ├── HelloClient.cpp └── HelloServer.cpp

其中的 Student.aidl Student.h Student.cpp 是我们自定义的数据类型,在 AIDL 中自定义数据类型需要继承 Parcelable

//Student.h
#ifndef _COM_YUANDAIMA_STUDENT_H
#define _COM_YUANDAIMA_STUDENT_H
#include <android-base/unique_fd.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <binder/Status.h>
#include <utils/RefBase.h>
#include <vector>
using namespace std;
using namespace android;
namespace com {
namespace yuandaima {
class Student : public Parcelable {
public:
Student();
virtual ~Student();
virtual status_t writeToParcel(Parcel* out) const;
virtual status_t readFromParcel(const Parcel* in);
void setAge(int32_t age);
int32_t getAge();
void setName(String16 name);
String16 getName();
private:
String16 mName;
int32_t mAge;
};
}
}
#endif
// Studeng.cpp
#include "Student.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
using namespace android;
namespace com {
namespace yuandaima {
Student::Student() {
this->setAge(20);
this->setName(String16("jack"));
}
Student::~Student() {
}
status_t Student::writeToParcel(Parcel* out) const {
status_t err;
err = out->writeString16(mName);
if (err != NO_ERROR) {
return err;
}
err = out->writeInt32(mAge);
if (err != NO_ERROR) {
return err;
}
return NO_ERROR;
}
status_t Student::readFromParcel(const Parcel* in) {
status_t err;
err = in->readString16(&mName);
if (err != NO_ERROR) {
return err;
}
err = in->readInt32(&mAge);
if (err != NO_ERROR) {
return err;
}
return NO_ERROR;
}
}}
//Student.h
#ifndef _COM_YUANDAIMA_STUDENT_H
#define _COM_YUANDAIMA_STUDENT_H


#include <android-base/unique_fd.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <binder/Status.h>
#include <utils/RefBase.h>
#include <vector>


using namespace std;
using namespace android;

namespace com {
namespace yuandaima {



    class Student : public Parcelable {
        public:
            Student();
            virtual ~Student();
            
            virtual status_t writeToParcel(Parcel* out) const;
            virtual status_t readFromParcel(const Parcel* in); 

            void setAge(int32_t age);
            int32_t getAge();
            void setName(String16 name);
            String16 getName();
        private:
            String16 mName;
            int32_t mAge;
    };

}
}
#endif

// Studeng.cpp
#include "Student.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>



using namespace std;
using namespace android;

namespace com {
namespace yuandaima {

    Student::Student() {
        this->setAge(20);
        this->setName(String16("jack"));
    }





    Student::~Student() {
    }

    status_t Student::writeToParcel(Parcel* out) const {
        status_t err;
        err = out->writeString16(mName);
        if (err != NO_ERROR) {
            return err;
        }
        err = out->writeInt32(mAge);
        if (err != NO_ERROR) {
            return err;
        }
        return NO_ERROR;
    }

    status_t Student::readFromParcel(const Parcel* in) {
        status_t err;
        err = in->readString16(&mName);
        if (err != NO_ERROR) {
            return err;
        }
        err = in->readInt32(&mAge);
        if (err != NO_ERROR) {
            return err;
        }

        return NO_ERROR;
    }

}}
//Student.h #ifndef _COM_YUANDAIMA_STUDENT_H #define _COM_YUANDAIMA_STUDENT_H #include <android-base/unique_fd.h> #include <binder/Parcel.h> #include <binder/Parcelable.h> #include <binder/Status.h> #include <utils/RefBase.h> #include <vector> using namespace std; using namespace android; namespace com { namespace yuandaima { class Student : public Parcelable { public: Student(); virtual ~Student(); virtual status_t writeToParcel(Parcel* out) const; virtual status_t readFromParcel(const Parcel* in); void setAge(int32_t age); int32_t getAge(); void setName(String16 name); String16 getName(); private: String16 mName; int32_t mAge; }; } } #endif // Studeng.cpp #include "Student.h" #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> using namespace std; using namespace android; namespace com { namespace yuandaima { Student::Student() { this->setAge(20); this->setName(String16("jack")); } Student::~Student() { } status_t Student::writeToParcel(Parcel* out) const { status_t err; err = out->writeString16(mName); if (err != NO_ERROR) { return err; } err = out->writeInt32(mAge); if (err != NO_ERROR) { return err; } return NO_ERROR; } status_t Student::readFromParcel(const Parcel* in) { status_t err; err = in->readString16(&mName); if (err != NO_ERROR) { return err; } err = in->readInt32(&mAge); if (err != NO_ERROR) { return err; } return NO_ERROR; } }}

在 aidl 中我们只需要简单声明即可:

//Student.aidl
package com.yuandaima;
parcelable Student cpp_header "com/yuandaima/Student.h";
//Student.aidl
package com.yuandaima;

parcelable Student cpp_header "com/yuandaima/Student.h";
//Student.aidl package com.yuandaima; parcelable Student cpp_header "com/yuandaima/Student.h";

与 Java 不同的是,我们需要指定对应的头文件。

IHelloService.aidl 中声明了我们 binder 服务的对外接口:

package com.yuandaima;
import com.yuandaima.Student;
interface IHello
{
void hello();
int sum(int x, int y);
int printList(in List<String> strs);
int printMap(in Map maps);
int printStudent(in Student student);
}
package com.yuandaima;




import com.yuandaima.Student;


interface IHello
{
    void hello();
    int sum(int x, int y);
    int printList(in List<String> strs);
  int printMap(in Map maps);
    int printStudent(in Student student);
}
package com.yuandaima; import com.yuandaima.Student; interface IHello { void hello(); int sum(int x, int y); int printList(in List<String> strs); int printMap(in Map maps); int printStudent(in Student student); }

接着我们编译 aidl 文件:

# 源码目录下
source build/envsetup.sh
# 选择合适的 product
lunch
# 进入项目目录下
# -I 用于指定我们的在哪里查找 import
aidl-cpp -I . com/yuandaima/IHello.aidl ./ ./IHello.cpp
# 源码目录下
source build/envsetup.sh
# 选择合适的 product
lunch


# 进入项目目录下
# -I 用于指定我们的在哪里查找 import
aidl-cpp -I . com/yuandaima/IHello.aidl ./ ./IHello.cpp
# 源码目录下 source build/envsetup.sh # 选择合适的 product lunch # 进入项目目录下 # -I 用于指定我们的在哪里查找 import aidl-cpp -I . com/yuandaima/IHello.aidl ./ ./IHello.cpp

编译后,生成了对应的 h 和 cpp 文件,整个项目结构如下:

AIDLCppTypeDemo
├── Android.bp
├── com
│ └── yuandaima
│ ├── BnHello.h
│ ├── BpHello.h
│ ├── IHello.aidl
│ ├── IHello.h
│ ├── Student.aidl
│ ├── Student.cpp
│ └── Student.h
├── HelloClient.cpp
├── HelloServer.cpp
└── IHello.cpp
AIDLCppTypeDemo

├── Android.bp

├── com

│   └── yuandaima

│       ├── BnHello.h
│       ├── BpHello.h
│       ├── IHello.aidl
│       ├── IHello.h
│       ├── Student.aidl
│       ├── Student.cpp
│       └── Student.h
├── HelloClient.cpp
├── HelloServer.cpp
└── IHello.cpp
AIDLCppTypeDemo ├── Android.bp ├── com │ └── yuandaima │ ├── BnHello.h │ ├── BpHello.h │ ├── IHello.aidl │ ├── IHello.h │ ├── Student.aidl │ ├── Student.cpp │ └── Student.h ├── HelloClient.cpp ├── HelloServer.cpp └── IHello.cpp

接着服务端类和服务端主程序 HelloServer.cpp

#define LOG_TAG "aidl_cpp"
#include <stdlib.h>
#include <utils/RefBase.h>
#include <utils/Log.h>
#include <binder/TextOutput.h>
#include <binder/IInterface.h>
#include <binder/IBinder.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include "com/yuandaima/IHello.h"
#include "com/yuandaima/BnHello.h"
using namespace android;
class IHelloServer : public com::yuandaima::BnHello
{
public:
binder::Status hello() override
{
ALOGI("hello");
return binder::Status();
}
binder::Status sum(int32_t v1, int32_t v2, int32_t *_aidl_return) override
{
ALOGI("server: sum: %d + %d", v1, v2);
*_aidl_return = v1 + v2;
return binder::Status();
}
binder::Status printList(const ::std::vector<::android::String16>& strs, int32_t* _aidl_return) override
{
for (const auto & str : strs)
{
ALOGI("%s", String8(str).c_str());
}
*_aidl_return = 1;
return binder::Status();
}
binder::Status printMap(const ::android::binder::Map& maps, int32_t* _aidl_return) override
{
for(auto it : maps){
std::string val;
it.second.getString(&val);
ALOGI("key is %s, value is %s", it.first.c_str(), val.c_str());
}
*_aidl_return = 1;
return binder::Status();
}
};
int main(int argc, char const *argv[])
{
defaultServiceManager()->addService(String16("IHello"), new IHelloServer());
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return 0;
}
#define LOG_TAG "aidl_cpp"




#include <stdlib.h>
#include <utils/RefBase.h>
#include <utils/Log.h>
#include <binder/TextOutput.h>
#include <binder/IInterface.h>
#include <binder/IBinder.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>


#include "com/yuandaima/IHello.h"
#include "com/yuandaima/BnHello.h"


using namespace android;



class IHelloServer : public com::yuandaima::BnHello

{

public:

    binder::Status hello() override
    {

        ALOGI("hello");
        return binder::Status();
    }



    binder::Status sum(int32_t v1, int32_t v2, int32_t *_aidl_return) override
    {

        ALOGI("server: sum: %d + %d", v1, v2);
        *_aidl_return = v1 + v2;

        return binder::Status();
    }



    binder::Status printList(const ::std::vector<::android::String16>& strs, int32_t* _aidl_return) override
    {

        for (const auto & str : strs)
        {

            ALOGI("%s", String8(str).c_str());
        }



        *_aidl_return = 1;

        return binder::Status();
    }




    binder::Status printMap(const ::android::binder::Map& maps, int32_t* _aidl_return) override
    {

        for(auto it : maps){
            std::string val;

            it.second.getString(&val);
            ALOGI("key is %s, value is %s", it.first.c_str(), val.c_str());
        }

        *_aidl_return = 1;

        return binder::Status();
    }





};



int main(int argc, char const *argv[])
{

    defaultServiceManager()->addService(String16("IHello"), new IHelloServer());
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    return 0;

}
#define LOG_TAG "aidl_cpp" #include <stdlib.h> #include <utils/RefBase.h> #include <utils/Log.h> #include <binder/TextOutput.h> #include <binder/IInterface.h> #include <binder/IBinder.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> #include "com/yuandaima/IHello.h" #include "com/yuandaima/BnHello.h" using namespace android; class IHelloServer : public com::yuandaima::BnHello { public: binder::Status hello() override { ALOGI("hello"); return binder::Status(); } binder::Status sum(int32_t v1, int32_t v2, int32_t *_aidl_return) override { ALOGI("server: sum: %d + %d", v1, v2); *_aidl_return = v1 + v2; return binder::Status(); } binder::Status printList(const ::std::vector<::android::String16>& strs, int32_t* _aidl_return) override { for (const auto & str : strs) { ALOGI("%s", String8(str).c_str()); } *_aidl_return = 1; return binder::Status(); } binder::Status printMap(const ::android::binder::Map& maps, int32_t* _aidl_return) override { for(auto it : maps){ std::string val; it.second.getString(&val); ALOGI("key is %s, value is %s", it.first.c_str(), val.c_str()); } *_aidl_return = 1; return binder::Status(); } }; int main(int argc, char const *argv[]) { defaultServiceManager()->addService(String16("IHello"), new IHelloServer()); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); return 0; }

接着完成客户端程序 HelloClient.cpp :

#define LOG_TAG "aidl_cpp"
#include <stdlib.h>
#include <utils/RefBase.h>
#include <utils/Log.h>
#include <binder/TextOutput.h>
#include <binder/IInterface.h>
#include <binder/IBinder.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include "com/yuandaima/IHello.h"
#include "com/yuandaima/BnHello.h"
using namespace android;
class IHelloServer : public com::yuandaima::BnHello
{
public:
binder::Status hello() override
{
ALOGI("hello");
return binder::Status();
}
binder::Status sum(int32_t v1, int32_t v2, int32_t *_aidl_return) override
{
ALOGI("server: sum: %d + %d", v1, v2);
*_aidl_return = v1 + v2;
return binder::Status();
}
binder::Status printList(const ::std::vector<::android::String16>& strs, int32_t* _aidl_return) override
{
for (const auto & str : strs)
{
ALOGI("%s", String8(str).c_str());
}
*_aidl_return = 1;
return binder::Status();
}
binder::Status printMap(const ::android::binder::Map& maps, int32_t* _aidl_return) override
{
for(auto it : maps){
std::string val;
it.second.getString(&val);
ALOGI("key is %s, value is %s", it.first.c_str(), val.c_str());
}
*_aidl_return = 1;
return binder::Status();
}
};
int main(int argc, char const *argv[])
{
defaultServiceManager()->addService(String16("IHello"), new IHelloServer());
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return 0;
}
#define LOG_TAG "aidl_cpp"




#include <stdlib.h>
#include <utils/RefBase.h>
#include <utils/Log.h>
#include <binder/TextOutput.h>
#include <binder/IInterface.h>
#include <binder/IBinder.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>


#include "com/yuandaima/IHello.h"
#include "com/yuandaima/BnHello.h"


using namespace android;



class IHelloServer : public com::yuandaima::BnHello

{

public:

    binder::Status hello() override
    {

        ALOGI("hello");
        return binder::Status();
    }



    binder::Status sum(int32_t v1, int32_t v2, int32_t *_aidl_return) override
    {

        ALOGI("server: sum: %d + %d", v1, v2);
        *_aidl_return = v1 + v2;

        return binder::Status();
    }



    binder::Status printList(const ::std::vector<::android::String16>& strs, int32_t* _aidl_return) override
    {

        for (const auto & str : strs)
        {

            ALOGI("%s", String8(str).c_str());
        }



        *_aidl_return = 1;

        return binder::Status();
    }




    binder::Status printMap(const ::android::binder::Map& maps, int32_t* _aidl_return) override
    {

        for(auto it : maps){
            std::string val;

            it.second.getString(&val);
            ALOGI("key is %s, value is %s", it.first.c_str(), val.c_str());
        }

        *_aidl_return = 1;

        return binder::Status();
    }





};



int main(int argc, char const *argv[])
{

    defaultServiceManager()->addService(String16("IHello"), new IHelloServer());
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    return 0;

}
#define LOG_TAG "aidl_cpp" #include <stdlib.h> #include <utils/RefBase.h> #include <utils/Log.h> #include <binder/TextOutput.h> #include <binder/IInterface.h> #include <binder/IBinder.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> #include "com/yuandaima/IHello.h" #include "com/yuandaima/BnHello.h" using namespace android; class IHelloServer : public com::yuandaima::BnHello { public: binder::Status hello() override { ALOGI("hello"); return binder::Status(); } binder::Status sum(int32_t v1, int32_t v2, int32_t *_aidl_return) override { ALOGI("server: sum: %d + %d", v1, v2); *_aidl_return = v1 + v2; return binder::Status(); } binder::Status printList(const ::std::vector<::android::String16>& strs, int32_t* _aidl_return) override { for (const auto & str : strs) { ALOGI("%s", String8(str).c_str()); } *_aidl_return = 1; return binder::Status(); } binder::Status printMap(const ::android::binder::Map& maps, int32_t* _aidl_return) override { for(auto it : maps){ std::string val; it.second.getString(&val); ALOGI("key is %s, value is %s", it.first.c_str(), val.c_str()); } *_aidl_return = 1; return binder::Status(); } }; int main(int argc, char const *argv[]) { defaultServiceManager()->addService(String16("IHello"), new IHelloServer()); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); return 0; }

最后编译测试:

# 项目目录下执行单编
mm
# 回到系统源码目录
adb push out/target/product/rice14/system/bin/IHelloTypeServer /data/local/tmp
adb push out/target/product/rice14/system/bin/IHelloTypeClient /data/local/tmp
# 进入模拟器 shell 环境
adb shell
cd /data/local/tmp
# 执行服务端
./IHelloTypeServer &
# 执行客户端
./IHelloTypeClient
# 项目目录下执行单编
mm
# 回到系统源码目录
adb push out/target/product/rice14/system/bin/IHelloTypeServer /data/local/tmp


adb push out/target/product/rice14/system/bin/IHelloTypeClient /data/local/tmp

# 进入模拟器 shell 环境
adb shell
cd /data/local/tmp


# 执行服务端
./IHelloTypeServer & 
# 执行客户端
./IHelloTypeClient 
# 项目目录下执行单编 mm # 回到系统源码目录 adb push out/target/product/rice14/system/bin/IHelloTypeServer /data/local/tmp adb push out/target/product/rice14/system/bin/IHelloTypeClient /data/local/tmp # 进入模拟器 shell 环境 adb shell cd /data/local/tmp # 执行服务端 ./IHelloTypeServer & # 执行客户端 ./IHelloTypeClient

接着查看log:

logcat | grep aidl_cpp
08-07 10:57:44.372 2824 2825 I aidl_cpp: hello
08-07 10:57:44.372 2824 2825 I aidl_cpp: server: sum: 1 + 2
08-07 10:57:44.373 2824 2825 I aidl_cpp: Hello Binder
08-07 10:57:44.373 2824 2825 I aidl_cpp: key is test, value is
08-07 12:04:57.181 10985 10986 I aidl_cpp: hello
08-07 12:04:57.182 10985 10986 I aidl_cpp: server: sum: 1 + 2
08-07 12:04:57.182 10985 10986 I aidl_cpp: Hello Binder
08-07 12:04:57.182 10985 10986 I aidl_cpp: key is test, value is
08-07 12:07:48.053 11334 11335 I aidl_cpp: hello
08-07 12:07:48.053 11334 11335 I aidl_cpp: server: sum: 1 + 2
08-07 12:07:48.054 11334 11335 I aidl_cpp: Hello Binder
08-07 12:07:48.054 11334 11335 I aidl_cpp: key is test, value is
08-07 12:07:48.054 11334 11335 I aidl_cpp: name is jack, age is 20
logcat | grep aidl_cpp




08-07 10:57:44.372  2824  2825 I aidl_cpp: hello
08-07 10:57:44.372  2824  2825 I aidl_cpp: server: sum: 1 + 2
08-07 10:57:44.373  2824  2825 I aidl_cpp: Hello Binder
08-07 10:57:44.373  2824  2825 I aidl_cpp: key is test, value is 
08-07 12:04:57.181 10985 10986 I aidl_cpp: hello
08-07 12:04:57.182 10985 10986 I aidl_cpp: server: sum: 1 + 2
08-07 12:04:57.182 10985 10986 I aidl_cpp: Hello Binder
08-07 12:04:57.182 10985 10986 I aidl_cpp: key is test, value is 
08-07 12:07:48.053 11334 11335 I aidl_cpp: hello
08-07 12:07:48.053 11334 11335 I aidl_cpp: server: sum: 1 + 2
08-07 12:07:48.054 11334 11335 I aidl_cpp: Hello Binder
08-07 12:07:48.054 11334 11335 I aidl_cpp: key is test, value is 
08-07 12:07:48.054 11334 11335 I aidl_cpp: name is jack, age is 20
logcat | grep aidl_cpp 08-07 10:57:44.372 2824 2825 I aidl_cpp: hello 08-07 10:57:44.372 2824 2825 I aidl_cpp: server: sum: 1 + 2 08-07 10:57:44.373 2824 2825 I aidl_cpp: Hello Binder 08-07 10:57:44.373 2824 2825 I aidl_cpp: key is test, value is 08-07 12:04:57.181 10985 10986 I aidl_cpp: hello 08-07 12:04:57.182 10985 10986 I aidl_cpp: server: sum: 1 + 2 08-07 12:04:57.182 10985 10986 I aidl_cpp: Hello Binder 08-07 12:04:57.182 10985 10986 I aidl_cpp: key is test, value is 08-07 12:07:48.053 11334 11335 I aidl_cpp: hello 08-07 12:07:48.053 11334 11335 I aidl_cpp: server: sum: 1 + 2 08-07 12:07:48.054 11334 11335 I aidl_cpp: Hello Binder 08-07 12:07:48.054 11334 11335 I aidl_cpp: key is test, value is 08-07 12:07:48.054 11334 11335 I aidl_cpp: name is jack, age is 20

从 log 可以看出我们的程序指针成功了。

参考资料

关于

我叫阿豪,2015 年本科毕业于国防科学技术大学指挥信息系统专业,毕业后从事信息化装备的研发工作,主要研究方向是 Android Framework 与 Linux Kernel。

如果你对 Android Framework 感兴趣或者正在学习 Android Framework,可以关注我的微信公众号和抖音,我会持续分享我的学习经验,帮助正在学习的你少走一些弯路。学习过程中如果你有疑问或者你的经验想要分享给大家可以添加我的微信,我拉你进技术交流群。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYMg1MP7' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片