Android中的Parcel机制(1)

  1. if (b != NULL) {

  2. LOG_REFS(“Parcel %p acquiring reference on remote %p”, who, b.get());

  3. b->incStrong(who);

  4. }

  5. return;

  6. }

  7. case BINDER_TYPE_WEAK_HANDLE: {

  8. const wp b = proc->getWeakProxyForHandle(obj.handle);

  9. if (b != NULL) b.get_refs()->incWeak(who);

  10. return;

  11. }

  12. case BINDER_TYPE_FD: {

  13. // intentionally blank – nothing to do to acquire this, but we do

  14. // recognize it as a legitimate object type.

  15. return;

  16. }

  17. }

  18. LOGD(“Invalid object type 0x%08lx”, obj.type);

  19. }

  20. void release_object(const sp& proc,

  21. const flat_binder_object& obj, const void* who)

  22. {

  23. switch (obj.type) {

  24. case BINDER_TYPE_BINDER:

  25. if (obj.binder) {

  26. LOG_REFS(“Parcel %p releasing reference on local %p”, who, obj.cookie);

  27. static_cast<IBinder*>(obj.cookie)->decStrong(who);

  28. }

  29. return;

  30. case BINDER_TYPE_WEAK_BINDER:

  31. if (obj.binder)

  32. static_castRefBase::weakref\_type\*(obj.binder)->decWeak(who);

  33. return;

  34. case BINDER_TYPE_HANDLE: {

  35. const sp b = proc->getStrongProxyForHandle(obj.handle);

  36. if (b != NULL) {

  37. LOG_REFS(“Parcel %p releasing reference on remote %p”, who, b.get());

  38. b->decStrong(who);

  39. }

  40. return;

  41. }

  42. case BINDER_TYPE_WEAK_HANDLE: {

  43. const wp b = proc->getWeakProxyForHandle(obj.handle);

  44. if (b != NULL) b.get_refs()->decWeak(who);

  45. return;

  46. }

  47. case BINDER_TYPE_FD: {

  48. if (obj.cookie != (void*)0) close(obj.handle);

  49. return;

  50. }

  51. }

  52. LOGE(“Invalid object type 0x%08lx”, obj.type);

  53. }

  54. inline static status_t finish_flatten_binder(

  55. const sp& binder, const flat_binder_object& flat, Parcel* out)

  56. {

  57. return out->writeObject(flat, false);

  58. }

  59. status_t flatten_binder(const sp& proc,

  60. const sp& binder, Parcel* out)

  61. {

  62. flat_binder_object obj;

  63. obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;

  64. if (binder != NULL) {

  65. IBinder *local = binder->localBinder();

  66. if (!local) {

  67. BpBinder *proxy = binder->remoteBinder();

  68. if (proxy == NULL) {

  69. LOGE(“null proxy”);

  70. }

  71. const int32_t handle = proxy ? proxy->handle() : 0;

  72. obj.type = BINDER_TYPE_HANDLE;

  73. obj.handle = handle;

  74. obj.cookie = NULL;

  75. } else {

  76. obj.type = BINDER_TYPE_BINDER;

  77. obj.binder = local->getWeakRefs();

  78. obj.cookie = local;

  79. }

  80. } else {

  81. obj.type = BINDER_TYPE_BINDER;

  82. obj.binder = NULL;

  83. obj.cookie = NULL;

  84. }

  85. return finish_flatten_binder(binder, obj, out);

  86. }

  87. status_t flatten_binder(const sp& proc,

  88. const wp& binder, Parcel* out)

  89. {

  90. flat_binder_object obj;

  91. obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;

  92. if (binder != NULL) {

  93. sp real = binder.promote();

  94. if (real != NULL) {

  95. IBinder *local = real->localBinder();

  96. if (!local) {

  97. BpBinder *proxy = real->remoteBinder();

  98. if (proxy == NULL) {

  99. LOGE(“null proxy”);

  100. }

  101. const int32_t handle = proxy ? proxy->handle() : 0;

  102. obj.type = BINDER_TYPE_WEAK_HANDLE;

  103. obj.handle = handle;

  104. obj.cookie = NULL;

  105. } else {

  106. obj.type = BINDER_TYPE_WEAK_BINDER;

  107. obj.binder = binder.get_refs();

  108. obj.cookie = binder.unsafe_get();

  109. }

  110. return finish_flatten_binder(real, obj, out);

  111. }

  112. // XXX How to deal?  In order to flatten the given binder,

  113. // we need to probe it for information, which requires a primary

  114. // reference…  but we don’t have one.

  115. //

  116. // The OpenBinder implementation uses a dynamic_cast<> here,

  117. // but we can’t do that with the different reference counting

  118. // implementation we are using.

  119. LOGE(“Unable to unflatten Binder weak reference!”);

  120. obj.type = BINDER_TYPE_BINDER;

  121. obj.binder = NULL;

  122. obj.cookie = NULL;

  123. return finish_flatten_binder(NULL, obj, out);

  124. } else {

  125. obj.type = BINDER_TYPE_BINDER;

  126. obj.binder = NULL;

  127. obj.cookie = NULL;

  128. return finish_flatten_binder(NULL, obj, out);

  129. }

  130. }

  131. inline static status_t finish_unflatten_binder(

  132. BpBinder* proxy, const flat_binder_object& flat, const Parcel& in)

  133. {

  134. return NO_ERROR;

  135. }

  136. status_t unflatten_binder(const sp& proc,

  137. const Parcel& in, sp* out)

  138. {

  139. const flat_binder_object* flat = in.readObject(false);

  140. if (flat) {

  141. switch (flat->type) {

  142. case BINDER_TYPE_BINDER:

  143. *out = static_cast<IBinder*>(flat->cookie);

  144. return finish_unflatten_binder(NULL, *flat, in);

  145. case BINDER_TYPE_HANDLE:

  146. *out = proc->getStrongProxyForHandle(flat->handle);

  147. return finish_unflatten_binder(

  148. static_cast<BpBinder*>(out->get()), *flat, in);

  149. }

  150. }

  151. return BAD_TYPE;

  152. }

  153. status_t unflatten_binder(const sp& proc,

  154. const Parcel& in, wp* out)

  155. {

  156. const flat_binder_object* flat = in.readObject(false);

  157. if (flat) {

  158. switch (flat->type) {

  159. case BINDER_TYPE_BINDER:

  160. *out = static_cast<IBinder*>(flat->cookie);

  161. return finish_unflatten_binder(NULL, *flat, in);

  162. case BINDER_TYPE_WEAK_BINDER:

  163. if (flat->binder != NULL) {

  164. out->set_object_and_refs(

  165. static_cast<IBinder*>(flat->cookie),

  166. static_castRefBase::weakref\_type\*(flat->binder));

  167. } else {

  168. *out = NULL;

  169. }

  170. return finish_unflatten_binder(NULL, *flat, in);

  171. case BINDER_TYPE_HANDLE:

  172. case BINDER_TYPE_WEAK_HANDLE:

  173. *out = proc->getWeakProxyForHandle(flat->handle);

  174. return finish_unflatten_binder(

  175. static_cast<BpBinder*>(out->unsafe_get()), *flat, in);

  176. }

  177. }

  178. return BAD_TYPE;

  179. }

  180. // ---------------------------------------------------------------------------

  181. Parcel::Parcel()

  182. {

  183. initState();

  184. }

  185. Parcel::~Parcel()

  186. {

  187. freeDataNoInit();

  188. }

  189. const uint8_t* Parcel::data() const

  190. {

  191. return mData;

  192. }

  193. size_t Parcel::dataSize() const

  194. {

  195. return (mDataSize > mDataPos ? mDataSize : mDataPos);

  196. }

  197. size_t Parcel::dataAvail() const

  198. {

  199. // TODO: decide what to do about the possibility that this can

  200. // report an available-data size that exceeds a Java int’s max

  201. // positive value, causing havoc.  Fortunately this will only

  202. // happen if someone constructs a Parcel containing more than two

  203. // gigabytes of data, which on typical phone hardware is simply

  204. // not possible.

  205. return dataSize() - dataPosition();

  206. }

  207. size_t Parcel::dataPosition() const

  208. {

  209. return mDataPos;

  210. }

  211. size_t Parcel::dataCapacity() const

  212. {

  213. return mDataCapacity;

  214. }

  215. status_t Parcel::setDataSize(size_t size)

  216. {

  217. status_t err;

  218. err = continueWrite(size);

  219. if (err == NO_ERROR) {

  220. mDataSize = size;

  221. LOGV(“setDataSize Setting data size of %p to %d/n”, this, mDataSize);

  222. }

  223. return err;

  224. }

  225. void Parcel::setDataPosition(size_t pos) const

  226. {

  227. mDataPos = pos;

  228. mNextObjectHint = 0;

  229. }

  230. status_t Parcel::setDataCapacity(size_t size)

  231. {

  232. if (size > mDataSize) return continueWrite(size);

  233. return NO_ERROR;

  234. }

  235. status_t Parcel::setData(const uint8_t* buffer, size_t len)

  236. {

  237. status_t err = restartWrite(len);

  238. if (err == NO_ERROR) {

  239. memcpy(const_cast<uint8_t*>(data()), buffer, len);

  240. mDataSize = len;

  241. mFdsKnown = false;

  242. }

  243. return err;

  244. }

  245. status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len)

  246. {

  247. const sp proc(ProcessState::self());

  248. status_t err;

  249. uint8_t *data = parcel->mData;

  250. size_t *objects = parcel->mObjects;

  251. size_t size = parcel->mObjectsSize;

  252. int startPos = mDataPos;

  253. int firstIndex = -1, lastIndex = -2;

  254. if (len == 0) {

  255. return NO_ERROR;

  256. }

  257. // range checks against the source parcel size

  258. if ((offset > parcel->mDataSize)

  259. || (len > parcel->mDataSize)

  260. || (offset + len > parcel->mDataSize)) {

  261. return BAD_VALUE;

  262. }

  263. // Count objects in range

  264. for (int i = 0; i < (int) size; i++) {

  265. size_t off = objects[i];

  266. if ((off >= offset) && (off < offset + len)) {

  267. if (firstIndex == -1) {

  268. firstIndex = i;

  269. }

  270. lastIndex = i;

  271. }

  272. }

  273. int numObjects = lastIndex - firstIndex + 1;

  274. // grow data

  275. err = growData(len);

  276. if (err != NO_ERROR) {

  277. return err;

  278. }

  279. // append data

  280. memcpy(mData + mDataPos, data + offset, len);

  281. mDataPos += len;

  282. mDataSize += len;

  283. if (numObjects > 0) {

  284. // grow objects

  285. if (mObjectsCapacity < mObjectsSize + numObjects) {

  286. int newSize = ((mObjectsSize + numObjects)*3)/2;

  287. size_t *objects =

  288. (size_t*)realloc(mObjects, newSize*sizeof(size_t));

  289. if (objects == (size_t*)0) {

  290. return NO_MEMORY;

  291. }

  292. mObjects = objects;

  293. mObjectsCapacity = newSize;

  294. }

  295. // append and acquire objects

  296. int idx = mObjectsSize;

  297. for (int i = firstIndex; i <= lastIndex; i++) {

  298. size_t off = objects[i] - offset + startPos;

  299. mObjects[idx++] = off;

  300. mObjectsSize++;

  301. flat_binder_object* flat

  302. = reinterpret_cast<flat_binder_object*>(mData + off);

  303. acquire_object(proc, *flat, this);

  304. if (flat->type == BINDER_TYPE_FD) {

  305. // If this is a file descriptor, we need to dup it so the

  306. // new Parcel now owns its own fd, and can declare that we

  307. // officially know we have fds.

  308. flat->handle = dup(flat->handle);

  309. flat->cookie = (void*)1;

  310. mHasFds = mFdsKnown = true;

  311. }

  312. }

  313. }

  314. return NO_ERROR;

  315. }

  316. bool Parcel::hasFileDescriptors() const

  317. {

  318. if (!mFdsKnown) {

  319. scanForFds();

  320. }

  321. return mHasFds;

  322. }

  323. status_t Parcel::writeInterfaceToken(const String16& interface)

  324. {

  325. // currently the interface identification token is just its name as a string

  326. return writeString16(interface);

  327. }

  328. bool Parcel::checkInterface(IBinder* binder) const

  329. {

  330. return enforceInterface(binder->getInterfaceDescriptor());

  331. }

  332. bool Parcel::enforceInterface(const String16& interface) const

  333. {

  334. const String16 str(readString16());

  335. if (str == interface) {

  336. return true;

  337. } else {

  338. LOGW(“**** enforceInterface() expected ‘%s’ but read ‘%s’/n”,

  339. String8(interface).string(), String8(str).string());

  340. return false;

  341. }

  342. }

  343. const size_t* Parcel::objects() const

  344. {

  345. return mObjects;

  346. }

  347. size_t Parcel::objectsCount() const

  348. {

  349. return mObjectsSize;

  350. }

  351. status_t Parcel::errorCheck() const

  352. {

  353. return mError;

  354. }

  355. void Parcel::setError(status_t err)

  356. {

  357. mError = err;

  358. }

  359. status_t Parcel::finishWrite(size_t len)

  360. {

  361. //printf(“Finish write of %d/n”, len);

  362. mDataPos += len;

  363. LOGV(“finishWrite Setting data pos of %p to %d/n”, this, mDataPos);

  364. if (mDataPos > mDataSize) {

  365. mDataSize = mDataPos;

  366. LOGV(“finishWrite Setting data size of %p to %d/n”, this, mDataSize);

  367. }

  368. //printf(“New pos=%d, size=%d/n”, mDataPos, mDataSize);

  369. return NO_ERROR;

  370. }

  371. status_t Parcel::writeUnpadded(const void* data, size_t len)

  372. {

  373. size_t end = mDataPos + len;

  374. if (end < mDataPos) {

  375. // integer overflow

  376. return BAD_VALUE;

  377. }

  378. if (end <= mDataCapacity) {

  379. restart_write:

  380. memcpy(mData+mDataPos, data, len);

  381. return finishWrite(len);

  382. }

  383. status_t err = growData(len);

  384. if (err == NO_ERROR) goto restart_write;

  385. return err;

  386. }

  387. status_t Parcel::write(const void* data, size_t len)

  388. {

  389. void* const d = writeInplace(len);

  390. if (d) {

  391. memcpy(d, data, len);

  392. return NO_ERROR;

  393. }

  394. return mError;

  395. }

  396. void* Parcel::writeInplace(size_t len)

  397. {

  398. const size_t padded = PAD_SIZE(len);

  399. // sanity check for integer overflow

  400. if (mDataPos+padded < mDataPos) {

  401. return NULL;

  402. }

  403. if ((mDataPos+padded) <= mDataCapacity) {

  404. restart_write:

  405. //printf(“Writing %ld bytes, padded to %ld/n”, len, padded);

  406. uint8_t* const data = mData+mDataPos;

  407. // Need to pad at end?

  408. if (padded != len) {

  409. #if BYTE_ORDER == BIG_ENDIAN

  410. static const uint32_t mask[4] = {

  411. 0x00000000, 0xffffff00, 0xffff0000, 0xff000000

  412. };

  413. #endif

  414. #if BYTE_ORDER == LITTLE_ENDIAN

  415. static const uint32_t mask[4] = {

  416. 0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff

  417. };

  418. #endif

  419. //printf(“Applying pad mask: %p to %p/n”, (void*)mask[padded-len],

  420. //    *reinterpret_cast<void**>(data+padded-4));

  421. *reinterpret_cast<uint32_t*>(data+padded-4) &= mask[padded-len];

  422. }

  423. finishWrite(padded);

  424. return data;

  425. }

  426. status_t err = growData(padded);

  427. if (err == NO_ERROR) goto restart_write;

  428. return NULL;

  429. }

  430. status_t Parcel::writeInt32(int32_t val)

  431. {

  432. return writeAligned(val);

  433. }

  434. status_t Parcel::writeInt64(int64_t val)

  435. {

  436. return writeAligned(val);

  437. }

  438. status_t Parcel::writeFloat(float val)

  439. {

  440. return writeAligned(val);

  441. }

  442. status_t Parcel::writeDouble(double val)

  443. {

  444. return writeAligned(val);

  445. }

  446. status_t Parcel::writeIntPtr(intptr_t val)

  447. {

  448. return writeAligned(val);

  449. }

  450. status_t Parcel::writeCString(const char* str)

  451. {

  452. return write(str, strlen(str)+1);

  453. }

  454. status_t Parcel::writeString8(const String8& str)

  455. {

  456. status_t err = writeInt32(str.bytes());

  457. if (err == NO_ERROR) {

  458. err = write(str.string(), str.bytes()+1);

  459. }

  460. return err;

  461. }

  462. status_t Parcel::writeString16(const String16& str)

  463. {

  464. return writeString16(str.string(), str.size());

  465. }

  466. status_t Parcel::writeString16(const char16_t* str, size_t len)

  467. {

  468. if (str == NULL) return writeInt32(-1);

  469. status_t err = writeInt32(len);

  470. if (err == NO_ERROR) {

  471. len *= sizeof(char16_t);

  472. uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));

  473. if (data) {

  474. memcpy(data, str, len);

  475. *reinterpret_cast<char16_t*>(data+len) = 0;

  476. return NO_ERROR;

  477. }

  478. err = mError;

  479. }

  480. return err;

  481. }

  482. status_t Parcel::writeStrongBinder(const sp& val)

  483. {

  484. return flatten_binder(ProcessState::self(), val, this);

  485. }

  486. status_t Parcel::writeWeakBinder(const wp& val)

  487. {

  488. return flatten_binder(ProcessState::self(), val, this);

  489. }

  490. status_t Parcel::writeNativeHandle(const native_handle* handle)

  491. {

  492. if (!handle || handle->version != sizeof(native_handle))

  493. return BAD_TYPE;

  494. status_t err;

  495. err = writeInt32(handle->numFds);

  496. if (err != NO_ERROR) return err;

  497. err = writeInt32(handle->numInts);

  498. if (err != NO_ERROR) return err;

  499. for (int i=0 ; err==NO_ERROR && inumFds ; i++)

  500. err = writeDupFileDescriptor(handle->data[i]);

  501. if (err != NO_ERROR) {

  502. LOGD(“write native handle, write dup fd failed”);

  503. return err;

  504. }

  505. err = write(handle->data + handle->numFds, sizeof(int)*handle->numInts);

  506. return err;

  507. }

  508. status_t Parcel::writeFileDescriptor(int fd)

  509. {

  510. flat_binder_object obj;

  511. obj.type = BINDER_TYPE_FD;

  512. obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;

  513. obj.handle = fd;

  514. obj.cookie = (void*)0;

  515. return writeObject(obj, true);

  516. }

  517. status_t Parcel::writeDupFileDescriptor(int fd)

  518. {

  519. flat_binder_object obj;

  520. obj.type = BINDER_TYPE_FD;

  521. obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;

  522. obj.handle = dup(fd);

  523. obj.cookie = (void*)1;

  524. return writeObject(obj, true);

  525. }

  526. status_t Parcel::write(const Flattenable& val)

  527. {

  528. status_t err;

  529. // size if needed

  530. size_t len = val.getFlattenedSize();

  531. size_t fd_count = val.getFdCount();

  532. err = this->writeInt32(len);

  533. if (err) return err;

  534. err = this->writeInt32(fd_count);

  535. if (err) return err;

  536. // payload

  537. void* buf = this->writeInplace(PAD_SIZE(len));

  538. if (buf == NULL)

  539. return BAD_VALUE;

  540. int* fds = NULL;

  541. if (fd_count) {

  542. fds = new int[fd_count];

  543. }

  544. err = val.flatten(buf, len, fds, fd_count);

  545. for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {

  546. err = this->writeDupFileDescriptor( fds[i] );

  547. }

  548. if (fd_count) {

  549. delete [] fds;

  550. }

  551. return err;

  552. }

  553. status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)

  554. {

  555. const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;

  556. const bool enoughObjects = mObjectsSize < mObjectsCapacity;

  557. if (enoughData && enoughObjects) {

  558. restart_write:

  559. *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;

  560. // Need to write meta-data?

  561. if (nullMetaData || val.binder != NULL) {

  562. mObjects[mObjectsSize] = mDataPos;

  563. acquire_object(ProcessState::self(), val, this);

  564. mObjectsSize++;

  565. }

  566. // remember if it’s a file descriptor

  567. if (val.type == BINDER_TYPE_FD) {

  568. mHasFds = mFdsKnown = true;

  569. }

  570. return finishWrite(sizeof(flat_binder_object));

  571. }

  572. if (!enoughData) {

  573. const status_t err = growData(sizeof(val));

  574. if (err != NO_ERROR) return err;

  575. }

  576. if (!enoughObjects) {

  577. size_t newSize = ((mObjectsSize+2)*3)/2;

  578. size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t));

  579. if (objects == NULL) return NO_MEMORY;

  580. mObjects = objects;

  581. mObjectsCapacity = newSize;

  582. }

  583. goto restart_write;

  584. }

  585. void Parcel::remove(size_t start, size_t amt)

  586. {

  587. LOG_ALWAYS_FATAL(“Parcel::remove() not yet implemented!”);

  588. }

  589. status_t Parcel::read(void* outData, size_t len) const

  590. {

  591. if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) {

  592. memcpy(outData, mData+mDataPos, len);

  593. mDataPos += PAD_SIZE(len);

  594. LOGV(“read Setting data pos of %p to %d/n”, this, mDataPos);

  595. return NO_ERROR;

  596. }

  597. return NOT_ENOUGH_DATA;

  598. }

  599. const void* Parcel::readInplace(size_t len) const

  600. {

  601. if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) {

  602. const void* data = mData+mDataPos;

  603. mDataPos += PAD_SIZE(len);

  604. LOGV(“readInplace Setting data pos of %p to %d/n”, this, mDataPos);

  605. return data;

  606. }

  607. return NULL;

  608. }

  609. template

  610. status_t Parcel::readAligned(T *pArg) const {

  611. COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));

  612. if ((mDataPos+sizeof(T)) <= mDataSize) {

  613. const void* data = mData+mDataPos;

  614. mDataPos += sizeof(T);

  615. *pArg =  *reinterpret_cast<const T*>(data);

  616. return NO_ERROR;

  617. } else {

  618. return NOT_ENOUGH_DATA;

  619. }

  620. }

  621. template

  622. T Parcel::readAligned() const {

  623. T result;

  624. if (readAligned(&result) != NO_ERROR) {

  625. result = 0;

  626. }

  627. return result;

  628. }

  629. template

  630. status_t Parcel::writeAligned(T val) {

  631. COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));

  632. if ((mDataPos+sizeof(val)) <= mDataCapacity) {

  633. restart_write:

  634. *reinterpret_cast<T*>(mData+mDataPos) = val;

  635. return finishWrite(sizeof(val));

  636. }

  637. status_t err = growData(sizeof(val));

  638. if (err == NO_ERROR) goto restart_write;

  639. return err;

  640. }

  641. status_t Parcel::readInt32(int32_t *pArg) const

  642. {

  643. return readAligned(pArg);

  644. }

  645. int32_t Parcel::readInt32() const

  646. {

  647. return readAligned<int32_t>();

  648. }

  649. status_t Parcel::readInt64(int64_t *pArg) const

  650. {

  651. return readAligned(pArg);

  652. }

  653. int64_t Parcel::readInt64() const

  654. {

  655. return readAligned<int64_t>();

  656. }

  657. status_t Parcel::readFloat(float *pArg) const

  658. {

  659. return readAligned(pArg);

  660. }

  661. float Parcel::readFloat() const

  662. {

  663. return readAligned<float>();

  664. }

  665. status_t Parcel::readDouble(double *pArg) const

  666. {

  667. return readAligned(pArg);

  668. }

  669. double Parcel::readDouble() const

  670. {

  671. return readAligned<double>();

  672. }

  673. status_t Parcel::readIntPtr(intptr_t *pArg) const

  674. {

  675. return readAligned(pArg);

  676. }

  677. intptr_t Parcel::readIntPtr() const

  678. {

  679. return readAligned<intptr_t>();

  680. }

  681. const char* Parcel::readCString() const

  682. {

  683. const size_t avail = mDataSize-mDataPos;

  684. if (avail > 0) {

  685. const char* str = reinterpret_cast<const char*>(mData+mDataPos);

  686. // is the string’s trailing NUL within the parcel’s valid bounds?

  687. const char* eos = reinterpret_cast<const char*>(memchr(str, 0, avail));

  688. if (eos) {

  689. const size_t len = eos - str;

  690. mDataPos += PAD_SIZE(len+1);

  691. LOGV(“readCString Setting data pos of %p to %d/n”, this, mDataPos);

  692. return str;

  693. }

  694. }

  695. return NULL;

  696. }

  697. String8 Parcel::readString8() const

  698. {

  699. int32_t size = readInt32();

  700. // watch for potential int overflow adding 1 for trailing NUL

  701. if (size > 0 && size < INT32_MAX) {

  702. const char* str = (const char*)readInplace(size+1);

  703. if (str) return String8(str, size);

  704. }

  705. return String8();

  706. }

  707. String16 Parcel::readString16() const

  708. {

  709. size_t len;

  710. const char16_t* str = readString16Inplace(&len);

  711. if (str) return String16(str, len);

  712. LOGE(“Reading a NULL string not supported here.”);

  713. return String16();

  714. }

  715. const char16_t* Parcel::readString16Inplace(size_t* outLen) const

  716. {

  717. int32_t size = readInt32();

  718. // watch for potential int overflow from size+1

  719. if (size >= 0 && size < INT32_MAX) {

  720. *outLen = size;

  721. const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t));

  722. if (str != NULL) {

  723. return str;

  724. }

  725. }

  726. *outLen = 0;

  727. return NULL;

  728. }

  729. sp Parcel::readStrongBinder() const

  730. {

  731. sp val;

  732. unflatten_binder(ProcessState::self(), *this, &val);

  733. return val;

  734. }

  735. wp Parcel::readWeakBinder() const

  736. {

  737. wp val;

  738. unflatten_binder(ProcessState::self(), *this, &val);

  739. return val;

  740. }

  741. native_handle* Parcel::readNativeHandle() const

  742. {

  743. int numFds, numInts;

  744. status_t err;

  745. err = readInt32(&numFds);

  746. if (err != NO_ERROR) return 0;

  747. err = readInt32(&numInts);

  748. if (err != NO_ERROR) return 0;

  749. native_handle* h = native_handle_create(numFds, numInts);

  750. for (int i=0 ; err==NO_ERROR && i<numFds ; i++) {

  751. h->data[i] = dup(readFileDescriptor());

  752. if (h->data[i] < 0) err = BAD_VALUE;

  753. }

  754. err = read(h->data + numFds, sizeof(int)*numInts);

  755. if (err != NO_ERROR) {

  756. native_handle_close(h);

  757. native_handle_delete(h);

  758. h = 0;

  759. }

  760. return h;

  761. }

  762. int Parcel::readFileDescriptor() const

  763. {

  764. const flat_binder_object* flat = readObject(true);

  765. if (flat) {

  766. switch (flat->type) {

  767. case BINDER_TYPE_FD:

  768. //LOGI(“Returning file descriptor %ld from parcel %p/n”, flat->handle, this);

  769. return flat->handle;

  770. }

  771. }

  772. return BAD_TYPE;

  773. }

  774. status_t Parcel::read(Flattenable& val) const

  775. {

  776. // size

  777. const size_t len = this->readInt32();

  778. const size_t fd_count = this->readInt32();

  779. // payload

  780. void const* buf = this->readInplace(PAD_SIZE(len));

  781. if (buf == NULL)

  782. return BAD_VALUE;

  783. int* fds = NULL;

  784. if (fd_count) {

  785. fds = new int[fd_count];

  786. }

  787. status_t err = NO_ERROR;

  788. for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {

  789. fds[i] = dup(this->readFileDescriptor());

  790. if (fds[i] < 0) err = BAD_VALUE;

  791. }

  792. if (err == NO_ERROR) {

  793. err = val.unflatten(buf, len, fds, fd_count);

  794. }

  795. if (fd_count) {

  796. delete [] fds;

  797. }

  798. return err;

  799. }

  800. const flat_binder_object* Parcel::readObject(bool nullMetaData) const

  801. {

  802. const size_t DPOS = mDataPos;

  803. if ((DPOS+sizeof(flat_binder_object)) <= mDataSize) {

  804. const flat_binder_object* obj

  805. = reinterpret_cast<const flat_binder_object*>(mData+DPOS);

  806. mDataPos = DPOS + sizeof(flat_binder_object);

  807. if (!nullMetaData && (obj->cookie == NULL && obj->binder == NULL)) {

  808. // When transferring a NULL object, we don’t write it into

  809. // the object list, so we don’t want to check for it when

  810. // reading.

  811. LOGV(“readObject Setting data pos of %p to %d/n”, this, mDataPos);

  812. return obj;

  813. }

  814. // Ensure that this object is valid…

  815. size_t* const OBJS = mObjects;

  816. const size_t N = mObjectsSize;

  817. size_t opos = mNextObjectHint;

  818. if (N > 0) {

  819. LOGV(“Parcel %p looking for obj at %d, hint=%d/n”,

  820. this, DPOS, opos);

  821. // Start at the current hint position, looking for an object at

  822. // the current data position.

  823. if (opos < N) {

  824. while (opos < (N-1) && OBJS[opos] < DPOS) {

  825. opos++;

  826. }

  827. } else {

  828. opos = N-1;

  829. }

  830. if (OBJS[opos] == DPOS) {

  831. // Found it!

  832. LOGV(“Parcel found obj %d at index %d with forward search”,

  833. this, DPOS, opos);

  834. mNextObjectHint = opos+1;

  835. LOGV(“readObject Setting data pos of %p to %d/n”, this, mDataPos);

  836. return obj;

  837. }

  838. // Look backwards for it…

  839. while (opos > 0 && OBJS[opos] > DPOS) {

  840. opos–;

  841. }

  842. if (OBJS[opos] == DPOS) {

  843. // Found it!

  844. LOGV(“Parcel found obj %d at index %d with backward search”,

  845. this, DPOS, opos);

  846. mNextObjectHint = opos+1;

  847. LOGV(“readObject Setting data pos of %p to %d/n”, this, mDataPos);

  848. return obj;

  849. }

  850. }

  851. LOGW(“Attempt to read object from Parcel %p at offset %d that is not in the object list”,

  852. this, DPOS);

  853. }

  854. return NULL;

  855. }

  856. void Parcel::closeFileDescriptors()

  857. {

  858. size_t i = mObjectsSize;

  859. if (i > 0) {

  860. //LOGI(“Closing file descriptors for %d objects…”, mObjectsSize);

  861. }

  862. while (i > 0) {

  863. i–;

  864. const flat_binder_object* flat

  865. = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);

  866. if (flat->type == BINDER_TYPE_FD) {

  867. //LOGI(“Closing fd: %ld/n”, flat->handle);

  868. close(flat->handle);

  869. }

  870. }

  871. }

  872. const uint8_t* Parcel::ipcData() const

  873. {

  874. return mData;

  875. }

  876. size_t Parcel::ipcDataSize() const

  877. {

  878. return (mDataSize > mDataPos ? mDataSize : mDataPos);

  879. }

  880. const size_t* Parcel::ipcObjects() const

  881. {

  882. return mObjects;

  883. }

  884. size_t Parcel::ipcObjectsCount() const

  885. {

  886. return mObjectsSize;

  887. }

  888. void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,

  889. const size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie)

  890. {

  891. freeDataNoInit();

  892. mError = NO_ERROR;

  893. mData = const_cast<uint8_t*>(data);

  894. mDataSize = mDataCapacity = dataSize;

  895. //LOGI(“setDataReference Setting data size of %p to %lu (pid=%d)/n”, this, mDataSize, getpid());

  896. mDataPos = 0;

  897. LOGV(“setDataReference Setting data pos of %p to %d/n”, this, mDataPos);

  898. mObjects = const_cast<size_t*>(objects);

  899. mObjectsSize = mObjectsCapacity = objectsCount;

  900. mNextObjectHint = 0;

  901. mOwner = relFunc;

  902. mOwnerCookie = relCookie;

  903. scanForFds();

  904. }

  905. void Parcel::print(TextOutput& to, uint32_t flags) const

  906. {

  907. to << “Parcel(”;

  908. if (errorCheck() != NO_ERROR) {

  909. const status_t err = errorCheck();

  910. to << “Error: " << (void*)err << " /”" << strerror(-err) << “/”";

  911. } else if (dataSize() > 0) {

  912. const uint8_t* DATA = data();

  913. to << indent << HexDump(DATA, dataSize()) << dedent;

  914. const size_t* OBJS = objects();

  915. const size_t N = objectsCount();

  916. for (size_t i=0; i<N; i++) {

  917. const flat_binder_object* flat

  918. = reinterpret_cast<const flat_binder_object*>(DATA+OBJS[i]);

  919. to << endl << “Object #” << i << " @ " << (void*)OBJS[i] << ": "

  920. << TypeCode(flat->type & 0x7f7f7f00)

  921. << " = " << flat->binder;

  922. }

  923. } else {

  924. to << “NULL”;

  925. }

  926. to << “)”;

  927. }

  928. void Parcel::releaseObjects()

  929. {

  930. const sp proc(ProcessState::self());

  931. size_t i = mObjectsSize;

  932. uint8_t* const data = mData;

  933. size_t* const objects = mObjects;

  934. while (i > 0) {

  935. i–;

  936. const flat_binder_object* flat

  937. = reinterpret_cast<flat_binder_object*>(data+objects[i]);

  938. release_object(proc, *flat, this);

  939. }

  940. }

  941. void Parcel::acquireObjects()

  942. {

  943. const sp proc(ProcessState::self());

  944. size_t i = mObjectsSize;

  945. uint8_t* const data = mData;

  946. size_t* const objects = mObjects;

  947. while (i > 0) {

  948. i–;

  949. const flat_binder_object* flat

  950. = reinterpret_cast<flat_binder_object*>(data+objects[i]);

  951. acquire_object(proc, *flat, this);

  952. }

  953. }

  954. void Parcel::freeData()

  955. {

  956. freeDataNoInit();

  957. initState();

  958. }

  959. void Parcel::freeDataNoInit()

  960. {

  961. if (mOwner) {

  962. //LOGI(“Freeing data ref of %p (pid=%d)/n”, this, getpid());

  963. mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);

  964. } else {

  965. releaseObjects();

  966. if (mData) free(mData);

  967. if (mObjects) free(mObjects);

  968. }

  969. }

  970. status_t Parcel::growData(size_t len)

  971. {

  972. size_t newSize = ((mDataSize+len)*3)/2;

  973. return (newSize <= mDataSize)

  974. ? (status_t) NO_MEMORY

  975. : continueWrite(newSize);

  976. }

  977. status_t Parcel::restartWrite(size_t desired)

  978. {

  979. if (mOwner) {

  980. freeData();

  981. return continueWrite(desired);

  982. }

  983. uint8_t* data = (uint8_t*)realloc(mData, desired);

  984. if (!data && desired > mDataCapacity) {

  985. mError = NO_MEMORY;

  986. return NO_MEMORY;

  987. }

  988. releaseObjects();

  989. if (data) {

  990. mData = data;

  991. mDataCapacity = desired;

  992. }

  993. mDataSize = mDataPos = 0;

  994. LOGV(“restartWrite Setting data size of %p to %d/n”, this, mDataSize);

  995. LOGV(“restartWrite Setting data pos of %p to %d/n”, this, mDataPos);

  996. free(mObjects);

  997. mObjects = NULL;

  998. mObjectsSize = mObjectsCapacity = 0;

  999. mNextObjectHint = 0;

  1000. mHasFds = false;

  1001. mFdsKnown = true;

  1002. return NO_ERROR;

  1003. }

  1004. status_t Parcel::continueWrite(size_t desired)

  1005. {

  1006. // If shrinking, first adjust for any objects that appear

  1007. // after the new data size.

  1008. size_t objectsSize = mObjectsSize;

  1009. if (desired < mDataSize) {

  1010. if (desired == 0) {

  1011. objectsSize = 0;

  1012. } else {

  1013. while (objectsSize > 0) {

  1014. if (mObjects[objectsSize-1] < desired)

  1015. break;

  1016. objectsSize–;

  1017. }

  1018. }

  1019. }

  1020. if (mOwner) {

  1021. // If the size is going to zero, just release the owner’s data.

  1022. if (desired == 0) {

  1023. freeData();

  1024. return NO_ERROR;

  1025. }

  1026. // If there is a different owner, we need to take

  1027. // posession.

  1028. uint8_t* data = (uint8_t*)malloc(desired);

  1029. if (!data) {

  1030. mError = NO_MEMORY;

  1031. return NO_MEMORY;

  1032. }

  1033. size_t* objects = NULL;

  1034. if (objectsSize) {

  1035. objects = (size_t*)malloc(objectsSize*sizeof(size_t));

  1036. if (!objects) {

  1037. mError = NO_MEMORY;

  1038. return NO_MEMORY;

  1039. }

  1040. // Little hack to only acquire references on objects

  1041. // we will be keeping.

  1042. size_t oldObjectsSize = mObjectsSize;

  1043. mObjectsSize = objectsSize;

  1044. acquireObjects();

  1045. mObjectsSize = oldObjectsSize;

  1046. }

  1047. if (mData) {

  1048. memcpy(data, mData, mDataSize < desired ? mDataSize : desired);

  1049. }

  1050. if (objects && mObjects) {

  1051. memcpy(objects, mObjects, objectsSize*sizeof(size_t));

  1052. }

  1053. //LOGI(“Freeing data ref of %p (pid=%d)/n”, this, getpid());

  1054. mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);

  1055. mOwner = NULL;

  1056. mData = data;

  1057. mObjects = objects;

  1058. mDataSize = (mDataSize < desired) ? mDataSize : desired;

  1059. LOGV(“continueWrite Setting data size of %p to %d/n”, this, mDataSize);

  1060. mDataCapacity = desired;

  1061. mObjectsSize = mObjectsCapacity = objectsSize;

  1062. mNextObjectHint = 0;

  1063. } else if (mData) {

  1064. if (objectsSize < mObjectsSize) {

  1065. // Need to release refs on any objects we are dropping.

  1066. const sp proc(ProcessState::self());

  1067. for (size_t i=objectsSize; i<mObjectsSize; i++) {

  1068. const flat_binder_object* flat

  1069. = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);

  1070. if (flat->type == BINDER_TYPE_FD) {

  1071. // will need to rescan because we may have lopped off the only FDs

  1072. mFdsKnown = false;

  1073. }

  1074. release_object(proc, *flat, this);

  1075. }

  1076. size_t* objects =

  1077. (size_t*)realloc(mObjects, objectsSize*sizeof(size_t));

  1078. if (objects) {

  1079. mObjects = objects;

  1080. }

  1081. mObjectsSize = objectsSize;

  1082. mNextObjectHint = 0;

  1083. }

  1084. // We own the data, so we can just do a realloc().

  1085. if (desired > mDataCapacity) {

  1086. uint8_t* data = (uint8_t*)realloc(mData, desired);

  1087. if (data) {

  1088. mData = data;

  1089. mDataCapacity = desired;

  1090. } else if (desired > mDataCapacity) {

  1091. mError = NO_MEMORY;

  1092. return NO_MEMORY;

  1093. }

  1094. } else {

  1095. mDataSize = desired;

  1096. LOGV(“continueWrite Setting data size of %p to %d/n”, this, mDataSize);

  1097. if (mDataPos > desired) {

  1098. mDataPos = desired;

  1099. LOGV(“continueWrite Setting data pos of %p to %d/n”, this, mDataPos);

  1100. }

  1101. }

  1102. } else {

  1103. // This is the first data.  Easy!

  1104. uint8_t* data = (uint8_t*)malloc(desired);

  1105. if (!data) {

  1106. mError = NO_MEMORY;

  1107. return NO_MEMORY;

  1108. }

  1109. if(!(mDataCapacity == 0 && mObjects == NULL

  1110. && mObjectsCapacity == 0)) {

  1111. LOGE(“continueWrite: %d/%p/%d/%d”, mDataCapacity, mObjects, mObjectsCapacity, desired);

  1112. }

  1113. mData = data;

  1114. mDataSize = mDataPos = 0;

  1115. LOGV(“continueWrite Setting data size of %p to %d/n”, this, mDataSize);

  1116. LOGV(“continueWrite Setting data pos of %p to %d/n”, this, mDataPos);

  1117. mDataCapacity = desired;

  1118. }

  1119. return NO_ERROR;

  1120. }

  1121. void Parcel::initState()

  1122. {

  1123. mError = NO_ERROR;

  1124. mData = 0;

  1125. mDataSize = 0;

  1126. mDataCapacity = 0;

  1127. mDataPos = 0;

  1128. LOGV(“initState Setting data size of %p to %d/n”, this, mDataSize);

  1129. LOGV(“initState Setting data pos of %p to %d/n”, this, mDataPos);

  1130. mObjects = NULL;

  1131. mObjectsSize = 0;

  1132. mObjectsCapacity = 0;

  1133. mNextObjectHint = 0;

  1134. mHasFds = false;

  1135. mFdsKnown = true;

  1136. mOwner = NULL;

  1137. }

  1138. void Parcel::scanForFds() const

  1139. {

  1140. bool hasFds = false;

  1141. for (size_t i=0; i<mObjectsSize; i++) {

  1142. const flat_binder_object* flat

  1143. = reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]);

  1144. if (flat->type == BINDER_TYPE_FD) {

  1145. hasFds = true;

  1146. break;

  1147. }

  1148. }

  1149. mHasFds = hasFds;

  1150. mFdsKnown = true;

  1151. }

  1152. }; // namespace android

