An article to understand the Android Framework

This paper aims to describe the Framework of the Framework, mainly to record my learning about android framework for a period of time, hoping to attract jade and help readers.

preface

Write before:
1. Is it necessary to learn linux kernel?
I think it is necessary. Learning linux kernel helps us to deepen our understanding of some concepts, such as "process" and "thread". Recommended introductory course: the operating system course of Mr. Li Zhijun, MOOC, University of China
2. Is it necessary to compile the android source code yourself?
If you don't have to, you can directly use android studio to view the sdk source code, unless you want to debug some functions. But you have to practice yourself to be more familiar.

android framework is closely related to our development. This paper will start with startup, that is, the initialization of the framework, and then involve several use scenarios in the running process of Android. For example, the user starts the app (what happens after clicking the desktop icon), and the user uses the app (what does Android do with one touch). It involves some concepts that we often contact, such as the main thread, anr, handler, binder, zygote, what is the entry of the app, why the user-defined view should go in three steps, and answers them one by one. The source code involved is api 27.

1, Initialization article

When the boot key is pressed, the device first executes BootLoader, which is responsible for loading the Linux kernel from to memory and initializing the kernel. Finally, the kernel will read init RC file and start various service programs defined in the file. Android framework is just a Linux program for the kernel, and the program is in init Is defined in the RC file. The initialization process of the Android framework begins here.

The first one created is the zygote process, which is the first Dalvik virtual machine program running in the system. As the name suggests, all the later Dalvik virtual machine processes "hatch" through it (we all know that all human cells are divided by fertilized eggs, so I think this name is very accurate and ingenious).

The first Dalvik1 process hatched by zygote is called system server, which is a very important process of the Framework. SystemServer is only the alias of the process, and the corresponding program of the process is still app\_process, because SystemServer is from app\_ Hatched in process. Ams, Wms, Pms, etc. are all created in this process. It can be said that SystemServer manages all activities of the Framework.

Note 1: Andoird 4.4 introduces ART

A Socket2 client is created in the system server, and AmS is responsible for managing the client. After that, all Dalvik processes will be started indirectly through the Socket client. When you want to start a new APK process, AmS will send a start command to the Socket server of zygote process through the Socket client, and then zygote will hatch a new process.

Note 2: Socket, a method of communication in Android process, is involved here. Readers who have studied computer network should have a certain concept of it. pipe and binder are two process communication methods that will be mentioned later. In any case, their ultimate purpose is to make developers call across processes as if they were calling locally. As for their advantages, disadvantages and implementation methods, readers can explore them by themselves.

1. zygote startup

We mentioned earlier that when the kernel is initialized, it will start in init For the program configured in RC file, zygote relevant configuration information is as follows:

service zygote /system/bin/app\_process -Xzygote /system/bin --zygote --start-system-server

Briefly explain the meaning of this statement. The kernel will execute / system / bin / APP\_ The program in the process3 directory starts a service called zygote. The parameter -- start system server is valid only when the -- zygote parameter is specified, which means to tell Zygotelnit to hatch the first process SystemServer after startup. Therefore, one of the things zygote does after starting is to start the system server.

Note 3: after Android supports 64 bit, it will execute / system / bin / APP according to different ABI S\_ Process32 and / system/bin/app\_process64 directory.

When zygote service from App\_ After process starts, a Dalvik virtual machine will be started, and the first Java class executed by the virtual machine is ZygoteInit java. (APP process fork is from zygote process, so ZygoteInit.main is also the entry of app, but it will execute different logic according to different processes. This is why sometimes we can see "... ZygoteInit. Main" in the call stack of program error log... " The reason for this.) ZygoteInit will do two other things: first, as mentioned earlier, start a Socket service port, which is used to receive the command to start a new process; Second, most of the preloaded Framework classes and resources are used by subsequent apps. The zygote fork app process does not need to copy this part, but uses shared memory.

Summary: after zygote's process is started, it mainly does three things: start a Socket service to receive the command to start a new process, preload most of the Framework classes and resources, and start SystemServer.

2. Startup of SystemServer

System server is finally called to zygote in the zygote process Started by the forksystemserver method. After initialization, you will do some initial configuration, such as closing the Socket server (no zygote process does not need), configuring the SystemServer running environment, and then calling SystemServer.main.

After the system server is started, two things are mainly done: one is to start various service threads through the system server manager, such as AMS, WMS, PMS, etc, And register it with ServiceManager (AMS and WMS are closely related to the operation of app, and the specific contents will be expanded later); second, start HomeActivity, that is, start launcher. The startup of launcher is similar to that of ordinary app, which will be described in detail later.

3. Start of ServiceManager

The ServiceManager here is not in the java world, but in the native world. It is also through init RC configuration is started, and its function is equivalent to the DNS server of service4. Each service started by the system server will be registered in it. When making cross process calls with a binder, we first go back to query the ServiceManager to obtain the binder reference of the corresponding service, and then carry out subsequent operations. This process is similar to that we query DNS server through domain name to obtain ip and finally visit the website.

Note: when we check the Framework code, we will find the SystemServiceRegistry class. This class has nothing to do with the registration of system services. It just caches the tool classes for querying various services.

Note 4: this does not refer to Service, one of the four components of Android.

2, Runtime

When we use android devices, we are the runtime of the Framework. Next, I will start with two key scenarios: the first scenario, click the desktop icon. This scenario will involve android message mechanism, APP startup, activity creation, window creation and view drawing. In the second scenario, we are sliding the screen or clicking buttons. This scenario will involve how the Framework obtains hardware events and the event distribution mechanism of the app.

1. What happens when you click the desktop icon

Start of Activity

We all know that desktop programs, that is, launcher s, are basically no different from ordinary app s. When we click the desktop icon, we actually call the startActivity method of activity. Activity is a subclass 5 of Context, so it should have called the startActivity method of Context. However, activity overloads this method. The difference from Context is whether there is an intent FLAG\_ ACTIVITY\_ NEW\_ Task judgment. This is why we add intent when starting a Context (such as a Service) that is not an activity FLAG\_ ACTIVITY\_ NEW\_ TASK. Whether it is an activity or Context, it will eventually call the execStartActivity method of Instrumentation, and then call the startActivity method of Ams across processes through Binder.

Note 5: more precisely, Activity is a subclass of ContextWrapper, which is just a proxy class. In fact, the specific operations of an Activity are completed by its member variable mBase, which is a ContextImpl class (inheriting Context). Therefore, if the Context is an Interface, this is a standard static proxy mode.

Ams calls to the startActivityAsUser method and calls the startActivityMayWait method of ActivityStarter.

ActivityStarter.java
final int startActivityMayWait(...){
    ...
    //Find activity related information through PMS according to intent
    //How not in androidmanifest activity cannot be found after XML registration
    ResolveInfo rInfo = mSupervisor.resolveIntent(intent,                             resolvedType, userId);
    ...
    //See below
    int res = startActivityLocked(...);
    ...
}

int startActivityLocked(...){
    ...
    //See below
    mLastStartActivityResult = startActivity(...);
    ...
}

private int startActivity(...){
    ...
    //ActivityRecord is the record of activity in Ams, which corresponds to the activity of our app one by one,
    //It has a member variable, appToken is a binder class, and the activity of the following app is through this
    //Class communicates with the activity of Ams
    ActivityRecord r = new ActivityRecord(...);
    ...
    //Call another overload of startActivity, as shown below
    return startActivity(...);
}

private int startActivity(...){
    ...
    //See below
    result = startActivityUnchecked(...);
    ...
}

private int startActivityUnchecked(...){
    //Initialize some parameters, such as mlunchsingletop (whether launchMode is singletop), adjust mlunchflags, etc
    setInitialState(...);
    //Further adjust mlunchflags. For example, the original activity is singleinstance or the activity to be started is
    //When using singleinstance or singletask, make sure mlunchflags has FLAG_ACTIVITY_NEW_TASK properties
    computeLaunchingTaskFlags();
    ...
    //Find out if there are started activities
    ActivityRecord reusedActivity = getReusableIntentActivity();
    if (reusedActivity != null) {
        if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                    || isDocumentLaunchesIntoExisting(mLaunchFlags)
                    || mLaunchSingleInstance || mLaunchSingleTask) {
                //Clean up the task so that the target activity is at the top. Here we can understand
                //FLAG_ ACTIVITY_ CLEAR_ The principle of top or singletask.
                ...
                if (top != null) {
                   ...
                   //Callback onNewIntent
                    deliverNewIntent(top);
                }
                ...
            }
    }
    ...
    //Adjust the stack and task
    mTargetStack.startActivityLocked(...);
    ...
    //See below
    mSupervisor.resumeFocusedStackTopActivityLocked(...);
}

