Negative screen, that is, right swipe on the desktop to enter the page, as shown in the GIF below, Google Desktop right swipe to enter the Google Feed page. This page is a separate application, and this article mainly explains the implementation principle of sliding in between the two applications.
1. Introduction to cross-process communication
The interaction between two applications is bound to involve cross-process communication. Before explaining the interaction principle between negative screen and desktop, it is necessary to introduce the principle of cross-process communication, namely the communication process of Binder. As this is not the focus of this time, it is only briefly explained here.
As shown in the figure above, Binder communication is a typical CS model with three main roles: Client process, Server process and Binder object for interprocess communication;
- The Server process provides external services Service.
- When the Client process starts the Service with bindService(), the Service returns a Binder object to the Client process.
- With this Binder object, the Client can actively communicate with the Server process.
- The Client process can register a Binder object with the Server process to communicate with the Client process.
- Finally, the Client and Server hold each other’s Binder objects, thus achieving two-way communication.
2. Google Feed screen solution
2.1 Interface Definition
With Binder’s cross-process communication, manufacturers can define different interfaces. The implementation details vary greatly. Here is a sample of Google’s native Feed screen solution.
With Binder, Google provides two AIDL interfaces:
// com.google.android.libraries.launcherclient
interface ILauncherOverlay {
oneway void startScroll(a); // Use this method to notify Overlay
oneway void onScroll(in float progress); // Progress of the slide Launcher
oneway void endScroll(a); // Launcher stops the slide and calls this method to notify the Overlay
oneway void windowAttached(in LayoutParams lp, in ILauncherOverlayCallback cb, in int flags);
oneway void windowDetached(in boolean isChangingConfigurations);
oneway void closeOverlay(in int flags);
oneway void onPause(a);
oneway void onResume(a);
oneway void openOverlay(in int flags);
oneway void requestVoiceDetection(in boolean start);
String getVoiceSearchLanguage(a);
boolean isVoiceDetectionRunning(a);
boolean hasOverlayContent(a);
oneway void windowAttached2(in Bundle bundle, in ILauncherOverlayCallback cb);
oneway void unusedMethod(a);
oneway void setActivityState(in int flags);
boolean startSearch(in byte[] data, in Bundle bundle);
}
interface ILauncherOverlayCallback {
oneway void overlayScrollChanged(float progress); // Overlay Active slide progress, Overlay calls back to the Launcher through this interface
oneway void overlayStatusChanged(int status); // Overlay slide state, such as the start slide, the Overlay calls back to the Launcher through this interface
}
Copy the code
An ILauncherOverlay is a layer on the desktop that represents a negative screen, and an ILauncherOverlayCallback is a callback associated with a negative screen.
From the perspective of Launcher, this can be regarded as a CS mode, with the Launcher as the Client and the negative screen as the Server. The IlauncherOverlay object will be obtained when the desktop overlay goes to a negative screen bindService, and the related interfaces of this object will be startScoll(), onScroll(), and endScroll() to notify the negative screen slide. The parameter in the onScroll(float Progress) method can inform the percentage of the negative screen to be displayed, which can be used with the desktop display to achieve the effect of sliding out of the negative screen.
2.2 Principle of negative one screen display on desktop
After all, negative screen and desktop are in two processes, how does negative screen display the page in the desktop? This is Window related knowledge.
Here cites a picture on the Internet to simply illustrate: the daily development of an Activity page, are through all kinds of View to achieve, and these V IEW are in the application process, corresponding to the system process (WMS), just in a Window. Windows are layered. There is a z-axis order between multiple Windows. WindowManagerSeervice manages these Windows. SurfaceFlinger was then responsible for drawing.
There are three types of Windows: application Window, child Window, and system Window. Each type has a different level. The higher the level, the higher the position.
Window | The hierarchy |
---|---|
The application Window | 1~99 |
The child Window | 1000 ~ 1999 |
The Window system | 2000 ~ 2999 |
The Window hierarchy can be set with the WindowManager$LayoutParams#type parameter. The negative screen is implemented by creating a higher level Window on top of the desktop Window to display the contents of the negative screen.
2.3 Code implementation fragment
Desktop by calling ILauncherOverlay# windowAttached (), will deliver a WindowManager. LayoutParams object, The Launcher sends its Windowed LayoutParams to the negative screen. The logic is as follows:
// Launcher.java
public void onAttachedToWindow(a) {
mOverlay.windowAttached(
mActivity.getWindow().getAttributes(),
mOverlayCallback,
mFlags);
}
Copy the code
/ / negative one screen at a time
WindowManager.LayoutParams mLayoutParams;
public void windowAttached(WindowManager.LayoutParams lp, ILauncherOverlayCallback cb, int flags) {
mLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
mLayoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
mLayoutParams.gravity = Gravity.START;
// The Window level of the screen is larger than that of the Launcher
mLayoutParams.type = lp.type + 1;
mLayoutParams.token = lp.token;
mLayoutParams.flags = WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS |
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
mLayoutParams.x = -screenWidth;
mLayoutParams.format = PixelFormat.TRANSLUCENT;
mWindowManager.addView(mOverlayDecorView, mLayoutParams);
if(cb ! =null) { cb.overlayStatusChanged(FLAG_SUCCESS); }}Copy the code
2.4 Window Result Verification
As shown above, we print the Window hierarchy of this desktop as follows:
$ adb shell dumpsys window w | grep -e "Window #" -e "mOwnerUid"
Window #0 Window{2fe89fd u0 NavigationBar}:
mOwnerUid=10028 mShowToOwnerOnly=false package=com.android.systemui appop=NONE
Window #1 Window{ee37895 u0 StatusBar}:
mOwnerUid=10028 mShowToOwnerOnly=false package=com.android.systemui appop=NONE
Window #2 Window{bc26972 u0 AssistPreviewPanel}:
mOwnerUid=10028 mShowToOwnerOnly=true package=com.android.systemui appop=NONE
Window #3 Window{484bff7 u0 DockedStackDivider}:
mOwnerUid=10028 mShowToOwnerOnly=false package=com.android.systemui appop=NONE
Window #4 Window{a6fb5f6 u0 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity}:
mOwnerUid=10038 mShowToOwnerOnly=true package=com.google.android.apps.nexuslauncher appop=NONE
Window #5 Window{11fd688 u0 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity}:
mOwnerUid=10040 mShowToOwnerOnly=true package=com.google.android.apps.nexuslauncher appop=NONE
Window #6 Window{21768cd u0 com.android.systemui.ImageWallpaper}:
mOwnerUid=10028 mShowToOwnerOnly=true package=com.android.systemui appop=NON
Copy the code
The window hierarchy is shown from top to bottom: the #0 navigation bar; #1 Status bar #2 Don’t know what it is. #3 appears to be split-screen dependent; #4 The window where the negative screen is; # 5 the launcher window; #6 The wallpaper is in window.
#4 and #5 are shown as NexusLauncherActivity. Uid can be used to distinguish a negative screen from a Luancher Window:
$ adb shell dumpsys package com.google.android.googlequicksearchbox | grep userId=
userId=10038
$ adb shell dumpsys package com.google.android.apps.nexuslauncher | grep userId=
userId=10040
Copy the code
2.5 Negative one-screen slide display process
Since the negative screen Window is at the top of the Launcher, to avoid blocking the desktop under normal desktop display, layoutMs. X of the negative screen Window needs to be set to -screenWidth. In other words, the negative screen Window is in the upper left of the desktop Window, as shown below:
At the beginning of the slide display, the layoutParams. x of the Window is set to 0, which directly overlays the desktop Window. However, the scrollX of the View in the Window should be set to screenWidth at the beginning, that is, the content is not visible. As the desktop slides, change the scrollX parameter of this View to gradually display the contents of the negative screen, as shown below:
3. Another option — reflection
3.1 Reflection Scheme Description
Reflection scheme, which refers to the negative screen provides a fixed class, fixed interface to the Launcher, as shown in the following code:
public class AssistantCtrl {
public View createView(a) {
/ /... .}}Copy the code
The Launcher interface returns the AssistantCtrl object, and then calls createView() to add the View to the AssistantCtrl class.
Context foreignContext = context.createPackageContext("Negative one screen Application package name", flag);
Class cls = foreignContext.getClassLoader().loadClass("AssistantCtrl class full pathname");
/ /... Reflection implementation, slightly...
Copy the code
The interaction between the two is shown as follows:
The Launcher obtains the instance of a negative View through reflection, so the negative View page is run in the Launcher process, and the data in the View needs to be obtained from the negative screen. Therefore, a layer of cross-process communication is also required here, which is also implemented with Binder.
3.2 Introduction to the principle of one Application reflecting another Application
There is a class loading mechanism involved, and here is a brief introduction.
Class loading is achieved by ClassLoader, Android is used basedexClassLoader.java
// BaseDexClassLoader.java
private final DexPathList pathList;
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
String librarySearchPath, ClassLoader parent, boolean isTrusted) {
// dexPath is the path where the dex file is located
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
}
@Override
protectedClass<? > findClass(String name)throws ClassNotFoundException {
Class c = pathList.findClass(name, suppressedExceptions);
}
Copy the code
// DexPathList private Element[] dexElements; DexPathList(ClassLoader definingContext, String dexPath, String librarySearchPath, File optimizedDirectory, boolean isTrusted) { // save dexPath for BaseDexClassLoader this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions, definingContext, isTrusted); } public Class<? > findClass(String name, List<Throwable> suppressed) { for (Element element : dexElements) { Class<? > clazz = element.findClass(name, definingContext, suppressed); if (clazz ! = null) { return clazz; } } return null; }Copy the code
In general, the ClassLoader loads the Class object of the object according to the file path of the bytecode file dex. That is to say, as long as the path of the dex file corresponding to the Class is known, the ClassLoader can load its Class object.
You can customize the ClassLoader to verify this, or you can check the source code of the Launcher reflection to get negative screen objects mentioned earlier. I won’t go into details here.
4. Compare the advantages and disadvantages of the two schemes
Advantages of Google Feed screen solution: decoupling of negative screen and Luancher; Negative screen pages are loaded in a negative screen process. Disadvantages: The slide switch between Launcher and negative screen is hard to handle, and the slide synchronization between the two is constantly achieved through cross-process communication during the slide, which is not as smooth as reflection.
Reflection scheme advantages: The View with a negative screen is directly loaded in the Launcher process, so the switch between the Launcher and the negative screen is smooth and easy to achieve. Disadvantages: Single-screen View is loaded directly into the Launcher, occupying the memory of the Launcher; The coupling degree of negative screen and Launcher is high, which is not easy to expand; We need the Launcher to provide other capabilities, such as permissions. For example, the images of the negative screen come from the Internet, which is the ability of the desktop to request the Internet.