本文的源码使用的是Android 2.1版本。

上一篇中我们透过源码看到了Parcel背后的机制,本质上把它当成一个Serialize就可以了,只是它是在内存中完成的序列化和反序列化,利用的是连续的内存空间,因此会更加高效。

我们接下来要说的是Parcel类如何应用。就应用程序而言,最常见使用Parcel类的场景就是在Activity间传递数据。没错,在Activity间使用Intent传递数据的时候,可以通过Parcelable机制传递复杂的对象。

在下面的程序中,MyColor用于保存一个颜色值,MainActivity在用户点击屏幕时将MyColor对象设成红色,传递到SubActivity中,此时SubActivity的TextView显示为红色的背景;当点击SubActivity时,将颜色值改为绿色,返回MainActivity,期望的是MainActivity的TextView显示绿色背景。

来看一下MyColor类的实现代码:

[java]  view plain copy

  1. package com.wenbin.test;

  2. import android.graphics.Color;

  3. import android.os.Parcel;

  4. import android.os.Parcelable;

  5. /**

  6. * @author 曹文斌

  7. * http://blog.csdn.net/caowenbin

  8. *

  9. */

  10. public class MyColor implements Parcelable {

  11. private int color=Color.BLACK;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

Android架构学习进阶是一条漫长而艰苦的道路,不能靠一时激情,更不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

上面分享的字节跳动公司2020年的面试真题解析大全,笔者还把一线互联网企业主流面试技术要点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

就先写到这,码字不易,写的很片面不好之处敬请指出,如果觉得有参考价值的朋友也可以关注一下我

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包阅读下载,最后觉得有帮助、有需要的朋友可以点个赞

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

ds() const

  1. {

  2. bool hasFds = false;

  3. for (size_t i=0; i<mObjectsSize; i++) {

  4. const flat_binder_object* flat

  5. = reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]);

  6. if (flat->type == BINDER_TYPE_FD) {

  7. hasFds = true;

  8. break;

  9. }

  10. }

  11. mHasFds = hasFds;

  12. mFdsKnown = true;

  13. }

  14. }; // namespace android