So far, let's summarize that Ams searches activity related information through PMS according to intent, which explains why it is not in Android manifest XML registration cannot be started. Then find the appropriate stack and task for the activity according to the activity's launchMode, taskAffinity and intent's launchFlags. The relationship among stack, task and ActivityRecord is shown in the following figure. Ams saves various state information of the activity through the ActivityRecord, and communicates with the activity of the app through its member variable appToken(binder class).

Pictures from the Internet

We will continue to talk about the resumefocussedstacktopactivitylocked method of ActivityStackSupervisor. This method will then call the resumeTopActivityUncheckedLocked method of ActivityStack, then call the resumeTopActivityInnerLocked method, and then return to the startSpecificActivityLocked method of ActivityStackSupervisor.

ActivityStackSupervisor.java
void startSpecificActivityLocked(...) {
    if (app != null && app.thread != null) {
        ...
        //If the app corresponding to the activity has been started, start the activity directly
        //See later for details
        realStartActivityLocked(...);
        ...
    }
    //Start the process through Ams, as shown below
    mService.startProcessLocked(...);
}

final boolean realStartActivityLocked(...){
    //Here's the app Thread is a binder class used to communicate with the app's ActivityThread
    //The scheduleLaunchActivity method of ActivityThread is called across processes through binder.
    app.thread.scheduleLaunchActivity();
}

Here, let's start the process through Ams. After Ams calls startProcessLocked, it will call another startProcessLocked overload

ActivityManagerService.java
final ProcessRecord startProcessLocked(...){
    ...
    if (app != null && app.pid > 0) {
        //If the app has been started and some conditions are met, it will return directly
    }
    ...
    //See below
    startProcessLocked(...);
    ...
}
private final void startProcessLocked(...){
    ...
    //entryPoint will be passed as a parameter through the socket, which will become the entry for the process java code execution
    if (entryPoint == null) entryPoint = "android.app.ActivityThread";
    ...
    //See below
    startResult = Process.start(entryPoint,...);
    ...
}

The start method of Process will immediately call the start method of ZygoteProcess, and then call the startViaZygote method.

ZygoteProcess.java
private Process.ProcessStartResult startViaZygote(...){
    ...
    //The openzygotesocketifneed method is used to establish a socket connection with the zygote process
    //We mentioned earlier that the zygote process will play the role of socket server, accept commands, and then fork in and out
    return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
    ...
}

private static Process.ProcessStartResult zygoteSendArgsAndGetResult(...){
    //Send the fork command and other parameters mentioned above, such as entryPoint
    ...
}

We return to the zygote process. When the zygote process is started, we call the main method of ZygoteInit for initialization, which will start the runSelectLoop thread of ZygoteServer and receive commands circularly. The main method is the processOneCommand method of ZygoteConnection.

ZygoteConnection.java
Runnable processOneCommand(...){
    //Read commands and parameters
    ...
    //fork process
    pid = Zygote.forkAndSpecialize(...);
    ...
    //Friends who have a certain understanding of c programming under linux will know that the pid of the child process after fork is 0
    if (pid == 0) {
        ...
        //Process subprocesses, see below
        return handleChildProc(parsedArgs, descriptors, childPipeFd);
    } 
}

private Runnable handleChildProc(...){
    ...
    return ZygoteInit.zygoteInit(...);
}

ZygoteInit.java
public static final Runnable zygoteInit(...){
    ...
    return RuntimeInit.applicationInit(...);
}

RuntimeInit.java
protected static Runnable applicationInit(...) {
    //args.startClass is the entryPoint we mentioned earlier, that is, "android.app.ActivityThread"
    //It can be seen that the first method called by app is the main method of ActivityThread, which is the entry of the application.
    return findStaticMain(args.startClass, args.startArgs, classLoader);
}

We have finally returned to our own process, and it is clear that the main method of ActivityThread (there is always an unspeakable intimacy when referring to the main method) is the entrance to the application. Then continue to explore.

ActivityThread.java
public static void main(String[] args) {
    //Create a looper. Looper is actually well understood, that is, it has been looping and taking instructions for execution.
    //(isn't the principle of our computer always refers to execution)
    Looper.prepareMainLooper();
    //Create an ActivityThread. At first, when we see the name, we will think it is a Thread class
    //In fact, it can also represent the main thread of the app, because it has sMainLooper,
    //Have sMainThreadHandler, which will deal with Ams and other system services
    //In my personal understanding, activity is the activity, and thread is the line. It is a line that connects the activities of all app s.
    ActivityThread thread = new ActivityThread();
    //Establish Binder channel with Ams, see below
    thread.attach(false);
    //Create a handler, which is actually a tool. Let's put messages into the MessageQueue, remove messages and process messages
    //looper is the protagonist. looper will always get messages from MessageQueue and execute them
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    ...
    Looper.loop();
}

 private void attach(boolean system) {
    ...
    //mgr is a binder class used to communicate with Ams
    mgr.attachApplication(mAppThread);
    ...
 }

Seeing this, we already know what the main thread is. The thread on which the process initially runs is the main thread. And then we will find a "terrible" fact, Our app will always run in sMainLooper (that is, in the main process, excluding other threads we create), including starting activity, sending touch messages and key messages. We will hand them over to sMainLooper through sMainThreadHandler (this is also the principle of synchronization through handler during development). Now that we know the concept of main thread, the principle of anr is well understood. sMainLooper will anr if it processes this message for more than 5s (activity) or 20s (service). This is a way to ensure the user experience.

Next, we return to Ams, which will then call the attachApplicationLocked method.

ActivityManagerService.java
private final boolean attachApplicationLocked(...){
    ...
    //Notify ActivityThread to create Application through binder IPC
    thread.bindApplication(...);
    ...
    mStackSupervisor.attachApplicationLocked(app);
    ...
}

ActivityStackSupervisor.java
boolean attachApplicationLocked(...) throws RemoteException {
    ...
    //Look what we found, deja vu. Yes, that's the problem we left above.
    //The reason is very simple. When we start an activity, we find that the process is not started,
    //When we start the process, of course, we have to restart the activity
    realStartActivityLocked(...);
    ...
}

final boolean realStartActivityLocked(...){
    ...
    //The thread here is a binder class, which corresponds to the ActivityThread
    app.thread.scheduleLaunchActivity(...);
    ...
}

Back to the app process, let's first look at the process of creating an Application

ActivityThread$ApplicationThread.java
public final void bindApplication(...){
    //Call the handleBindApplication method through the handler, and then call the performLaunchActivity method
    sendMessage(H.BIND_APPLICATION, data);
}

