Android 6.0 완전한 native service
이전 블 로그 '안 드 로 이 드 6.0 완전한 시스템 서비스 추가 방법 (app - framework - kernel)'http://www.cnblogs.com/hackfun/p/7418902.html시스템 서 비 스 를 추가 하 는 방법 을 소개 했다. 클 라 이언 트 와 서버 는 모두 JAVA 를 바탕 으로 이 루어 진 OpersysService 이다.진일보 한 학습 을 통 해 저 는 C + + 를 어떻게 사용 하여 같은 기능 을 실현 하 는 시스템 서비스 hfnativeservice 를 보 여 드 리 겠 습 니 다.OpersysService 를 호 환 하기 위해 Opersys - Service 서버 의 HAL 과 driver 를 보류 하고 hfnativeservice 에서 사용 할 수 있 도록 합 니 다. 즉, OpersysService 와 hfnativeservice 라 는 두 서 비 스 는 모두 같은 HAL 과 driver 를 사용 합 니 다.그 중에서 hfnativeservice 는 서버 사망 알림 체 제 를 추가 했다. 즉, hfnative - service 의 서버 프로 세 스 가 죽 었 을 때 클 라 이언 트 는 이 통 지 를 받 고 해당 하 는 청 소 를 할 것 이다.
주로 다음 과 같은 몇 가지 절 차 를 중심 으로 완전한 C + + 시스템 서 비 스 를 추가 합 니 다. (A) HAL 과 Driver (B) 를 추가 하고 서비스 인 터 페 이 스 를 추가 하 며 동적 라 이브 러 리 (C) 를 생 성하 고 서버 (D) 등록 서버 (E) 를 추가 하여 클 라 이언 트 (F) 테스트 를 추가 합 니 다.
(A) HAL 과 Driver 추가 이 부분 은 이전 블 로그 '안 드 로 이 드 6.0 어떻게 완전한 시스템 서비스 (app - framework - kernel) 를 추가 합 니까?' 를 참고 합 니 다.
(A) circular - char 드라이브 추가 (B) opersyshw 추가qemu HAL (B) 서비스 인터페이스 추가, 동적 라 이브 러 리 생 성 대외 적 으로 서버 나 클 라 이언 트 의 인터페이스 만 제공 하기 위해 클 라 이언 트 와 서버 간 의 통신 실현 디 테 일 을 함께 놓 고 동적 라 이브 러 리 so 파일 을 생 성 합 니 다. 서버 와 클 라 이언 트 가 사용 할 때 이 so 를 불 러 오 면 됩 니 다.IHfNative Service. cpp 는 클 라 이언 트 와 서버 에 같은 인 터 페 이 스 를 제공 하고 proxy 와 native 간 Binder 통신 디 테 일 을 실현 합 니 다.HfNativeManager. cpp 는 IHfNativeService. cpp 가 제공 하 는 인터페이스 에 따라 클 라 이언 트 의 조작 세부 사항, 예 를 들 어 서비스 획득, 등록 사망 알림 등 을 숨 겼 습 니 다.
관련 헤더 파일:
frameworks/native/include/hfnative/HfNativeManager.h
1 #ifndef ANDROID_HACKFUN_HACKFUN_NATIVE_SERVICE_H
2 #define ANDROID_HACKFUN_HACKFUN_NATIVE_SERVICE_H
3
4 #include
5 #include
6
7 #include
8
9 #include
10 #include
11 #include
12 #include
13
14 namespace android {
15 // ---------------------------------------------------------------------------
16
17 class HfNativeManager : public Singleton
18 {
19 public:
20 HfNativeManager();
21 ~HfNativeManager();
22
23 int init_hfnative(void);
24 void deinit_hfnative(void);
25 int read_queue(char *buff, int len);
26 int write_queue(char *buff, int len);
27 int test_hfnative(int value);
28
29 status_t assertState();
30 bool checkService() const;
31 void resetServiceStatus();
32
33 private:
34 bool isDied;
35 // DeathRecipient interface
36 void hfNativeServiceDied();
37
38 mutable sp mHfNativeServer;
39 mutable sp<:deathrecipient> mDeathObserver;
40 };
41
42 }; // namespace android
43
44 #endif // ANDROID_HACKFUN_HACKFUN_NATIVE_SERVICE_H
frameworks/native/include/hfnative/IHfNativeService.h
1 #ifndef ANDROID_HACKFUN_HACKFUN_COMPOSER_CLIENT_H
2 #define ANDROID_HACKFUN_HACKFUN_COMPOSER_CLIENT_H
3
4 #include
5 #include
6
7 #include
8 #include
9
10 #include
11
12 namespace android {
13 // ----------------------------------------------------------------------------
14
15 class IHfNativeService : public IInterface
16 {
17 public:
18 DECLARE_META_INTERFACE(HfNativeService);
19
20 virtual int init_native(void) = 0;
21 virtual void finalize_native(void) = 0;
22 virtual int read_native(char *Buff, int Len) = 0;
23 virtual int write_native(char *Buff, int Len) = 0;
24 virtual int test_native(int value) = 0;
25 };
26
27 // ----------------------------------------------------------------------------
28
29 class BnHfNativeService: public BnInterface {
30 public:
31 virtual status_t onTransact(uint32_t code, const Parcel& data,
32 Parcel* reply, uint32_t flags = 0);
33 };
34
35 // ----------------------------------------------------------------------------
36
37 }; // namespace android
38
39 #endif // ANDROID_HACKFUN_HACKFUN_COMPOSER_CLIENT_H
원본 파일:
frameworks/native/libs/hfnative/IHfNativeService.cpp
1 #define LOG_TAG "HfNativeService"
2
3 #include
4 #include
5 #include
6 #include
7
8 #include
9 #include
10 #include
11 #include
12 #include
13
14 namespace android {
15
16 enum {
17 INIT_NATIVE = IBinder::FIRST_CALL_TRANSACTION,
18 FINALIZE_NATIVE,
19 READ_NATIVE,
20 WRITE_NATIVE,
21 TEST_NATIVE
22 };
23
24 class BpHfNativeService : public BpInterface
25 {
26 public:
27 BpHfNativeService(const sp& impl)
28 : BpInterface(impl)
29 {
30 }
31
32 int init_native(void)
33 {
34 Parcel data, reply;
35
36 data.writeInterfaceToken(IHfNativeService::getInterfaceDescriptor());
37 remote()->transact(INIT_NATIVE, data, &reply);
38
39 return (int)reply.readInt32();
40 }
41
42 void finalize_native(void)
43 {
44 Parcel data, reply;
45
46 data.writeInterfaceToken(IHfNativeService::getInterfaceDescriptor());
47 remote()->transact(FINALIZE_NATIVE, data, &reply);
48 }
49
50 int read_native(char *Buff, int Len)
51 {
52 Parcel data, reply;
53
54 data.writeInterfaceToken(IHfNativeService::getInterfaceDescriptor());
55 data.writeInt32(Len);
56 remote()->transact(READ_NATIVE, data, &reply);
57 reply.read((void *)Buff, (size_t)Len);
58 return (int) reply.readInt32();
59 }
60
61
62 int write_native(char *Buff, int Len)
63 {
64 Parcel data, reply;
65
66 data.writeInterfaceToken(IHfNativeService::getInterfaceDescriptor());
67 data.writeInt32(Len);
68 data.write((const void *)Buff, (size_t)Len);
69 remote()->transact(WRITE_NATIVE, data, &reply);
70 return (int) reply.readInt32();
71 }
72
73 int test_native(int value)
74 {
75 Parcel data, reply;
76
77 data.writeInterfaceToken(IHfNativeService::getInterfaceDescriptor());
78 data.writeInt32(value);
79 remote()->transact(TEST_NATIVE, data, &reply);
80 return (int) reply.readInt32();
81 }
82 };
83
84 IMPLEMENT_META_INTERFACE(HfNativeService, "android.hfnative.HfNativeService");
85
86 status_t BnHfNativeService::onTransact(
87 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
88 {
89 char *buff;
90 int len, retval;
91 status_t status;
92
93 switch(code) {
94 case INIT_NATIVE:
95 CHECK_INTERFACE(IHfNativeService, data, reply);
96 retval = init_native();
97 reply->writeInt32(retval);
98 return NO_ERROR;
99
100 case FINALIZE_NATIVE:
101 CHECK_INTERFACE(IHfNativeService, data, reply);
102 finalize_native();
103 return NO_ERROR;
104
105 case READ_NATIVE: {
106 CHECK_INTERFACE(IHfNativeService, data, reply);
107 len = data.readInt32();
108 buff = (char *)malloc(len);
109 retval = read_native(buff, len);
110 reply->write((const void *)buff, (size_t)len);
111 free(buff);
112 reply->writeInt32(retval);
113 return NO_ERROR;
114 } break;
115
116 case WRITE_NATIVE: {
117 CHECK_INTERFACE(IHfNativeService, data, reply);
118 len = data.readInt32();
119 buff = (char *)malloc(len);
120 status = data.read((void *)buff, (size_t)len);
121 retval = write_native(buff, len);
122 free(buff);
123 reply->writeInt32(retval);
124 return NO_ERROR;
125 } break;
126
127 case TEST_NATIVE:
128 CHECK_INTERFACE(IHfNativeService, data, reply);
129 retval = test_native(data.readInt32());
130 reply->writeInt32(retval);
131 return NO_ERROR;
132
133 default:
134 return BBinder::onTransact(code, data, reply, flags);
135 }
136 }
137
138 }; // namespace android
frameworks/native/libs/hfnative/HfNativeManager.cpp
1 #define LOG_TAG "HfNative"
2
3 #include
4 #include
5
6 #include
7 #include
8 #include
9
10 #include
11 #include
12
13 #include
14 #include
15
16 // ----------------------------------------------------------------------------
17 namespace android {
18 // ----------------------------------------------------------------------------
19
20 HfNativeManager::HfNativeManager() : isDied(false)
21 {
22
23 }
24
25 HfNativeManager::~HfNativeManager()
26 {
27 }
28
29 void HfNativeManager::hfNativeServiceDied()
30 {
31 isDied = true;
32 mHfNativeServer.clear();
33 }
34
35 status_t HfNativeManager::assertState() {
36 if (mHfNativeServer == NULL) {
37 // try for one second
38 const String16 name("hfnativeservice");
39 for (int i=0 ; i<4 ; i++) {
40 status_t err = getService(name, &mHfNativeServer);
41 if (err == NAME_NOT_FOUND) {
42 usleep(250000);
43 continue;
44 }
45 if (err != NO_ERROR) {
46 return err;
47 }
48 break;
49 }
50
51 init_hfnative();
52 ALOGI("test hfnativeservice [%d]", test_hfnative(20));
53
54
55 class DeathObserver : public IBinder::DeathRecipient {
56 HfNativeManager& mHfNativeManager;
57 virtual void binderDied(const wp& who) {
58 ALOGW("hfnativeservice died [%p]", who.unsafe_get());
59 mHfNativeManager.hfNativeServiceDied();
60 }
61 public:
62 DeathObserver(HfNativeManager& mgr) : mHfNativeManager(mgr) { }
63 };
64
65 mDeathObserver = new DeathObserver(*const_cast(this));
66 mHfNativeServer->asBinder(mHfNativeServer)->linkToDeath(mDeathObserver);
67 }
68
69 return NO_ERROR;
70 }
71
72 bool HfNativeManager::checkService() const
73 {
74 return isDied? true:false;
75 }
76
77 void HfNativeManager::resetServiceStatus()
78 {
79 isDied = false;
80 }
81
82
83 int HfNativeManager::init_hfnative(void)
84 {
85 return mHfNativeServer->init_native();
86 }
87
88 void HfNativeManager::deinit_hfnative(void)
89 {
90 mHfNativeServer->finalize_native();
91 }
92
93 int HfNativeManager::read_queue(char *buff, int len)
94 {
95 return mHfNativeServer->read_native(buff,len);
96 }
97
98 int HfNativeManager::write_queue(char *buff, int len)
99 {
100 return mHfNativeServer->write_native(buff,len);
101 }
102
103 int HfNativeManager::test_hfnative(int value)
104 {
105 return mHfNativeServer->test_native(value);
106 }
107 // ----------------------------------------------------------------------------
108 }; // namespace android
frameworks/native/libs/hfnative/Android.mk
1 LOCAL_PATH:= $(call my-dir)
2 include $(CLEAR_VARS)
3
4 LOCAL_SRC_FILES:= \
5 IHfNativeService.cpp \
6 HfNativeManager.cpp
7
8 LOCAL_SHARED_LIBRARIES := \
9 libbinder \
10 libcutils \
11 libutils
12
13 LOCAL_MODULE:= libhfnativemgriface
14
15 #ifneq ($(filter generic%,$(TARGET_DEVICE)),)
16 # Emulator build
17 # LOCAL_CFLAGS += -DUSE_FENCE_SYNC
18 #endif
19
20 include $(BUILD_SHARED_LIBRARY)
21
22 #ifeq (,$(ONE_SHOT_MAKEFILE))
23 #include $(call first-makefiles-under,$(LOCAL_PATH))
24 #endif
(C) 서버 추가
서버 에서 말하자면 클 라 이언 트 의 원 격 호출 입 니 다. 예 를 들 어 클 라 이언 트 가 write 를 호출 합 니 다.native () 때, 서버 의 writenative () 도 호출 됩 니 다.왜 클 라 이언 트 가 서버 의 write 를 직접 호출 할 수 없 습 니까?native () 는 클 라 이언 트 와 서버 가 각각 다른 프로 세 스에 있 기 때문에 프로 세 스 간 의 통신 은 반드시 Binder, socket 등 메커니즘 을 통 해 전달 되 어야 한다.
frameworks/native/services/hfnativeservice/HfNativeService.h
1 #ifndef ANDROID_HACKFUN_NATIVE_SERVICE_H
2 #define ANDROID_HACKFUN_NATIVE_SERVICE_H
3
4 #include
5 #include
6
7 #include
8
9 #include
10 #include
11 #include
12 #include
13 #include
14 #include
15
16 #include
17
18 #include
19
20 namespace android {
21
22 // ---------------------------------------------------------------------------
23
24 // ---------------------------------------------------------------------------
25 class HfNativeService : public BinderService,
26 public BnHfNativeService
27 {
28 public:
29 static char const* getServiceName() {
30 return "hfnativeservice";
31 }
32
33 HfNativeService();
34
35 private:
36 virtual int init_native(void);
37 virtual void finalize_native(void);
38 virtual int read_native(char *Buff, int Len);
39 virtual int write_native(char *Buff, int Len);
40 virtual int test_native(int value);
41 };
42
43 // ---------------------------------------------------------------------------
44 }; // namespace android
45
46 #endif // ANDROID_HACKFUN_NATIVE_SERVICE_H
frameworks/native/services/hfnativeservice/HfNativeService.cpp
1 #include
2 #include
3 #include
4
5 #include
6 #include
7 #include
8 #include
9
10 #include
11 #include
12
13 #include
14
15 #include "HfNativeService.h"
16
17 #include
18 #include
19 #include
20
21 #include
22
23 namespace android {
24 // ---------------------------------------------------------------------------
25
26
27 opersyshw_device_t* opersyshw_dev;
28
29
30 HfNativeService::HfNativeService()
31 {
32 }
33
34 int HfNativeService::init_native(void)
35 {
36 int err;
37 hw_module_t* module;
38 opersyshw_device_t* dev = NULL;
39
40 ALOGI("init_native()");
41
42 err = hw_get_module(OPERSYSHW_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
43 if (err == 0) {
44 if (module->methods->open(module, "", ((hw_device_t**) &dev)) != 0) {
45 ALOGE("Can't open opersys module!!!");
46 return 0;
47 }
48 } else {
49 ALOGE("Can't get opersys module!!!");
50 return 0;
51 }
52
53 opersyshw_dev = dev;
54
55 return 0;
56 }
57
58 void HfNativeService::finalize_native(void)
59 {
60 opersyshw_device_t* dev = opersyshw_dev;
61
62 ALOGI("finalize_native()");
63
64 if (dev == NULL) {
65 return;
66 }
67
68 dev->close();
69
70 free(dev);
71 }
72
73
74 int HfNativeService::read_native(char *Buff, int Len)
75 {
76 opersyshw_device_t* dev = opersyshw_dev;
77 char* real_byte_array = Buff;
78 int length;
79
80 ALOGI("read_native()");
81
82 if (dev == NULL) {
83 return 0;
84 }
85
86 length = dev->read((char*) real_byte_array, Len);
87
88 ALOGI("read data from hal: %s", (char *)real_byte_array);
89
90 return length;
91 }
92
93 int HfNativeService::write_native(char *Buff, int Len)
94 {
95 opersyshw_device_t* dev = opersyshw_dev;
96 char* real_byte_array = Buff;
97 int length;
98
99 ALOGI("write_native()");
100
101 if (dev == NULL) {
102 return 0;
103 }
104
105 length = dev->write((char*) real_byte_array, Len);
106
107 ALOGI("write data to hal: %s", (char *)real_byte_array);
108
109 return length;
110 }
111
112 int HfNativeService::test_native(int value)
113 {
114 opersyshw_device_t* dev = opersyshw_dev;
115
116 if (dev == NULL) {
117 return 0;
118 }
119
120 ALOGI("test_native()");
121
122 return dev->test(value);
123 }
124
125 // ---------------------------------------------------------------------------
126 }; // namespace android
frameworks/native/services/hfnativeservice/Android.mk
1 LOCAL_PATH:= $(call my-dir)
2 include $(CLEAR_VARS)
3
4 LOCAL_SRC_FILES:= \
5 HfNativeService.cpp \
6
7 LOCAL_CFLAGS:= -DLOG_TAG=\"HfNativeService\"
8
9 LOCAL_C_INCLUDES += \
10 $(call include-path-for, libhardware)/hardware
11
12 LOCAL_SHARED_LIBRARIES := \
13 libcutils \
14 libutils \
15 libbinder \
16 libhardware \
17 libhfnativemgriface
18
19 LOCAL_MODULE:= libhfnativeservice
20
21 include $(BUILD_SHARED_LIBRARY)
(D) 로그 인 서버 에서 추 가 된 서 비 스 를 시작 하여 독립 된 프로 세 스에 서 실행 하고 클 라 이언 트 의 요청 을 기다 리 도록 합 니 다.
frameworks/native/cmds/hfnative/main_hfnativeservice.cpp
1 #include
2 #include
3 #include
4 #include
5 #include
6
7 #include
8
9 using namespace android;
10
11 int main(int argc, char** argv) {
12 #if 1
13 HfNativeService::publishAndJoinThreadPool(true);
14 // Like the SurfaceFlinger, limit the number of binder threads to 4.
15 ProcessState::self()->setThreadPoolMaxThreadCount(4);
16 #else
17
18 sp proc(ProcessState::self());
19
20 sp sm = defaultServiceManager();
21
22 sm->addService(String16("hfnativeservice"), new HfNativeService());
23
24 ProcessState::self()->startThreadPool();
25 ProcessState::self()->giveThreadPoolName();
26 IPCThreadState::self()->joinThreadPool();
27 ProcessState::self()->setThreadPoolMaxThreadCount(4);
28 #endif
29 return 0;
30 }
frameworks/native/cmds/hfnative/Android.mk
1 LOCAL_PATH:= $(call my-dir)
2 include $(CLEAR_VARS)
3
4 LOCAL_SRC_FILES:= \
5 main_hfnativeservice.cpp
6
7 LOCAL_SHARED_LIBRARIES := \
8 libhfnativeservice \
9 libbinder \
10 libutils
11
12 LOCAL_C_INCLUDES := \
13 $(LOCAL_PATH)/../../services/hfnativeservice
14
15 LOCAL_MODULE:= hfnativeservice
16
17 include $(BUILD_EXECUTABLE)
(E) 클 라 이언 트 추가 사용 자 는 클 라 이언 트 가 제공 하 는 인터페이스 에 따라 직접 사용 할 수 있 으 며 복잡 한 클 라 이언 트 와 서버 의 통신 세부 사항 에 관심 을 가 질 필요 가 없다.HfnativeManager 가 제공 하 는 인 터 페 이 스 를 사용 하면 원 격 호출 이 가능 합 니 다.서버 의 사망 통 지 를 기다 리 는 데 사용 할 독립 된 스 레 드 를 만 들 었 습 니 다.
frameworks/base/tests/Hfnative/main_hfnativeclient.cpp
1 #define LOG_TAG "HfNativeClient"
2
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11
12 #include
13
14 using namespace android;
15
16
17 class HfThread: public Thread {
18 public:
19 HfNativeManager *hfmgr;
20
21 HfThread(void *ptr) {
22 this->hfmgr = (HfNativeManager *)ptr;
23 }
24
25 bool threadLoop();
26 };
27
28 bool HfThread::threadLoop()
29 {
30 if (hfmgr->checkService()) {
31 ALOGW("hfnativeservice Died, please do some clear!");
32 hfmgr->resetServiceStatus();
33 }
34
35 usleep(250000);
36
37 return true;
38 }
39
40 int main(int argc, char **argv)
41 {
42 const char *str = {"Hello, Android !\0"};
43 char buff[strlen(str)];
44
45 HfNativeManager *hfmgr = new HfNativeManager();
46
47 if (hfmgr->assertState() == NO_ERROR) {
48 hfmgr->write_queue(const_cast<char *>(str), strlen(str));
49 usleep(100000);
50 hfmgr->read_queue(buff, sizeof(buff));
51 ALOGI("Service returned: %s", buff);
52 }
53
54 sp th = new HfThread((void *)hfmgr);
55 th->run();
56
57 ProcessState::self()->startThreadPool();
58 IPCThreadState::self()->joinThreadPool();
59
60 return 0;
61 }
frameworks/base/tests/Hfnative/Android.mk
1 LOCAL_PATH:= $(call my-dir)
2 include $(CLEAR_VARS)
3
4 LOCAL_SRC_FILES:= \
5 main_hfnativeclient.cpp
6
7 LOCAL_SHARED_LIBRARIES := \
8 libhfnativemgriface \
9 libbinder \
10 libutils \
11 libcutils
12
13 LOCAL_C_INCLUDES := \
14 $(ANDROID_SOURCE)/frameworks/native/include/
15
16 LOCAL_MODULE:= hfnativeclient
17
18 include $(BUILD_EXECUTABLE)
(F) 테스트
컴 파일 후 대응 하 는 파일 생 성:
out/target/product//.../system/lib/libhfnativemgriface.so
out/target/product//.../system/lib/libhfnativeservice.so
out/target/product//.../system/bin/hfnativeservice
out/target/product//.../system/bin/hfnativeclient
그리고 push 는 기계 의 해당 디 렉 터 리 로 갑 니 다. 기계 루트 디 렉 터 리 에서 다음 명령 을 실행 하고 출력 인쇄 를 관찰 합 니 다. (서버 프로 세 스 를 먼저 시작 해 야 합 니 다.)
# cd system/bin
# hfnativeservice &
# service check hfnativeservice
Service hfnativeservice: found
# hfnativeclient &
# kill -9 pid
출력 출력:
......
01-01 13:53:55.148 2424 2424 I HfNativeService: init_native()
01-01 13:53:55.149 2424 2424 D opersyshw_qemu: OPERSYS HW has been initialized
01-01 13:53:55.150 2424 2427 I HfNativeService: test_native()
01-01 13:53:55.151 2434 2434 I HfNative: test hfnativeservice [20]
01-01 13:53:55.151 2424 2424 I HfNativeService: write_native()
01-01 13:53:55.151 2424 2424 D opersyshw_qemu: OPERSYS HW - write()for 16 bytes called
01-01 13:53:55.151 2424 2424 D opersyshw_qemu: write data to driver: Hello, Android !
01-01 13:53:55.151 2424 2424 I HfNativeService: write data to hal: Hello, Android !
01-01 13:53:55.252 2424 2427 I HfNativeService: read_native()
01-01 13:53:55.252 2424 2427 D opersyshw_qemu: OPERSYS HW - read()for 16 bytes called
01-01 13:53:55.252 2424 2427 D opersyshw_qemu: read data from driver: Hello, Android !
01-01 13:53:55.252 2424 2427 I HfNativeService: read data from hal: Hello, Android !
01-01 13:53:55.252 2434 2434 I HfNativeClient: Service returned: Hello, Android !
......
01-01 13:54:08.210 2434 2434 W HfNative: hfnativeservice died [0xb6cc90c0]
01-01 13:54:08.210 211 211 I ServiceManager: service 'hfnativeservice' died
01-01 13:54:08.269 2434 2437 W HfNativeClient: hfnativeservice Died, please do some clear!
......
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.