本文的源码使用的是Android 2.1版本。

上一篇中我们透过源码看到了Parcel背后的机制,本质上把它当成一个Serialize就可以了,只是它是在内存中完成的序列化和反序列化,利用的是连续的内存空间,因此会更加高效。

我们接下来要说的是Parcel类如何应用。就应用程序而言,最常见使用Parcel类的场景就是在Activity间传递数据。没错,在Activity间使用Intent传递数据的时候,可以通过Parcelable机制传递复杂的对象。

在下面的程序中,MyColor用于保存一个颜色值,MainActivity在用户点击屏幕时将MyColor对象设成红色,传递到SubActivity中,此时SubActivity的TextView显示为红色的背景;当点击SubActivity时,将颜色值改为绿色,返回MainActivity,期望的是MainActivity的TextView显示绿色背景。

来看一下MyColor类的实现代码:

[java]  view plain copy

  1. package com.wenbin.test;

  2. import android.graphics.Color;

  3. import android.os.Parcel;

  4. import android.os.Parcelable;

  5. /**

  6. * @author 曹文斌

  7. * http://blog.csdn.net/caowenbin

  8. *

  9. */

  10. public class MyColor implements Parcelable {

  11. private int color=Color.BLACK;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-YMeizn6C-1713786810334)]

[外链图片转存中…(img-BCCPwdye-1713786810336)]

[外链图片转存中…(img-BHbMyE79-1713786810337)]

[外链图片转存中…(img-5geU31Af-1713786810338)]

[外链图片转存中…(img-Wi2u1rL0-1713786810339)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

[外链图片转存中…(img-Atyg0Jg4-1713786810341)]

总结

Android架构学习进阶是一条漫长而艰苦的道路,不能靠一时激情,更不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

上面分享的字节跳动公司2020年的面试真题解析大全,笔者还把一线互联网企业主流面试技术要点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

就先写到这,码字不易,写的很片面不好之处敬请指出,如果觉得有参考价值的朋友也可以关注一下我

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包阅读下载,最后觉得有帮助、有需要的朋友可以点个赞

[外链图片转存中…(img-5jgNSTDX-1713786810342)]

[外链图片转存中…(img-piaZ2yur-1713786810343)]

[外链图片转存中…(img-difUcImg-1713786810344)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值