ActivityThread.java
private void handleBindApplication(AppBindData data) {
    ...
    //Create Instrumentation
    mInstrumentation = new Instrumentation();
    ...
    //Create an Application and call back the attachBaseContext method
    app = data.info.makeApplication(data.restrictedBackupMode, null);
    ...
    //Callback onCreate method
    mInstrumentation.callApplicationOnCreate(app);
    ...
}

Next, let's look at the process of creating an Activity

ActivityThread$ApplicationThread.java
public final void scheduleLaunchActivity(...){
    //Call the handleLaunchActivity method through the handler, and then call the performLaunchActivity method
    sendMessage(H.LAUNCH_ACTIVITY, r);
}

ActivityThread.java
private Activity performLaunchActivity(...){
    //The real Context class is the mBase mentioned above
    ContextImpl appContext = createBaseContextForActivity(r);
    ...
    //Create activity with emission
    activity = mInstrumentation.newActivity(...);
    ...
    //Assign appContext to mBase and call back attachBaseContext(context);
    //getInstrumentation() mentioned earlier calling the execStartActivity method of Instrumentation
    //r.token is a binder class and corresponds to the ActivityRecord of Ams. Another article (see Note 6) mentioned that it plays an important role
    activity.attach(appContext, this, getInstrumentation(),r.token,...,app,...,window,...);
    ...
    //Callback onCreate
    mInstrumentation.callActivityOnCreate(...);
}

So far, the framework of startActivity has been described.

Drawing of View

This part is not about custom views, Instead, it creates windows (including adding and drawing). From the perspective of WmS, a Window is not a Window class, but a view class. When WmS receives a user's message, it needs to send the message to the Window. The view class itself cannot directly receive the message delivered by WmS. The IWindow class must really receive the user's message (binder class), and the IWindow class is implemented by the ViewRoot.W class. Each w contains a view variable. These two sentences are quoted from Android kernel analysis. From the later explanation, we can see that the Window class is more an abstraction of the Window, and the view is the content of the Window.

Three windows are defined in the Framework: application window, sub window and system window. The application window corresponds to an activity, The next step is to explain the application window The creation of the window (hereinafter referred to as window). Since the window corresponds to a Activity, the window is created in the process of startActivity. It is mentioned that the creation of Activity will callback onCreate, and we will call setContentView method when we develop it. SetContentView will call setContentView method of Window class, if you go to see attach of Activity. Method, you will find that the window class is actually a PhoneWindow class.

PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
    ...
    installDecor();
    ...
    //Load the customized layout of the app. It can be seen from the following that our layout is wrapped with at least two view s,
    //First by mContentParent and then by mDecor
    mLayoutInflater.inflate(layoutResID, mContentParent);
    ...
}

private void installDecor() {
    ...
    //Create a decorview (inherited from FrameLayout). As the name suggests, all the contents of the window are displayed in this view,
    //This View is the root of all views of the Activity.
    //This is why we view the Activity view hierarchy, and the outermost layer is FrameLayout
    mDecor = generateDecor(-1);
    ...
    //Configure different xml layouts according to different style s,
    //These xml have a common feature: they all have an id of id_ ANDROID_ Content view
    //Therefore, ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT) can be used
    //Assign value to contentParent
    mContentParent = generateLayout(mDecor);
    ...
}  

This step only creates the View, and the next two steps will be involved: 1. Add the window to Wms, and Wms will manage the window in a certain form like Ams. 2. Convert the content to be displayed by the View into data, save it in the screen buffer memory, and hand it over to the system for drawing.

In the process of startActivity, handleResumeActivity will be called after the Activity is created.

ActivityThread.java
private void handleLaunchActivity(...){
    ...
    Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        ...
        //See below
        handleResumeActivity(...);
        ...
    }
}

final void handleResumeActivity(...){
    ...
    //Callback onResume
    r = performResumeActivity(token, clearHide, reason);
    ...
    //Make decor invisible
    View decor = r.window.getDecorView();
    decor.setVisibility(View.INVISIBLE);
    ...
    //Call makeVisible of Activity
    r.activity.makeVisible();
    ...
}

Activity.java
void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        //Finally, addView of WindowManagerGlobal will be called
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
    }

WindowManagerGlobal.java
public void addView(...) {
    ...
    //Create viewrootimpl (abstract of view root)
    root = new ViewRootImpl(view.getContext(), display);
    view.setLayoutParams(wparams);
    //Cache the view (mdecor in this case), root (an abstraction of view) and layoutParam
    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    ...
    //See below
    root.setView(view, wparams, panelParentView);
    ...
}

ViewRootImpl.java
public void setView(...) {
    ...
    //The following section continues
    requestLayout();
    ...
    //ipc to Wms, and the Session class is the server
    //mInputChannel is an InputChannel class, which is the implementation of the pipeline in the java layer. It will be described in detail later when we talk about Android events
    res = mWindowSession.addToDisplay(...,mInputChannel);
    ...
}

Session.java
@Override
public int addToDisplay(...) {
    //See below
    return mService.addWindow(...);
}

WindowManagerService.java
public int addWindow(...) {
    //Create WindowState
    //Session is the Binder server of the session
    //Client is the Binder client of IWindow
    final WindowState win = new WindowState(..., session, client,...);
    ...
    //The windowsaddedlocked method of the Session will be called, as shown below
    win.attach();
    //Cache win
    mWindowMap.put(client.asBinder(), win);
    ...
    if (win.canReceiveKeys()) {
        //If the window is interactive (for example, Toast is non interactive), update the focus window
        focusChanged = updateFocusedWindowLocked(...);
        ...
    }
}

Session.java
void windowAddedLocked(String packageName) {
    ...
    if (mSurfaceSession == null) {
        //Create SurfaceSession, which is a class that directly interacts with surfacelinker,
        //Used to add, delete, transform windows to SurfaceFlinger. Each application corresponds to only one Session object,
        //Therefore, mSurfaceSession will actually be created only once,
        //That is, a SurfaceSession object will be constructed when the first window in the application is created,
        //Subsequent window additions in the same application will no longer construct the SurfaceSession object
        mSurfaceSession = new SurfaceSession();
        ...
        //Save Session
        mService.mSessions.add(this);
        ...
    }
    ...
}

Here you can see that Wms saves the information of the window, that is, manages it, which is the basis of event distribution.

Returning to the requestLayout method above, requestLayout calls the scheduleTraversals method, which initiates a View tree traversal message. The message is processed asynchronously, and the corresponding processing function is the performttraversals method.

ViewRootImpl.java
private void performTraversals() {
    final View host = mView;
    if (mFirst) {
        ...
        //If the window is displayed for the first time, initialize attachinfo and assign it to mView,
        //Call the dispatch method of the ViewGroup to recursively pass the attachinfo to the child View
        host.dispatchAttachedToWindow(mAttachInfo, 0);
    }
    ...
    //If the window size changes, call host Measure() recalculates the size of the view in the window
    //Android's View and ViewGroup are classic combination modes
    //The measure process will traverse the entire View tree and call the measure of each View and the callback onMeasure
    //The process of layout and draw is similar
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
    //According to the execution results of the seven steps, judge whether it is necessary to rearrange. For example, when the size of any view changes,
    //It affects the layout of other views
    performLayout(lp, mWidth, mHeight);
    ...
    //If it is judged that the painting is not cancelled and it is not a new surface, start painting
    //The meaning of newSurface variable pull means that a Surface object has been created in ViewRoot, but the object has not been allocated real video memory by WmS,
    //In ViewRoot, it is called windowsession Relayoutwindow() allocates real video memory in the Surface object,
    //In general, the newSurface here is false.
    performDraw();
    ...
}

