The InputDispatcher thread is used to obtain and distribute events from the mInboundQueue queue. This chapter introduces the relevant knowledge of InputDispatcher.
threadLoop
After the InputDispatcherThread thread is started, a thredLoop method will also be called. This method obtains events from the mInboundQueue queue and performs subsequent processing. The relevant code is located in frameworks/native/services/inputlinker/dispatcher/inputdispatcher cpp:
status_t InputDispatcher::start() { if (mThread) { return ALREADY_EXISTS; } mThread = std::make_unique<InputThread>( "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); }); return OK; } void InputDispatcher::dispatchOnce() { nsecs_t nextWakeupTime = LONG_LONG_MAX; { // acquire lock AutoMutex _l(mLock); // Wake up the waiting thread. monitor() is used to monitor whether the dispatcher has a deadlock mDispatcherIsAliveCondition.broadcast(); // When an event is generated in the mInboundQueue, obtain the lock and handle the related event if (!haveCommandsLocked()) { dispatchOnceInnerLocked(&nextWakeupTime); } } // release lock ... //After inputReader reads the event and puts the event into the minBoundQueue, it calls loop::wake to wake up the inputDispatcher mLooper->pollOnce(timeoutMillis); } bool InputDispatcher::haveCommandsLocked() const { return !mCommandQueue.isEmpty(); }
The thread executes looper->pollonce and enters epoll_wait status: exit the wait status when any of the following occurs:
- Callback: wake up through callback method;
- Timeout: when nextWakeupTime is reached, wake up after timeout;
- Wake: actively call the wake() method of Looper;
1.1 dispatchOnceInnerLocked
dispatchOnceInnerLocked will take EntryEvent from the mInboundQueue queue header and assign it to mPendingEvent. It will be processed differently according to the type of input event. After the processing is completed, the mPendingEvent will be released. The relevant code is located in frameworks/native/services/inputlinker/dispatcher/ inputdispatcher cpp:
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { // Get current time nsecs_t currentTime = now(); ... // Optimize the app handover delay. When the handover times out, preempt the distribution and discard all other events to be processed. bool isAppSwitchDue = mAppSwitchDueTime <= currentTime; if (mAppSwitchDueTime < *nextWakeupTime) { *nextWakeupTime = mAppSwitchDueTime; } ... // mPendingEvent initialized to null if (!mPendingEvent) { if (mInboundQueue.empty()) { if (isAppSwitchDue) { // The inbound queue is empty so the app switch key we were waiting // for will never arrive. Stop waiting for it. resetPendingAppSwitchLocked(false); isAppSwitchDue = false; } ... if (!mPendingEvent) { // If the mInboundQueue queue is empty, it will be returned directly when there are no events to process return; } } else { // Events for fetching headers from mInboundQueue mPendingEvent = mInboundQueue.front(); mInboundQueue.pop_front(); traceInboundQueueLengthLocked(); } ... } ... // Event distribution switch (mPendingEvent->type) { // Distribution configuration change event case EventEntry::Type::CONFIGURATION_CHANGED: { ... } // Distribution input device reset event case EventEntry::Type::DEVICE_RESET: { ... } // Distribute focus events case EventEntry::Type::FOCUS: { ... } // Touch point change event when distributing multi touch case EventEntry::Type::POINTER_CAPTURE_CHANGED: { ... } // Distribute drag and drop events case EventEntry::Type::DRAG: { ... } // Distribute key events case EventEntry::Type::KEY: { ... } // Distribute touch events case EventEntry::Type::MOTION: { // Force mPendingEvent to MotionEntry std::shared_ptr<MotionEntry> motionEntry = std::static_pointer_cast<MotionEntry>(mPendingEvent); if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { dropReason = DropReason::APP_SWITCH; } if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) { dropReason = DropReason::STALE; } if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) { dropReason = DropReason::BLOCKED; } // If it is a Motion Event, the dispatchMotionLocked branch will be taken done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime); break; } // Distribute sensor events case EventEntry::Type::SENSOR: { ... } } if (done) { if (dropReason != DropReason::NOT_DROPPED) { dropInboundEventLocked(*mPendingEvent, dropReason); } mLastDropReason = dropReason; // Release mPendingEvent after processing releasePendingEventLocked(); *nextWakeupTime = LONG_LONG_MIN; } }
As can be seen from the above code, when inputDispatcher obtains an event, it will be distributed according to the event type. At present, the following events will be distributed:
- Configuration change event [evententry:: type:: configuration\u changed]
- Input device reset event [evententry:: type:: device\u reset]
- Focus event [EventEntry::Type::FOCUS]
- Touch point change event [evententry:: type:: pointer\u capture\u changed]
- Drag event [EventEntry::Type::DRAG]
- Key event [EventEntry::Type::KEY]
- Touch event [EventEntry::Type::MOTION]
- Sensor event [EventEntry::Type::SENSOR]
The following takes the MotionEvent event as an example to analyze the processing flow of the input event.
1.2 dispatchMotionLocked
dispatchMotionLocked is used to handle touch events. The relevant code is located in frameworks/native/services/inputlinker/dispatcher/inputdispatcher cpp:
bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { ... // Determine whether it is a Touch event bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER; // Identify targets. std::vector<InputTarget> inputTargets; bool conflictingPointerActions = false; InputEventInjectionResult injectionResult; if (isPointerEvent) { // Find the focus window for Touch events injectionResult = findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime, &conflictingPointerActions); } else { // Find focus window for non Touch events injectionResult = findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime); } // For MotionEvent, input is returned in four cases_ EVENT_ INJECTION_ Pending status // There are two cases when the focus window is empty and the focus application is not empty: // 1. the timeout for missing focus window is not set; // 2. the time-out period of the focus window has not expired; // 3. when the focus window and focus application are not empty: // 4. the focus window is suspended; if (injectionResult == InputEventInjectionResult::PENDING) { return false; } ... // Start distributing events after the target window is found if (conflictingPointerActions) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, "conflicting pointer actions"); synthesizeCancelationEventsForAllConnectionsLocked(options); } dispatchEventLocked(currentTime, entry, inputTargets); return true; }
For Touch events and non Touch events, there are two different logics to find focus windows: findTouchedWindowTargetsLocked has a lot of processing logics about split screen and multiple display IDs. Compared with findFocusedWindowTargetsLocked, the code logic is slightly more complex. In order to focus on the main process of event distribution, we take findFocusedWindowTargetsLocked as an example to illustrate the process of finding focus windows;
1.3 findFocusedWindowTargetsLocked
findFocusedWindowTargetsLocked is used to find the focus window. The relevant code is located in frameworks/native/services/inputlinker/dispatcher/inputdispatcher cpp:
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) { std::string reason; // Find the display id of the input event. Since Android supports multi screen devices, there may be multiple display IDs. The default is 0 int32_t displayId = getTargetDisplayId(entry); // Find InputWindowHandle based on display id std::shared_ptr<InputApplicationHandle> focusedApplicationHandle = getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); // Find InputApplicationHandle based on display id sp<InputApplicationHandle> focusedApplicationHandle = getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); // If focusedwindownhandle and focusedApplicationHandle are both empty, it means that there is no focus application or focus window at present, so this event will be discarded directly if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) { ALOGI("Dropping %s event because there is no focused window or focused application in " "display %" PRId32 ".", EventEntry::typeToString(entry.type), displayId); return INPUT_EVENT_INJECTION_FAILED; } // The focus application is not empty and the focus window is empty if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) { // Timeout for missing focus window is not set if (!mNoFocusedWindowTimeoutTime.has_value()) { // We just discovered that there's no focused window. Start the ANR timer // Obtain the timeout, which is 5s by default const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout( DEFAULT_INPUT_DISPATCHING_TIMEOUT.count()); // Record timeout mNoFocusedWindowTimeoutTime = currentTime + timeout; // Record focus application waiting for focus window mAwaitedFocusedApplication = focusedApplicationHandle; ALOGW("Waiting because no window has focus but %s may eventually add a " "window when it finishes starting up. Will wait for %" PRId64 "ms", mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout)); *nextWakeupTime = *mNoFocusedWindowTimeoutTime; return INPUT_EVENT_INJECTION_PENDING; } else if (currentTime > *mNoFocusedWindowTimeoutTime) { // If it has timed out, the event will be discarded directly ALOGE("Dropping %s event because there is no focused window", EventEntry::typeToString(entry.type)); return INPUT_EVENT_INJECTION_FAILED; } else { // Wait until the timeout period expires return INPUT_EVENT_INJECTION_PENDING; } } // Reset timeout and waiting focus application resetNoFocusedWindowTimeoutLocked(); // Check permissions. if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) { return INPUT_EVENT_INJECTION_PERMISSION_DENIED; } // The focus window has been paused if (focusedWindowHandle->getInfo()->paused) { ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str()); return INPUT_EVENT_INJECTION_PENDING; } // After successfully finding the focus window, add it to inputTargets addWindowTargetLocked(focusedWindowHandle, InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0), inputTargets); // Done. return INPUT_EVENT_INJECTION_SUCCEEDED; }
There are two very important maps: mFocusedWindowHandlesByDisplay and mFocusedApplicationHandlesByDisplay. They store the focus application and focus window corresponding to each current display respectively. These maps are displayed in frameworks/native/services/inputlinker/dispatcher/ inputdispatcher H declared:
// key is display id, value is InputWindowHandle, i.e. focus window std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay GUARDED_BY(mLock); // key is display id, value is InputApplicationHandle, i.e. focus application std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay GUARDED_BY(mLock);
1.4 setInputWindowsLocked
setInputWindowsLocked is used to set the focus window. The relevant code is located in frameworks/native/services/inputlinker/dispatcher/inputdispatcher cpp:
void InputDispatcher::setInputWindowsLocked( const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) { ... // Find all current inputwindowhandles from mwindownesbydisplay according to displayId, including focus windows and non focus windows const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId); ... sp<InputWindowHandle> newFocusedWindowHandle = nullptr; bool foundHoveredWindow = false; for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) { // Set newFocusedWindowHandle to the top most focused window instead of the last one // Traverse the updated list of all windows, and set the window that is visible and gets the focus as the new focus window if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && windowHandle->getInfo()->visible) { newFocusedWindowHandle = windowHandle; } if (windowHandle == mLastHoverWindowHandle) { foundHoveredWindow = true; } } if (!foundHoveredWindow) { mLastHoverWindowHandle = nullptr; } // Find the current focus window from mFocusedWindowHandlesByDisplay according to displayId sp<InputWindowHandle> oldFocusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, displayId); // Different inputwindowhandles have different token s if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) { if (oldFocusedWindowHandle != nullptr) { if (DEBUG_FOCUS) { ALOGD("Focus left window: %s in display %" PRId32, oldFocusedWindowHandle->getName().c_str(), displayId); } // Get the corresponding InputChannel according to the Token in the InputWindowHandle sp<InputChannel> focusedInputChannel = getInputChannelLocked(oldFocusedWindowHandle->getToken()); if (focusedInputChannel != nullptr) { CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, "focus left window"); synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); // Add FocusEntry losing focus to mInboundQueue enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/); } // Remove historical InputWindowHandle from mFocusedWindowHandlesByDisplay mFocusedWindowHandlesByDisplay.erase(displayId); } if (newFocusedWindowHandle != nullptr) { if (DEBUG_FOCUS) { ALOGD("Focus entered window: %s in display %" PRId32, newFocusedWindowHandle->getName().c_str(), displayId); } // Update mFocusedWindowHandlesByDisplay mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; // Add FocusEntry to mInboundQueue to get focus enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/); } // Add a CommandEntry for focus change to the mCommandQueue to notify the upper level of focus window change if (mFocusedDisplayId == displayId) { onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); } } }
Each display corresponds to several inputwindowhandles and a focus InputWindowHandle. This method will update mwindownhandlesbydisplay and mFocusedWindowHandlesByDisplay according to the incoming InputWindowHandle list. The calling process is as follows:
-->frameworks/native/services/surfaceflinger/SurfaceFlinger.onMessageInvalidate -->frameworks/native/services/surfaceflinger/SurfaceFlinger.updateInputFlinger -->frameworks/native/services/surfaceflinger/SurfaceFlinger.updateInputWindowInfo -->frameworks/native/services/inputflinger/IInputFlinger.setInputWindows -->frameworks/native/services/inputflinger/InputManager.setInputWindows -->frameworks/native/services/inputflinger/InputDispatcher.setInputWindows -->frameworks/native/services/inputflinger/InputDispatcher.setInputWindowsLocked
1.5 setFocusedApplication
setFocusedApplication is used to set the focus application. The relevant codes are located in frameworks/native/services/inputlinker/dispatcher/inputdispatcher cpp:
void InputDispatcher::setFocusedApplication( int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) { if (DEBUG_FOCUS) { ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId, inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>"); } { // acquire lock std::scoped_lock _l(mLock); // Set focus application setFocusedApplicationLocked(displayId, inputApplicationHandle); } // release lock // Wake up poll loop since it may need to make new input dispatching choices. mLooper->wake(); }
1.6 dispatchEventLocked
dispatchEventLocked is used to pass input events. The relevant codes are located in frameworks/native/services/inputlinker/dispatcher/inputdispatcher cpp:
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry, const std::vector<InputTarget>& inputTargets) { ... for (const InputTarget& inputTarget : inputTargets) { // Find the Connection corresponding to InputChannel sp<Connection> connection = getConnectionLocked(inputTarget.inputChannel->getConnectionToken()); if (connection != nullptr) { // If the Connection is not empty, start preparing to distribute input events prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); } } }
The ispatchEventLocked function finds the corresponding Connection according to the InputTarget. It is the channel connecting the native application process. With it, you can start preparing to distribute input events.
1.7 prepareDispatchCycleLocked
The prepareDispatchCycleLocked function is used to prepare to distribute input events. In this function, the connection status will be verified. When the connection status is normal, the event will be queued. The relevant code is located in frameworks/native/services/inputlinker/dispatcher/inputdispatcher cpp:
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget& inputTarget) { // If the Connection status is abnormal, the direct return will not add the input event to the mOutbound queue if (connection->status != Connection::STATUS_NORMAL) { return; } ... // Queue input event enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget); }
1.8 enqueueDispatchEntriesLocked
enqueueDispatchEntriesLocked function is used to queue input events. The relevant codes are located in frameworks/native/services/inputlinker/dispatcher/inputdispatcher cpp:
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget& inputTarget) { ... // Determine whether the outboundQueue of the Connection is empty bool wasEmpty = connection->outboundQueue.empty(); // Queue input event enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_OUTSIDE); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_IS); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER); // If the outbound queue was previously empty, start the dispatch cycle going. if (wasEmpty && !connection->outboundQueue.empty()) { // If the previous outboundQueue is empty, and it is not empty after enqueueDispatchEntryLocked, the event distribution starts startDispatchCycleLocked(currentTime, connection); } }
1.9 startDispatchCycleLocked
startDispatchCycleLocked is used to distribute input events. The relevant codes are located in frameworks/native/services/inputlinker/dispatcher/inputdispatcher cpp:
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) { ... // After enqueueDispatchEntryLocked, connection->outboundqueue is not empty while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.front(); // Record the time of event distribution dispatchEntry->deliveryTime = currentTime; // The default timeout is 5s const nsecs_t timeout = getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken()); // The distribution timeout is the current time plus the default timeout dispatchEntry->timeoutTime = currentTime + timeout; // Publish the event. status_t status; EventEntry* eventEntry = dispatchEntry->eventEntry; switch (eventEntry->type) { case EventEntry::Type::MOTION: { MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); ... // Get event signature std::array<uint8_t, 32> hmac = getSignature(*motionEntry, *dispatchEntry); // Inputpublisher Publishmotionevent publishes the input event status = connection->inputPublisher .publishMotionEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId, motionEntry->deviceId, motionEntry->source, motionEntry->displayId, std::move(hmac), dispatchEntry->resolvedAction, motionEntry->actionButton, dispatchEntry->resolvedFlags, motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState, motionEntry->classification, xScale, yScale, xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, motionEntry->xCursorPosition, motionEntry->yCursorPosition, motionEntry->downTime, motionEntry->eventTime, motionEntry->pointerCount, motionEntry->pointerProperties, usingCoords); reportTouchEventForStatistics(*motionEntry); break; } ... } // After publishing, remove the DispatchEntry from the outboundQueue of the Connection connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(), connection->outboundQueue.end(), dispatchEntry)); // Then add the DispatchEntry to the waitQueue of the Connection connection->waitQueue.push_back(dispatchEntry); if (connection->responsive) { // Insert a record into AnrTracker mAnrTracker.insert(dispatchEntry->timeoutTime, connection->inputChannel->getConnectionToken()); } traceWaitQueueLength(connection); } }
1.10 publishMotionEvent
The publishMotionEvent function publishes events. The relevant code is located in frameworks/native/libs/input/inputtransport cpp:
status_t InputPublisher::publishMotionEvent( uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xScale, float yScale, float xOffset, float yOffset, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { InputMessage msg; msg.header.type = InputMessage::Type::MOTION; msg.body.motion.seq = seq; msg.body.motion.eventId = eventId; msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; msg.body.motion.displayId = displayId; msg.body.motion.hmac = std::move(hmac); msg.body.motion.action = action; msg.body.motion.actionButton = actionButton; msg.body.motion.flags = flags; msg.body.motion.edgeFlags = edgeFlags; msg.body.motion.metaState = metaState; msg.body.motion.buttonState = buttonState; msg.body.motion.classification = classification; msg.body.motion.xScale = xScale; msg.body.motion.yScale = yScale; msg.body.motion.xOffset = xOffset; msg.body.motion.yOffset = yOffset; msg.body.motion.xPrecision = xPrecision; msg.body.motion.yPrecision = yPrecision; msg.body.motion.xCursorPosition = xCursorPosition; msg.body.motion.yCursorPosition = yCursorPosition; msg.body.motion.downTime = downTime; msg.body.motion.eventTime = eventTime; msg.body.motion.pointerCount = pointerCount; for (uint32_t i = 0; i < pointerCount; i++) { msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]); msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); } // Encapsulate the input event into an InputMessage and continue through the inputchannel SendMessage send message return mChannel->sendMessage(&msg); }
1.11 sendMessage
sendMessage sends events to the application process. The relevant codes are located in frameworks/native/libs/input/inputtransport cpp:
status_t InputChannel::sendMessage(const InputMessage* msg) { const size_t msgLength = msg->size(); InputMessage cleanMsg; msg->getSanitizedCopy(&cleanMsg); ssize_t nWrite; do { // Send InputMessage to application process through socket nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); ...... return OK; }
summary
So far, the work of inputdispatcher has come to an end. The events are finally sent to the client through the socket. The following is the overall flow chart of inputdispatcher for your reference:
end of document
If you want to be an architect, you should not be limited to coding and business. You should be able to select, expand and improve your programming thinking. In addition, good career planning is also very important. The habit of learning is very important, but the most important thing is to persevere. Any plan that cannot be implemented is empty talk.
If you have no direction, here is a set of advanced notes on the eight Android modules prepared by Ali senior architects to help you systematize the messy, scattered and fragmented knowledge, so that you can systematically and efficiently master all the knowledge points of Android development.
Compared with the fragmented content we usually read, the knowledge points of this note are more systematic, easier to understand and remember, and are arranged strictly according to the knowledge system.
1, Necessary skills for architects to build foundations
1. Deep understanding of Java generics
2. Notes in simple terms
3. Concurrent programming
4. Data transmission and serialization
5. Principles of Java virtual machine
6. High efficiency IO
......
2, Source code analysis of top 100 Android frameworks
1.retro 2.0 source code analysis
2.Okhttp3 source code analysis
3.ButterKnife source code analysis
4.mpadroidchart source code analysis
5.Glide source code analysis
6.Leakcanary source code analysis
7. Universal lmage loader source code analysis
8.EventBus 3.0 source code analysis
9.zxing source code analysis
10.Picasso source code analysis
11.LottieAndroid usage details and source code analysis
12.Fresco source code analysis - picture loading process
3, Practical analysis of Android performance optimization
- Tencent Bugly: some understanding of string matching algorithm
- Iqiyi: Android APP crash capture scheme - xCrash
- Byte skipping: deeply understand one of the Gradle frameworks: Plugin, Extension, buildSrc
- Baidu APP Technology: Android H5 first screen Optimization Practice
- Alipay client architecture analysis: Android client startup speed optimization "garbage collection"
- Ctrip: component architecture practice from the perspective of Zhixing Android project
- Netease News Construction Optimization: how to make your construction speed "like lightning"?
- ...
4, Advanced kotlin enhanced combat
1. Getting started with Kotlin
2. Kotlin practical pit avoidance Guide
3. Project practice "Kotlin Jetpack practice"
-
Start with a Demo of worshiping the great God
-
What was Kotlin's experience writing Gradle scripts?
-
Three realms of Kotlin programming
-
Kotlin higher order function
-
Kotlin generics
-
Kotlin extension
-
Entrusted by Kotlin
-
Debugging skills of "unknown" coordination process
-
Graphic collaboration: suspend
5, Advanced decryption of Android advanced UI open source framework
1. Use of smartrefreshlayout
2.Android PullToRefresh control source code analysis
3. Basic usage of Android pulltorefresh pull-down refresh Library
4.LoadSir- efficient and easy-to-use loading feedback page management framework
5. Detailed explanation of Android general LoadingView loading framework
6.mpadroidchart implements LineChart (line chart)
7. Hellocharts Android User Guide
8.SmartTable User Guide
9. introduction to the open source project Android uitableview
10.ExcelPanel User Guide
11. Deep analysis of Android open source project SlidingMenu
12.MaterialDrawer User Guide
6, NDK module development
1. NDK module development
2. JNI module
3. Native development tools
4. Linux Programming
5. Bottom picture processing
6. Audio and video development
7. Machine learning
7, Advanced shuttle Technology
1. Overview of Flutter cross platform development
2. Setup of the fluent development environment in Windows
3. Write your first shuttle app
4. Setup and debugging of Flutter development environment
5. Basic grammar of Dart Grammar (1)
6. The use and source code analysis of the collection of Dart Grammar (2)
7. Set operator functions and source code analysis in Dart Grammar (3)
...
8, Wechat applet development
1. Applet overview and introduction
2. Applet UI development
3. API operation
4. Shopping mall project practice
Full set of video materials:
1, Interview collection
2, Source code analysis collection
3, Open source framework collection
You are welcome to support three links with one button. If you need the information in the text, you can directly click on the official certified wechat card of CSDN at the end of the text to receive [guarantee 100% free] ↓↓