For the first time, since the window setting is invisible, the previous code can see that the makeVisible method of Activity will call mdecor SetVisibility (View.VISIBLE), after a series of calls, will be called to the performTraversals method of ViewRootImpl again, then the performDraw method will be called.

Before we talk about drawing, we should first clarify several concepts.

  1. Surface:

Surface is a handle to the raw image buffer. That is to say, surface corresponds to a section of memory whose data is the content to be drawn. The surface class essentially represents a plane

  1. Canvas:

We know that drawing different patterns is obviously a work, not a piece of data. Android uses Canvas class to represent these operations, that is, Canvas is the function class drawn. Look at the description of Canvas: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap) - the function of Canvas is to respond to the call of draw and write it into the bitmap, which is used to save all pixels, so you will know the concept of Canvas better.

ViewRootImpl.java
private void performDraw() {
    ...
    draw(fullRedrawNeeded);
    ...
}

private void draw(boolean fullRedrawNeeded) {
    //Previously applied to Wms
    Surface surface = mSurface;
    ...
    if (!drawSoftware(...)) {
        return;
    }
    ...
}

private boolean drawSoftware(...) {
    ...
    //Get canvas and lock
    canvas = mSurface.lockCanvas(dirty);
    ...
    //Traverse the draw of subclasses. Obviously, this process is to convert the contents of all subclasses into pixels and write them into the bitmap of Canvas
    mView.draw(canvas);
    ...
    //Submit the Canvas that saves all view contents to the surface and submit it to the bottom layer of the system for drawing.
    surface.unlockCanvasAndPost(canvas);
    ...
}

So far, the framework drawn by view has been described.

2. One touch, what did Android do

This title comes from One touch, what did Android do So much of the following will be quoted from this article.

What does a touch mean? In the process of using Android devices, clicking, long pressing, sliding (TouchEvent) and pressing the entity key (KeyEvent) can become "one touch". Therefore, one touch can represent all the operations we use.

Of course, our touch starts with the hardware, which will pass events to the kernel and the kernel to the Framework. As mentioned earlier, various services will be started when the system server is started:

SystemServer.java
public static void main(String[] args) {
    new SystemServer().run();
}
private void run() {
    ...
    startOtherServices();
    ...
}
private void startOtherServices() {
    ...
    //Create InputManagerService
    inputManager = new InputManagerService(context);
    ...
    //Create WindowManagerService and hold InputManagerService
    wm = WindowManagerService.main(..., inputManager,...);
    ...
    //Set InputMonitor as callback and start InputManagerService
    inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
    inputManager.start();
}

InputManagerService.java
public InputManagerService(Context context) {
    ...
    //Initialize the native object (mPtr is long and saves the pointer of the native object, which is a common method used by jni to keep the native object)
    //InputManagerService corresponds to the native inputmanager cpp
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    ...
}
public void start() {
    ...
    //Used to start the two threads mentioned below
    nativeStart(mPtr);
    ...
}

InputManager.cpp
InputManager::InputManager(...) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
/**
From the above code, we can see that InputDispatcher and InputReader are created during InputManager initialization
 And InputReaderThread and inputdispatcher thread. InputReader is used to read input information (i.e. various events),
InputDispatcher Used to distribute events. Two threads are obviously used to run these two functions.
*/

The InputReader thread will continuously call the driver of the input device to read all messages input by the user. Both the thread and the InputDispatcher thread run in the system \ _processspace. The InputDispatcher thread takes out the original message from its message queue. The extracted message may be distributed in two ways. The first method is to send it directly to the customer window through the pipe. The other method is to send it to the WmS first, and the WmS will process it. If the WmS does not process the message, it will be sent to the customer window. Otherwise, it will not be sent to the customer window (quoted from Android kernel analysis). As shown in the figure (the picture is also from Android kernel analysis):

If it is a key message, InputDispatcher will first call back the callback function defined in InputManager, which will not only call back the callback function in InputMonitor, but also call the related functions defined in WmS. For system key messages, such as "Home" key and telephone key, WmS will process them by default. If events are consumed, InputDispatcher will not continue to pass these key messages to the customer window. For touch screen messages, InputDispatcher will directly pass them to the customer window.

InputManagerService.java
//Callback of key events. We mentioned earlier that the callback object is InputMonitor
// Native callback.
private long interceptKeyBeforeDispatching(InputWindowHandle focus,
           KeyEvent event, int policyFlags) {
   return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}

InputMonitor.java
@Override
public long interceptKeyBeforeDispatching(
           InputWindowHandle focus, KeyEvent event, int policyFlags) {
   WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
   //Callback Wms. mPolicy is created during SystemServer initialization. It is a PhoneWindowManager class. You can see the processing of various keys
   return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
}

When InputDispatcher is passed directly to the window, find the window that currently gets the focus through findtouchedwindow atlocked method, and then conduct ipc through Pipe. That is to say, a set of window related information will be maintained in the native layer. Let's go back to the process of adding windows in Wms:

WindowManagerService.java
public int addWindow(...) {
   //Create a WindowState, and the InputWindowHandle will be created in the construction method
   final WindowState win = new WindowState(...);
   ...
   //Establish pipeline communication, where outInputChannel is passed from app process
   win.openInputChannel(outInputChannel Is from app process);
   ...
   //Update the information of window focus change to the native layer. Similarly, it will be updated when the window is switched or destroyed
   mInputMonitor.updateInputWindowsLw(false /*force*/);
   ...
}

WindowState.java
void openInputChannel(InputChannel outInputChannel) {
   ...
   InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
   mInputChannel = inputChannels[0];
   mClientChannel = inputChannels[1];
   /*Channel[0]Save on the server side*/
   mInputWindowHandle.inputChannel = inputChannels[0];
   ...
   /* Channel[1]Return to ViewRootImpl end of app*/
   mClientChannel.transferTo(outInputChannel);
   ...
   /*Register to the InputManagerService native layer*/
   mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}

It can be seen that a group of windowstates are maintained in Wms for window creation, destruction and switching, while a group of inputwindowhandles are maintained in InputManagerService for event distribution.

Let's go back to ViewRootImpl. When adding a window, we called the setView method.

ViewRootImpl.java
public void setView(...) {
    ...
    //InputChannel class is the implementation of pipeline in java layer
    mInputChannel = new InputChannel();
    ...
    //View iwindowsession Aidl will find that the mInputChannel here is marked out,
    //That is, changes in another process will be synchronized to this process
    res = mWindowSession.addToDisplay(...,mInputChannel);
    ...
    if (mInputChannel != null) {
        if (mInputQueueCallback != null) {
            mInputQueue = new InputQueue();
            mInputQueueCallback.onInputQueueCreated(mInputQueue);
        }
        //Create a callback for the pipeline, see below
        mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
    }
}

ViewRootImpl$WindowInputEventReceiver.java
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
    //See below
    super(inputChannel, looper);
}

InputEventReceiver.java
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
    ...
    //Establish a callback for the pipeline, and the native layer will call back to the InputEventReceiver when it receives the event
    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                    inputChannel, mMessageQueue);
    ...
}

When the InputDispatcher of InputManagerService passes messages to the app process through the pipeline, the pipeline of the app process will call back InputEventReceiver (i.e. WindowInputEventReceiver) for event distribution. The following code can be said to be the standard answer to the responsibility chain mode, which is very exciting. Readers can learn by themselves.

summary

Android Framework can be said to be a huge project. If we fall into details at the beginning, we can't go a way. Gentlemen are good at fake things. With the help of the research and learning results of big men, we can learn the overall framework first and break it individually when necessary. Thank you very much. The following references may be omitted. I apologize here! I hope this article is helpful to readers.

This article is transferred from https://juejin.cn/post/6844903717301387272 , in case of infringement, please contact to delete. This paper aims to describe the Framework of the Framework, mainly to record my learning about android framework for a period of time, hoping to attract jade and help readers.

preface

Write before:
1. Is it necessary to learn linux kernel?
I think it is necessary. Learning linux kernel helps us to deepen our understanding of some concepts, such as "process" and "thread". Recommended introductory course: the operating system course of Mr. Li Zhijun, MOOC, University of China
2. Is it necessary to compile the android source code yourself?
If you don't have to, you can directly use android studio to view the sdk source code, unless you want to debug some functions. But you have to practice yourself to be more familiar.

android framework is closely related to our development. This paper will start with startup, that is, the initialization of the framework, and then involve several use scenarios in the running process of Android. For example, the user starts the app (what happens after clicking the desktop icon), and the user uses the app (what does Android do with one touch). It involves some concepts that we often contact, such as the main thread, anr, handler, binder, zygote, what is the entry of the app, why the user-defined view should go in three steps, and answers them one by one. The source code involved is api 27.

1, Initialization article

When the boot key is pressed, the device first executes BootLoader, which is responsible for loading the Linux kernel from to memory and initializing the kernel. Finally, the kernel will read init RC file and start various service programs defined in the file. Android framework is just a Linux program for the kernel, and the program is in init Is defined in the RC file. The initialization process of the Android framework begins here.

The first one created is the zygote process, which is the first Dalvik virtual machine program running in the system. As the name suggests, all the later Dalvik virtual machine processes "hatch" through it (we all know that all human cells are divided by fertilized eggs, so I think this name is very accurate and ingenious).

The first Dalvik1 process hatched by zygote is called system server, which is a very important process of the Framework. SystemServer is only the alias of the process, and the corresponding program of the process is still app\_process, because SystemServer is from app\_ Hatched in process. Ams, Wms, Pms, etc. are all created in this process. It can be said that SystemServer manages all activities of the Framework.

Note 1: Andoird 4.4 introduces ART

A Socket2 client is created in the system server, and AmS is responsible for managing the client. After that, all Dalvik processes will be started indirectly through the Socket client. When you want to start a new APK process, AmS will send a start command to the Socket server of zygote process through the Socket client, and then zygote will hatch a new process.

Note 2: Socket, a method of communication in Android process, is involved here. Readers who have studied computer network should have a certain concept of it. pipe and binder are two process communication methods that will be mentioned later. In any case, their ultimate purpose is to make developers call across processes as if they were calling locally. As for their advantages, disadvantages and implementation methods, readers can explore them by themselves.

1. zygote startup

We mentioned earlier that when the kernel is initialized, it will start in init For the program configured in RC file, zygote relevant configuration information is as follows:

service zygote /system/bin/app\_process -Xzygote /system/bin --zygote --start-system-server

Briefly explain the meaning of this statement. The kernel will execute / system / bin / APP\_ The program in the process3 directory starts a service called zygote. The parameter -- start system server is valid only when the -- zygote parameter is specified, which means to tell Zygotelnit to hatch the first process SystemServer after startup. Therefore, one of the things zygote does after starting is to start the system server.

Note 3: after Android supports 64 bit, it will execute / system / bin / APP according to different ABI S\_ Process32 and / system/bin/app\_process64 directory.

When zygote service from App\_ After process starts, a Dalvik virtual machine will be started, and the first Java class executed by the virtual machine is ZygoteInit java. (APP process fork is from zygote process, so ZygoteInit.main is also the entry of app, but it will execute different logic according to different processes. This is why sometimes we can see "... ZygoteInit. Main" in the call stack of program error log... " The reason for this.) ZygoteInit will do two other things: first, as mentioned earlier, start a Socket service port, which is used to receive the command to start a new process; Second, most of the preloaded Framework classes and resources are used by subsequent apps. The zygote fork app process does not need to copy this part, but uses shared memory.

Summary: after zygote's process is started, it mainly does three things: start a Socket service to receive the command to start a new process, preload most of the Framework classes and resources, and start SystemServer.

2. Startup of SystemServer

System server is finally called to zygote in the zygote process Started by the forksystemserver method. After initialization, you will do some initial configuration, such as closing the Socket server (no zygote process does not need), configuring the SystemServer running environment, and then calling SystemServer.main.

After the system server is started, two things are mainly done: one is to start various service threads through the system server manager, such as AMS, WMS, PMS, etc, And register it with ServiceManager (AMS and WMS are closely related to the operation of app, and the specific contents will be expanded later); second, start HomeActivity, that is, start launcher. The startup of launcher is similar to that of ordinary app, which will be described in detail later.

3. Start of ServiceManager

The ServiceManager here is not in the java world, but in the native world. It is also through init RC configuration is started, and its function is equivalent to the DNS server of service4. Each service started by the system server will be registered in it. When making cross process calls with a binder, we first go back to query the ServiceManager to obtain the binder reference of the corresponding service, and then carry out subsequent operations. This process is similar to that we query DNS server through domain name to obtain ip and finally visit the website.

Note: when we check the Framework code, we will find the SystemServiceRegistry class. This class has nothing to do with the registration of system services. It just caches the tool classes for querying various services.

Note 4: this does not refer to Service, one of the four components of Android.

2, Runtime

When we use android devices, we are the runtime of the Framework. Next, I will start with two key scenarios: the first scenario, click the desktop icon. This scenario will involve android message mechanism, APP startup, activity creation, window creation and view drawing. In the second scenario, we are sliding the screen or clicking buttons. This scenario will involve how the Framework obtains hardware events and the event distribution mechanism of the app.

1. What happens when you click the desktop icon

Start of Activity

We all know that desktop programs, that is, launcher s, are basically no different from ordinary app s. When we click the desktop icon, we actually call the startActivity method of activity. Activity is a subclass 5 of Context, so it should have called the startActivity method of Context. However, activity overloads this method. The difference from Context is whether there is an intent FLAG\_ ACTIVITY\_ NEW\_ Task judgment. This is why we add intent when starting a Context (such as a Service) that is not an activity FLAG\_ ACTIVITY\_ NEW\_ TASK. Whether it is an activity or Context, it will eventually call the execStartActivity method of Instrumentation, and then call the startActivity method of Ams across processes through Binder.

Note 5: more precisely, Activity is a subclass of ContextWrapper, which is just a proxy class. In fact, the specific operations of an Activity are completed by its member variable mBase, which is a ContextImpl class (inheriting Context). Therefore, if the Context is an Interface, this is a standard static proxy mode.

Ams calls to the startActivityAsUser method and calls the startActivityMayWait method of ActivityStarter.

ActivityStarter.java
final int startActivityMayWait(...){
    ...
    //Find activity related information through PMS according to intent
    //How not in androidmanifest activity cannot be found after XML registration
    ResolveInfo rInfo = mSupervisor.resolveIntent(intent,                             resolvedType, userId);
    ...
    //See below
    int res = startActivityLocked(...);
    ...
}

int startActivityLocked(...){
    ...
    //See below
    mLastStartActivityResult = startActivity(...);
    ...
}

private int startActivity(...){
    ...
    //ActivityRecord is the record of activity in Ams, which corresponds to the activity of our app one by one,
    //It has a member variable, appToken is a binder class, and the activity of the following app is through this
    //Class communicates with the activity of Ams
    ActivityRecord r = new ActivityRecord(...);
    ...
    //Call another overload of startActivity, as shown below
    return startActivity(...);
}

private int startActivity(...){
    ...
    //See below
    result = startActivityUnchecked(...);
    ...
}

private int startActivityUnchecked(...){
    //Initialize some parameters, such as mlunchsingletop (whether launchMode is singletop), adjust mlunchflags, etc
    setInitialState(...);
    //Further adjust mlunchflags. For example, the original activity is singleinstance or the activity to be started is
    //When using singleinstance or singletask, make sure mlunchflags has FLAG_ACTIVITY_NEW_TASK properties
    computeLaunchingTaskFlags();
    ...
    //Find out if there are started activities
    ActivityRecord reusedActivity = getReusableIntentActivity();
    if (reusedActivity != null) {
        if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                    || isDocumentLaunchesIntoExisting(mLaunchFlags)
                    || mLaunchSingleInstance || mLaunchSingleTask) {
                //Clean up the task so that the target activity is at the top. Here we can understand
                //FLAG_ ACTIVITY_ CLEAR_ The principle of top or singletask.
                ...
                if (top != null) {
                   ...
                   //Callback onNewIntent
                    deliverNewIntent(top);
                }
                ...
            }
    }
    ...
    //Adjust the stack and task
    mTargetStack.startActivityLocked(...);
    ...
    //See below
    mSupervisor.resumeFocusedStackTopActivityLocked(...);
}

So far, let's summarize that Ams searches activity related information through PMS according to intent, which explains why it is not in Android manifest XML registration cannot be started. Then find the appropriate stack and task for the activity according to the activity's launchMode, taskAffinity and intent's launchFlags. The relationship among stack, task and ActivityRecord is shown in the following figure. Ams saves various state information of the activity through the ActivityRecord, and communicates with the activity of the app through its member variable appToken(binder class).

Pictures from the Internet

We will continue to talk about the resumefocussedstacktopactivitylocked method of ActivityStackSupervisor. This method will then call the resumeTopActivityUncheckedLocked method of ActivityStack, then call the resumeTopActivityInnerLocked method, and then return to the startSpecificActivityLocked method of ActivityStackSupervisor.

ActivityStackSupervisor.java
void startSpecificActivityLocked(...) {
    if (app != null && app.thread != null) {
        ...
        //If the app corresponding to the activity has been started, start the activity directly
        //See later for details
        realStartActivityLocked(...);
        ...
    }
    //Start the process through Ams, as shown below
    mService.startProcessLocked(...);
}

final boolean realStartActivityLocked(...){
    //Here's the app Thread is a binder class used to communicate with the app's ActivityThread
    //The scheduleLaunchActivity method of ActivityThread is called across processes through binder.
    app.thread.scheduleLaunchActivity();
}

Here, let's start the process through Ams. After Ams calls startProcessLocked, it will call another startProcessLocked overload

ActivityManagerService.java
final ProcessRecord startProcessLocked(...){
    ...
    if (app != null && app.pid > 0) {
        //If the app has been started and some conditions are met, it will return directly
    }
    ...
    //See below
    startProcessLocked(...);
    ...
}
private final void startProcessLocked(...){
    ...
    //entryPoint will be passed as a parameter through the socket, which will become the entry for the process java code execution
    if (entryPoint == null) entryPoint = "android.app.ActivityThread";
    ...
    //See below
    startResult = Process.start(entryPoint,...);
    ...
}

The start method of Process will immediately call the start method of ZygoteProcess, and then call the startViaZygote method.

ZygoteProcess.java
private Process.ProcessStartResult startViaZygote(...){
    ...
    //The openzygotesocketifneed method is used to establish a socket connection with the zygote process
    //We mentioned earlier that the zygote process will play the role of socket server, accept commands, and then fork in and out
    return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
    ...
}

private static Process.ProcessStartResult zygoteSendArgsAndGetResult(...){
    //Send the fork command and other parameters mentioned above, such as entryPoint
    ...
}

We return to the zygote process. When the zygote process is started, we call the main method of ZygoteInit for initialization, which will start the runSelectLoop thread of ZygoteServer and receive commands circularly. The main method is the processOneCommand method of ZygoteConnection.

ZygoteConnection.java
Runnable processOneCommand(...){
    //Read commands and parameters
    ...
    //fork process
    pid = Zygote.forkAndSpecialize(...);
    ...
    //Friends who have a certain understanding of c programming under linux will know that the pid of the child process after fork is 0
    if (pid == 0) {
        ...
        //Process subprocesses, see below
        return handleChildProc(parsedArgs, descriptors, childPipeFd);
    } 
}

private Runnable handleChildProc(...){
    ...
    return ZygoteInit.zygoteInit(...);
}

ZygoteInit.java
public static final Runnable zygoteInit(...){
    ...
    return RuntimeInit.applicationInit(...);
}

RuntimeInit.java
protected static Runnable applicationInit(...) {
    //args.startClass is the entryPoint we mentioned earlier, that is, "android.app.ActivityThread"
    //It can be seen that the first method called by app is the main method of ActivityThread, which is the entry of the application.
    return findStaticMain(args.startClass, args.startArgs, classLoader);
}

We have finally returned to our own process, and it is clear that the main method of ActivityThread (there is always an unspeakable intimacy when referring to the main method) is the entrance to the application. Then continue to explore.

ActivityThread.java
public static void main(String[] args) {
    //Create a looper. Looper is actually well understood, that is, it has been looping and taking instructions for execution.
    //(isn't the principle of our computer always refers to execution)
    Looper.prepareMainLooper();
    //Create an ActivityThread. At first, when we see the name, we will think it is a Thread class
    //In fact, it can also represent the main thread of the app, because it has sMainLooper,
    //Have sMainThreadHandler, which will deal with Ams and other system services
    //In my personal understanding, activity is the activity, and thread is the line. It is a line that connects the activities of all app s.
    ActivityThread thread = new ActivityThread();
    //Establish Binder channel with Ams, see below
    thread.attach(false);
    //Create a handler, which is actually a tool. Let's put messages into the MessageQueue, remove messages and process messages
    //looper is the protagonist. looper will always get messages from MessageQueue and execute them
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    ...
    Looper.loop();
}

 private void attach(boolean system) {
    ...
    //mgr is a binder class used to communicate with Ams
    mgr.attachApplication(mAppThread);
    ...
 }

Seeing this, we already know what the main thread is. The thread on which the process initially runs is the main thread. And then we will find a "terrible" fact, Our app will always run in sMainLooper (that is, in the main process, excluding other threads we create), including starting activity, sending touch messages and key messages. We will hand them over to sMainLooper through sMainThreadHandler (this is also the principle of synchronization through handler during development). Now that we know the concept of main thread, the principle of anr is well understood. sMainLooper will anr if it processes this message for more than 5s (activity) or 20s (service). This is a way to ensure the user experience.

Next, we return to Ams, which will then call the attachApplicationLocked method.

ActivityManagerService.java
private final boolean attachApplicationLocked(...){
    ...
    //Notify ActivityThread to create Application through binder IPC
    thread.bindApplication(...);
    ...
    mStackSupervisor.attachApplicationLocked(app);
    ...
}

ActivityStackSupervisor.java
boolean attachApplicationLocked(...) throws RemoteException {
    ...
    //Look what we found, deja vu. Yes, that's the problem we left above.
    //The reason is very simple. When we start an activity, we find that the process is not started,
    //When we start the process, of course, we have to restart the activity
    realStartActivityLocked(...);
    ...
}

final boolean realStartActivityLocked(...){
    ...
    //The thread here is a binder class, which corresponds to the ActivityThread
    app.thread.scheduleLaunchActivity(...);
    ...
}

Back to the app process, let's first look at the process of creating an Application

ActivityThread$ApplicationThread.java
public final void bindApplication(...){
    //Call the handleBindApplication method through the handler, and then call the performLaunchActivity method
    sendMessage(H.BIND_APPLICATION, data);
}

ActivityThread.java
private void handleBindApplication(AppBindData data) {
    ...
    //Create Instrumentation
    mInstrumentation = new Instrumentation();
    ...
    //Create an Application and call back the attachBaseContext method
    app = data.info.makeApplication(data.restrictedBackupMode, null);
    ...
    //Callback onCreate method
    mInstrumentation.callApplicationOnCreate(app);
    ...
}

Next, let's look at the process of creating an Activity

ActivityThread$ApplicationThread.java
public final void scheduleLaunchActivity(...){
    //Call the handleLaunchActivity method through the handler, and then call the performLaunchActivity method
    sendMessage(H.LAUNCH_ACTIVITY, r);
}

ActivityThread.java
private Activity performLaunchActivity(...){
    //The real Context class is the mBase mentioned above
    ContextImpl appContext = createBaseContextForActivity(r);
    ...
    //Create activity with emission
    activity = mInstrumentation.newActivity(...);
    ...
    //Assign appContext to mBase and call back attachBaseContext(context);
    //getInstrumentation() mentioned earlier calling the execStartActivity method of Instrumentation
    //r.token is a binder class and corresponds to the ActivityRecord of Ams. Another article (see Note 6) mentioned that it plays an important role
    activity.attach(appContext, this, getInstrumentation(),r.token,...,app,...,window,...);
    ...
    //Callback onCreate
    mInstrumentation.callActivityOnCreate(...);
}

So far, the framework of startActivity has been described.

Drawing of View

This part is not about custom views, Instead, it creates windows (including adding and drawing). From the perspective of WmS, a Window is not a Window class, but a view class. When WmS receives a user's message, it needs to send the message to the Window. The view class itself cannot directly receive the message delivered by WmS. The IWindow class must really receive the user's message (binder class), and the IWindow class is implemented by the ViewRoot.W class. Each w contains a view variable. These two sentences are quoted from Android kernel analysis. From the later explanation, we can see that the Window class is more an abstraction of the Window, and the view is the content of the Window.

Three windows are defined in the Framework: application window, sub window and system window. The application window corresponds to an activity, The next step is to explain the application window The creation of the window (hereinafter referred to as window). Since the window corresponds to a Activity, the window is created in the process of startActivity. It is mentioned that the creation of Activity will callback onCreate, and we will call setContentView method when we develop it. SetContentView will call setContentView method of Window class, if you go to see attach of Activity. Method, you will find that the window class is actually a PhoneWindow class.

PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
    ...
    installDecor();
    ...
    //Load the customized layout of the app. It can be seen from the following that our layout is wrapped with at least two view s,
    //First by mContentParent and then by mDecor
    mLayoutInflater.inflate(layoutResID, mContentParent);
    ...
}

private void installDecor() {
    ...
    //Create a decorview (inherited from FrameLayout). As the name suggests, all the contents of the window are displayed in this view,
    //This View is the root of all views of the Activity.
    //This is why we view the Activity view hierarchy, and the outermost layer is FrameLayout
    mDecor = generateDecor(-1);
    ...
    //Configure different xml layouts according to different style s,
    //These xml have a common feature: they all have an id of id_ ANDROID_ Content view
    //Therefore, ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT) can be used
    //Assign value to contentParent
    mContentParent = generateLayout(mDecor);
    ...
}  

This step only creates the View, and the next two steps will be involved: 1. Add the window to Wms, and Wms will manage the window in a certain form like Ams. 2. Convert the content to be displayed by the View into data, save it in the screen buffer memory, and hand it over to the system for drawing.

In the process of startActivity, handleResumeActivity will be called after the Activity is created.

ActivityThread.java
private void handleLaunchActivity(...){
    ...
    Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        ...
        //See below
        handleResumeActivity(...);
        ...
    }
}

final void handleResumeActivity(...){
    ...
    //Callback onResume
    r = performResumeActivity(token, clearHide, reason);
    ...
    //Make decor invisible
    View decor = r.window.getDecorView();
    decor.setVisibility(View.INVISIBLE);
    ...
    //Call makeVisible of Activity
    r.activity.makeVisible();
    ...
}

Activity.java
void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        //Finally, addView of WindowManagerGlobal will be called
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
    }

WindowManagerGlobal.java
public void addView(...) {
    ...
    //Create viewrootimpl (abstract of view root)
    root = new ViewRootImpl(view.getContext(), display);
    view.setLayoutParams(wparams);
    //Cache the view (mdecor in this case), root (an abstraction of view) and layoutParam
    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    ...
    //See below
    root.setView(view, wparams, panelParentView);
    ...
}

ViewRootImpl.java
public void setView(...) {
    ...
    //The following section continues
    requestLayout();
    ...
    //ipc to Wms, and the Session class is the server
    //mInputChannel is an InputChannel class, which is the implementation of the pipeline in the java layer. It will be described in detail later when we talk about Android events
    res = mWindowSession.addToDisplay(...,mInputChannel);
    ...
}

Session.java
@Override
public int addToDisplay(...) {
    //See below
    return mService.addWindow(...);
}

WindowManagerService.java
public int addWindow(...) {
    //Create WindowState
    //Session is the Binder server of the session
    //Client is the Binder client of IWindow
    final WindowState win = new WindowState(..., session, client,...);
    ...
    //The windowsaddedlocked method of the Session will be called, as shown below
    win.attach();
    //Cache win
    mWindowMap.put(client.asBinder(), win);
    ...
    if (win.canReceiveKeys()) {
        //If the window is interactive (for example, Toast is non interactive), update the focus window
        focusChanged = updateFocusedWindowLocked(...);
        ...
    }
}

Session.java
void windowAddedLocked(String packageName) {
    ...
    if (mSurfaceSession == null) {
        //Create SurfaceSession, which is a class that directly interacts with surfacelinker,
        //Used to add, delete, transform windows to SurfaceFlinger. Each application corresponds to only one Session object,
        //Therefore, mSurfaceSession will actually be created only once,
        //That is, a SurfaceSession object will be constructed when the first window in the application is created,
        //Subsequent window additions in the same application will no longer construct the SurfaceSession object
        mSurfaceSession = new SurfaceSession();
        ...
        //Save Session
        mService.mSessions.add(this);
        ...
    }
    ...
}

Here you can see that Wms saves the information of the window, that is, manages it, which is the basis of event distribution.

Returning to the requestLayout method above, requestLayout calls the scheduleTraversals method, which initiates a View tree traversal message. The message is processed asynchronously, and the corresponding processing function is the performttraversals method.

ViewRootImpl.java
private void performTraversals() {
    final View host = mView;
    if (mFirst) {
        ...
        //If the window is displayed for the first time, initialize attachinfo and assign it to mView,
        //Call the dispatch method of the ViewGroup to recursively pass the attachinfo to the child View
        host.dispatchAttachedToWindow(mAttachInfo, 0);
    }
    ...
    //If the window size changes, call host Measure() recalculates the size of the view in the window
    //Android's View and ViewGroup are classic combination modes
    //The measure process will traverse the entire View tree and call the measure of each View and the callback onMeasure
    //The process of layout and draw is similar
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
    //According to the execution results of the seven steps, judge whether it is necessary to rearrange. For example, when the size of any view changes,
    //It affects the layout of other views
    performLayout(lp, mWidth, mHeight);
    ...
    //If it is judged that the painting is not cancelled and it is not a new surface, start painting
    //The meaning of newSurface variable pull means that a Surface object has been created in ViewRoot, but the object has not been allocated real video memory by WmS,
    //In ViewRoot, it is called windowsession Relayoutwindow() allocates real video memory in the Surface object,
    //In general, the newSurface here is false.
    performDraw();
    ...
}

For the first time, since the window setting is invisible, the previous code can see that the makeVisible method of Activity will call mdecor SetVisibility (View.VISIBLE), after a series of calls, will be called to the performTraversals method of ViewRootImpl again, then the performDraw method will be called.

Before we talk about drawing, we should first clarify several concepts.

  1. Surface:

Surface is a handle to the raw image buffer. That is to say, surface corresponds to a section of memory whose data is the content to be drawn. The surface class essentially represents a plane

  1. Canvas:

We know that drawing different patterns is obviously a work, not a piece of data. Android uses Canvas class to represent these operations, that is, Canvas is the function class drawn. Look at the description of Canvas: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap) - the function of Canvas is to respond to the call of draw and write it into the bitmap, which is used to save all pixels, so you will know the concept of Canvas better.

ViewRootImpl.java
private void performDraw() {
    ...
    draw(fullRedrawNeeded);
    ...
}

private void draw(boolean fullRedrawNeeded) {
    //Previously applied to Wms
    Surface surface = mSurface;
    ...
    if (!drawSoftware(...)) {
        return;
    }
    ...
}

private boolean drawSoftware(...) {
    ...
    //Get canvas and lock
    canvas = mSurface.lockCanvas(dirty);
    ...
    //Traverse the draw of subclasses. Obviously, this process is to convert the contents of all subclasses into pixels and write them into the bitmap of Canvas
    mView.draw(canvas);
    ...
    //Submit the Canvas that saves all view contents to the surface and submit it to the bottom layer of the system for drawing.
    surface.unlockCanvasAndPost(canvas);
    ...
}

So far, the framework drawn by view has been described.

2. One touch, what did Android do

This title comes from One touch, what did Android do So much of the following will be quoted from this article.

What does a touch mean? In the process of using Android devices, clicking, long pressing, sliding (TouchEvent) and pressing the entity key (KeyEvent) can become "one touch". Therefore, one touch can represent all the operations we use.

Of course, our touch starts with the hardware, which will pass events to the kernel and the kernel to the Framework. As mentioned earlier, various services will be started when the system server is started:

SystemServer.java
public static void main(String[] args) {
    new SystemServer().run();
}
private void run() {
    ...
    startOtherServices();
    ...
}
private void startOtherServices() {
    ...
    //Create InputManagerService
    inputManager = new InputManagerService(context);
    ...
    //Create WindowManagerService and hold InputManagerService
    wm = WindowManagerService.main(..., inputManager,...);
    ...
    //Set InputMonitor as callback and start InputManagerService
    inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
    inputManager.start();
}

InputManagerService.java
public InputManagerService(Context context) {
    ...
    //Initialize the native object (mPtr is long and saves the pointer of the native object, which is a common method used by jni to keep the native object)
    //InputManagerService corresponds to the native inputmanager cpp
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    ...
}
public void start() {
    ...
    //Used to start the two threads mentioned below
    nativeStart(mPtr);
    ...
}

InputManager.cpp
InputManager::InputManager(...) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
/**
From the above code, we can see that InputDispatcher and InputReader are created during InputManager initialization
 And InputReaderThread and inputdispatcher thread. InputReader is used to read input information (i.e. various events),
InputDispatcher Used to distribute events. Two threads are obviously used to run these two functions.
*/

The InputReader thread will continuously call the driver of the input device to read all messages input by the user. Both the thread and the InputDispatcher thread run in the system \ _processspace. The InputDispatcher thread takes out the original message from its message queue. The extracted message may be distributed in two ways. The first method is to send it directly to the customer window through the pipe. The other method is to send it to the WmS first, and the WmS will process it. If the WmS does not process the message, it will be sent to the customer window. Otherwise, it will not be sent to the customer window (quoted from Android kernel analysis). As shown in the figure (the picture is also from Android kernel analysis):

If it is a key message, InputDispatcher will first call back the callback function defined in InputManager, which will not only call back the callback function in InputMonitor, but also call the related functions defined in WmS. For system key messages, such as "Home" key and telephone key, WmS will process them by default. If events are consumed, InputDispatcher will not continue to pass these key messages to the customer window. For touch screen messages, InputDispatcher will directly pass them to the customer window.

InputManagerService.java
//Callback of key events. We mentioned earlier that the callback object is InputMonitor
// Native callback.
private long interceptKeyBeforeDispatching(InputWindowHandle focus,
           KeyEvent event, int policyFlags) {
   return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}

InputMonitor.java
@Override
public long interceptKeyBeforeDispatching(
           InputWindowHandle focus, KeyEvent event, int policyFlags) {
   WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
   //Callback Wms. mPolicy is created during SystemServer initialization. It is a PhoneWindowManager class. You can see the processing of various keys
   return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
}

When InputDispatcher is passed directly to the window, find the window that currently gets the focus through findtouchedwindow atlocked method, and then conduct ipc through Pipe. That is to say, a set of window related information will be maintained in the native layer. Let's go back to the process of adding windows in Wms:

WindowManagerService.java
public int addWindow(...) {
   //Create a WindowState, and the InputWindowHandle will be created in the construction method
   final WindowState win = new WindowState(...);
   ...
   //Establish pipeline communication, where outInputChannel is passed from app process
   win.openInputChannel(outInputChannel Is from app process);
   ...
   //Update the information of window focus change to the native layer. Similarly, it will be updated when the window is switched or destroyed
   mInputMonitor.updateInputWindowsLw(false /*force*/);
   ...
}

WindowState.java
void openInputChannel(InputChannel outInputChannel) {
   ...
   InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
   mInputChannel = inputChannels[0];
   mClientChannel = inputChannels[1];
   /*Channel[0]Save on the server side*/
   mInputWindowHandle.inputChannel = inputChannels[0];
   ...
   /* Channel[1]Return to ViewRootImpl end of app*/
   mClientChannel.transferTo(outInputChannel);
   ...
   /*Register to the InputManagerService native layer*/
   mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}

It can be seen that a group of windowstates are maintained in Wms for window creation, destruction and switching, while a group of inputwindowhandles are maintained in InputManagerService for event distribution.

Let's go back to ViewRootImpl. When adding a window, we called the setView method.

ViewRootImpl.java
public void setView(...) {
    ...
    //InputChannel class is the implementation of pipeline in java layer
    mInputChannel = new InputChannel();
    ...
    //View iwindowsession Aidl will find that the mInputChannel here is marked out,
    //That is, changes in another process will be synchronized to this process
    res = mWindowSession.addToDisplay(...,mInputChannel);
    ...
    if (mInputChannel != null) {
        if (mInputQueueCallback != null) {
            mInputQueue = new InputQueue();
            mInputQueueCallback.onInputQueueCreated(mInputQueue);
        }
        //Create a callback for the pipeline, see below
        mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
    }
}

ViewRootImpl$WindowInputEventReceiver.java
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
    //See below
    super(inputChannel, looper);
}

InputEventReceiver.java
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
    ...
    //Establish a callback for the pipeline, and the native layer will call back to the InputEventReceiver when it receives the event
    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                    inputChannel, mMessageQueue);
    ...
}

When the InputDispatcher of InputManagerService passes messages to the app process through the pipeline, the pipeline of the app process will call back InputEventReceiver (i.e. WindowInputEventReceiver) for event distribution. The following code can be said to be the standard answer to the responsibility chain mode, which is very exciting. Readers can learn by themselves.

summary

Android Framework can be said to be a huge project. If we fall into details at the beginning, we can't go a way. Gentlemen are good at fake things. With the help of the research and learning results of big men, we can learn the overall framework first and break it individually when necessary. Thank you very much. The following references may be omitted. I apologize here! I hope this article is helpful to readers.

This article is transferred from https://juejin.cn/post/6844903717301387272 , in case of infringement, please contact to delete.

Keywords: Android Programmer Framework

Added by bashaash on Wed, 22 Dec 2021 13:36:46 +0200