-
Settled! Tsinghua genius Wang Yin joins Huawei at level 22, former Alibaba P10 Zhao Haiping joins ByteDance, possibly at level 4+ -
Baidu Netdisk “Cracked Version”, Pandownload developer arrested
Author: Focusing
1. How to conduct unit testing and ensure app stability?
Reference Answer: To test Android applications, the following types of automated unit tests are typically created:
-
Local Testing: Runs only on the local machine’s JVM to minimize execution time. This type of unit test does not depend on the Android framework, or if it does, it can easily use a mock framework to simulate dependencies to isolate Android dependencies. Mock frameworks like Google’s recommended Mockito;
-
Android Official – Setting Up Local Unit Tests (https://developer.android.com/training/testing/unit-testing/local-unit-tests.html)
-
Instrumented Testing: Unit tests that run on a real device or emulator. Since they need to run on the device, they are slower. These tests can access instrument (Android system) information, such as the context of the application being tested. Generally, this approach is used when dependencies cannot be easily mocked;
-
Android Official – Setting Up Instrumented Unit Tests (https://developer.android.com/training/testing/unit-testing/instrumented-unit-tests.html)
Note: Unit tests are not suitable for testing complex UI interaction events.
Recommended Article: Android Unit Testing – Just Read This One (https://juejin.im/post/5b57e3fbf265da0f47352618)
The stability of the app is mainly determined by the overall system architecture design, and the details of code programming should not be overlooked. As the saying goes, “A thousand-mile dyke collapses due to an ant hole.” If not considered carefully, seemingly trivial code snippets may lead to the collapse of the entire software system. Therefore, in addition to local testing, Monkey stress testing is also needed before going live.
2. How to check an object’s garbage collection status in Android?
Reference Answer: First, understand the scenarios and usage of the four types of Java references (strong reference, soft reference, weak reference, phantom reference).
For example, a SoftReference object is used to hold a soft reference, but it is also a Java object. So when the soft reference object is garbage collected, although the get method of this SoftReference object returns null, the SoftReference object itself is not null. At this point, the SoftReference object no longer has any value and requires an appropriate cleanup mechanism to avoid memory leaks caused by a large number of SoftReference objects.
3. How to compress the size of an APK?
Reference Answer: A complete APK contains the following directories (drag the APK file into Android Studio):
-
META-INF/: Contains CERT.SF and CERT.RSA signature files, as well as the MANIFEST.MF manifest file.
-
assets/: Contains application resources that can be retrieved by the application using the AssetManager object.
-
res/: Contains uncompiled resources resources.arsc.
-
lib/: Contains compiled code specific to processor software layers. This directory contains subdirectories for each platform, such as armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, and mips.
-
resources.arsc: Contains compiled resources. This file contains the XML content of all configurations in the res/values/ folder. The packaging tool extracts this XML content, compiles it into binary format, and archives the content. This includes language strings and styles, as well as content paths directly included in the resources.arsc file, such as layout files and images.
-
classes.dex: Contains classes compiled in a format understandable by the Dalvik / ART virtual machine.
-
AndroidManifest.xml: Contains the core Android manifest file. This file lists the application’s name, version, permissions, and referenced library files. This file uses Android’s binary XML format.
-
lib, class.dex, and res occupy more than 90% of the space, so these three areas are the key to optimizing APK size (actual situation may vary).
Reduce res, compress image and text files: Image file compression is aimed at jpg and png formats. We usually place multiple sets of images with different resolutions to adapt to different screens, and here we can appropriately reduce the number of images. In practical use, keeping one or two sets is enough (if keeping one set, it’s recommended to keep xxhdpi, if keeping two sets, then add hdpi), and then compress the remaining images (jpg uses Yitu compression, png tries to use pngquant compression).
Reduce dex file size:
-
Add resource obfuscation.
-
shrinkResources set to true indicates removing unused resources, working in conjunction with code compression.
-
minifyEnabled set to true indicates enabling code compression via ProGuard, working with proguardFiles configuration to obfuscate code and remove unused code.
-
Code obfuscation not only compresses the APK but also enhances security.
Recommended Article: Best Practices for Android Obfuscation (https://www.jianshu.com/p/cba8ca7fc36d)
Reduce lib file size: Due to the inclusion of many third-party libraries, the lib folder usually occupies a lot of space, especially when there are so libraries. Many so libraries introduce armeabi, armeabi-v7a, and x86 types; here you can keep only one of armeabi or armeabi-v7a. In fact, mainstream apps like WeChat do this.
Simply configure it directly in build.gradle, the same applies to NDK configuration.
Recommended Article: APK Slimming (https://www.jianshu.com/p/5921e9561f5f)
Reference Answer: First, understand the reasons for setting up multiple channels. Adding different identifiers in the installation package, combined with automated buried points, allows the application to carry channel information when requesting the network, facilitating backend operational statistics, such as counting the download volume of our application in different application markets.
Here is an example using Umeng statistics:
-
First, set the dynamic channel variable in the manifest.xml file:
-
Next, configure productFlavors in the build.gradle file under the app directory, which is to configure the packaging channels:
Finally, in the terminal output command line at the bottom of the editor:
-
Execute ./gradlew assembleRelease, which will generate the release package for all channels;
-
Execute ./gradlew assembleVIVO, which will generate both the release and debug versions of the VIVO channel;
-
Execute ./gradlew assembleVIVORelease to generate the release package for VIVO.
Recommended Article: Meituan Android Automation Journey – Walle Generating Channel Package (https://github.com/Meituan-Dianping/walle)
Reference Answer: Pluginization refers to dividing the APK into host and plugin parts. The modules or functions that need to be implemented are extracted as independent units that can be dynamically loaded or replaced during the app’s runtime, reducing the scale of the host.
-
Host: This is the currently running app.
-
Plugin: Relative to the pluginization technology, this refers to the APK class files that need to be loaded and executed.
Hotfix emphasizes fixing known bugs without requiring a second installation of the application.
Class Loading Mechanism: Two commonly used class loaders in Android are DexClassLoader and PathClassLoader, both of which inherit from BaseDexClassLoader. The difference is that PathClassLoader can only load dex/jar/apk files from internal storage directories. DexClassLoader supports loading dex/jar/apk files from specified directories (not limited to internal).
Plugin Communication: By generating a corresponding DexClassLoader for the plugin APK, you can access the classes within it, which can be divided into single DexClassLoader and multiple DexClassLoader structures.
-
If using a multiple ClassLoader mechanism, the main project needs to load the class from the plugin’s ClassLoader before calling its methods via reflection. The plugin framework generally manages access to classes in various plugins through a unified entry point and imposes certain restrictions.
-
If using a single ClassLoader mechanism, the main project can directly access classes in the plugin by class name. This method has a drawback: if two different plugin projects reference different versions of a library, the program may fail.
Resource Loading: The principle is to add the paths of the plugin APKs to the AssetManager through reflection and create Resource objects to load resources. There are two processing methods:
-
Merge Style: When adding paths for all plugins and the main project during addAssetPath; since all paths are included in AssetManager, the generated Resource can access resources from both the plugin and the main project. However, since the main project and each plugin are independently compiled, there may be resource ID conflicts when accessing resources.
-
Independent Style: Each plugin only adds its APK path, and the resources of each plugin are isolated. However, if resource sharing is desired, the corresponding Resource object must be obtained.
Recommended Articles:
Android Dynamic Loading Technology – A Simple and Understandable Introduction (https://segmentfault.com/a/1190000004062866)
Deep Understanding of Android Pluginization Technology (https://yq.aliyun.com/articles/361233)
Why Hot Update is Necessary (https://www.cnblogs.com/baiqiantao/p/9160806.html)
Reference Answer: The reason for introducing componentization is that as projects scale with increasing demands, the complexity of intertwined business modules increases, leading to a lack of constraints in code between each business module, resulting in blurred code boundaries, frequent code conflicts, and changes to a small issue potentially causing new problems. Adding a new requirement requires familiarity with the relevant code logic, increasing development time.
-
Avoid reinventing the wheel, saving development and maintenance costs.
-
Reasonably arrange manpower through components and modules for business benchmarks to improve development efficiency.
-
Different projects can share a component or module, ensuring the uniformity of the overall technical solution.
-
Prepare for future pluginization to share the same underlying model.
The componentization development process involves breaking down a fully functional app or module into multiple sub-modules (Modules), each of which can be independently compiled and run, and can be combined into another new app or module. Each module does not depend on each other but can interact with each other. However, when released, these components are unified into a single APK, and in some special cases, they can even be upgraded or downgraded.
For example:
The App is the main application, ModuleA and ModuleB are two business modules (relatively independent and not affecting each other), and Library is the basic module that contains all the dependencies needed by all modules and some utility classes, such as network access, time utilities, etc.
Note: The basic components provided to each business module need to be split into aar or library based on specific circumstances. Stable components like login and basic network layers are generally packaged into aar to reduce compilation time, while custom View components that may change frequently with version iterations are extracted as Library in source code form.
Recommended Article: Dry Goods | Looking at Componentization Architecture Practice from Zhixing Android Project (https://mp.weixin.qq.com/s?__biz=MjM5MDI3MjA5MQ==&mid=2697268363&idx=1&sn=3db2dce36a912936961c671dd1f71c78)
Cross-component communication scenarios:
-
The first type is page jumps between components (Activity to Activity, Fragment to Fragment, Activity to Fragment, Fragment to Activity) and data transmission during jumps (basic data types and serializable custom class types).
-
The second type is calling custom classes and custom methods between components (components providing services externally).
Analysis of Cross-Component Communication Solutions: The first type of page jumps between components is simple to implement; providing the necessary API to pass different types of data during jumps is sufficient.
The second type of calling custom classes and methods between components is slightly more complex and requires ARouter in conjunction with a public service (CommonService) in the architecture:
-
Providing service business modules:
-
Declare the Service interface (containing the custom methods to be called) in the CommonService, implement this Service interface in your module, and expose the implementation class through the ARouter API.
-
Using service business modules: Obtain this Service interface (polymorphically holding the actual implementation class) through the ARouter API to call the custom methods declared in the Service interface, thus achieving interaction between modules.
-
In addition, you can use AndroidEventBus’s unique Tag, which can make it easier to locate the code that sends and receives events during development. If you group by using the component name as the prefix for the Tag, it can also better manage and view the events of each component. However, it is also advised not to overuse EventBus.
How to manage too many routing tables?
-
RouterHub exists in the basic library and can be seen as a communication protocol that all components need to adhere to. It can contain not only routing address constants but also various key values named when passing data across components, along with appropriate comments. Any component developer does not need to communicate in advance; as long as they depend on this protocol, they will know how to collaborate, improving efficiency and reducing the risk of errors. What is agreed upon is naturally stronger than what is said verbally.
-
Tips: If you find it troublesome to write every routing address in the basic library’s RouterHub, you can also create a private RouterHub within each component to manage the routing addresses that do not need to be cross-component, only putting the routing addresses that need to be cross-component into the public RouterHub of the basic library. If you do not need to centrally manage all routing addresses, this is also a recommended method.
ARouter Routing Principles: ARouter maintains a routing table Warehouse, which stores all module jump relationships. ARouter routing jumps actually still call startActivity for the jump, using the native Framework mechanism, just creating jump rules through apt annotation and artificially intercepting jumps and setting jump conditions.
Reference Answer: In componentization, each business module is independent and does not have any interdependencies, so a business module cannot access the code of another business module. If you want to jump from page A of business module A to page B of business module B, this cannot be achieved solely by the module itself, which requires a cross-component communication solution – routing (Router).
Routing mainly has the following two scenarios:
-
The first type is page jumps between components (Activity to Activity, Fragment to Fragment, Activity to Fragment, Fragment to Activity) and data transmission during jumps (basic data types and serializable custom class types).
-
The second type is calling custom classes and custom methods between components (components providing services externally).
Buried points are collecting information on specific processes in the application to track the usage status of the application:
-
Code Buried Point: Call the corresponding interface in the SDK to send buried point data when a certain event occurs. Most third-party data statistical service providers such as Baidu Statistics, Umeng, TalkingData, and Sensors Analytics adopt this solution.
-
Full Buried Point: Full buried points refer to reporting all behaviors that meet certain conditions generated within the web page/app to the backend server.
-
Visual Buried Point: Configure collection nodes through visual tools (e.g., Mixpanel), automatically parse the configuration on the Android side, and report buried point data, thus achieving the so-called automatic buried points.
-
No Buried Point: This does not mean that no buried points are needed, but rather that the Android side automatically collects all events and reports buried point data, filtering out useful data during backend data calculations.
9. Hook and Instrumentation Technology
Reference Answer: Hook is a technology used to change the execution results of APIs, allowing the redirection of the execution of system API functions (the application’s triggering events and backend logic processing are executed step by step according to the event flow. Hook means intercepting and monitoring the transmission of events before they reach their destination, like a hook catching events, and being able to handle some specific events while hooking, such as reverse cracking apps).
In Android, there are generally two ways to implement Hook:
-
Requires root permission to directly Hook the system, which can affect all apps.
-
No root permission, but can only Hook its own app, unable to affect other system apps.
Instrumentation is a static way of modifying third-party code, meaning modifying the source code (intermediate code) during the compilation phase and repackaging it, which is static tampering; while Hook does not require modifying the source code or intermediate code during the compilation phase, it modifies calls at runtime through reflection, which is dynamic tampering.
Recommended Articles:
Android Pluginization Principle Analysis – Hook Mechanism of Dynamic Proxy (http://weishu.me/2016/01/28/understand-plugin-framework-proxy-hook/)
Basic Concept of Android Instrumentation (https://blog.csdn.net/fei20121106/article/details/51879047)
Android Reverse Journey (http://www.520monkey.com/)
Reference Answer: Android’s signature mechanism includes message digest, digital signature, and digital certificate.
-
Message Digest: Execute a one-way Hash function on the message data to generate a fixed-length hash value.
-
Digital Signature: A method of electronically storing message signatures. A complete digital signature scheme should consist of two parts: signature algorithm and verification algorithm.
-
Digital Certificate: A file digitally signed by a certificate authority (Certificate Authentication) that contains information about the public key owner and the public key.
Recommended Article: Understand Android v1 & v2 Signature Mechanism in One Article (https://blog.csdn.net/freekiteyu/article/details/84849651)
Reference Answer: In v1 version signatures, the signature exists in the APK package as a file. This version of the APK package is a standard zip package. The difference between V2 and V1 is that V2 signs the entire zip package and adds an APK signature block to the zip package, which contains signature information.
V2 version signature block (APK Signing Block) is mainly divided into three parts:
-
SignerData (Signer Data): Mainly includes the signer’s certificate, the integrity check hash of the entire APK, and some necessary information.
-
Signature (Signature): The signature data of the developer for the SignerData part.
-
PublicKey (Public Key): The public key data used for signature verification.
V3 version signature block is also divided into the same three parts. The difference from V2 is that in the SignerData part, V3 adds an attr block, which consists of smaller level blocks. Each level block can store a certificate information. The certificate of the previous level block verifies the certificate of the next level block. The certificate of the last level block must comply with the certificate of the public key used to sign the entire APK in SignerData.
Recommended Articles:
APK Signature Scheme v3 (https://source.android.google.cn/security/apksigning/v3)
New Features of Android P v3 Signature (https://xuanxuanblingbling.github.io/ctf/android/2018/12/30/signature/)
Android 5.0 New Features:
-
Material Design style
-
Support for 64-bit ART virtual machine (The ART virtual machine introduced in 5.0, before 5.0 was Dalvik. The difference is: Dalvik requires the bytecode to be converted into machine code (JIT) each time it runs. ART pre-compiles the bytecode into machine code (AOT) when the application is first installed).
-
Notification details can be user-designed
Android 6.0 New Features:
-
Dynamic permission management
-
Support for quick charging switch
-
Support for folder drag and drop applications
-
Camera adds professional mode
Android 7.0 New Features:
-
Multi-window support
-
V2 signature
-
Enhanced Java 8 language features
-
Night mode
Android 8.0 (O) New Features:
-
Optimized notifications: Notification channels, notification flags, sleep notifications, notification timeout, notification settings, notification clearing
-
Picture-in-Picture mode: Set android:supportsPictureInPicture in the manifest for activities.
-
Background restrictions
-
Auto-fill framework
-
System optimizations and many other enhancements
Android 9.0 (P) New Features:
-
Indoor WIFI positioning
-
Support for “notch” screens
-
Security enhancements and many other improvements
Android 10.0 (Q) New Features:
-
Night mode: All applications on the phone can be set to dark mode.
-
Desktop mode: Provides a PC-like experience, but cannot replace a PC.
-
Screen recording: Start by long-pressing “screenshot” in the power menu.
Recommended Article: Official Android Developers Documentation (https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels)
Reference Answer: Role: Determines the size of the View through widthMeasureSpec and heightMeasureSpec.
Composition: A 32-bit int value, with the high 2 bits representing SpecMode (measurement mode) and the low 30 bits representing SpecSize (size under a certain measurement mode).
Three modes:
-
UNSPECIFIED: The parent container imposes no restrictions on the View; it can be any size. Commonly used internally by the system.
-
EXACTLY (Exact Mode): The parent view specifies an exact size SpecSize for the child view. Corresponds to match_parent or specific values in LayoutParams.
-
AT_MOST (Maximum Mode): The parent container specifies a maximum size SpecSize for the child view, which cannot exceed this value. Corresponds to wrap_content in LayoutParams.
and briefly describe their usage and layout efficiency
Reference Answer: Common layouts in Android are divided into traditional and new layouts.
Traditional Layouts (written in XML code, generated by code):
-
Frame Layout:
-
Linear Layout:
-
Absolute Layout:
-
Relative Layout:
-
Table Layout:
For nested multi-layer Views, their layout efficiency: LinearLayout = FrameLayout >> RelativeLayout.
15. Differentiate between Animation and Animator, and summarize their principles
Types of Animation: The former only has four properties: transparency, rotation, translation, and scaling, while the latter can perform dynamic changes on any property of the control (as long as there is a setter method for that property).
Operable Objects: The former can only perform animations on UI components, while property animations can perform animations on almost any object (regardless of whether it is displayed on the screen).
Where is Glide’s source code design subtle?
Reference Answer: Image loading libraries: Fresco, Glide, Picasso, etc.
The subtlety of Glide’s design lies in:
-
Glide’s Lifecycle Binding: It can synchronize the image loading state with the current page’s lifecycle, allowing the entire loading process to start/resume, stop, and destroy with the page’s state.
-
Glide’s Caching Design: Caching design for Resource through (three-level cache, LRU algorithm, Bitmap reuse).
-
Glide’s Complete Loading Process: The Engine class exposes a series of methods for Request operations.
17. How to bypass the restrictions of 9.0?
Reference Answer:
18. What network loading libraries have you used?
Reference Answer: Network loading libraries: OkHttp, Retrofit, xUtils, Volley, etc.
Recommended Articles:
Android OkHttp Source Code Analysis Beginner’s Tutorial (One) (https://juejin.im/post/5c46822c6fb9a049ea394510)
Android OkHttp Source Code Analysis Beginner’s Tutorial (Two) (https://juejin.im/post/5c4682d2f265da6130752a1d)
(Grayscale, Mandatory Update, Regional Update)
Internal Updates::
-
Obtain the online version number via the interface, versionCode.
-
Compare the online versionCode with the local versionCode and pop up an update window.
-
Download the APK file (file download).
-
Install the APK.
Grayscale Update::
-
Find a single channel to deploy a special version.
-
Modify the upgrade platform to allow pushing upgrade notifications or even mandatory upgrades for some users.
-
Open a separate download entrance.
-
Both versions’ code are packed into the app package, and a testing framework is implanted in the app to control which version to display. The testing framework communicates with the server-side API, controlling the distribution of app versions A/B among users. The server will have corresponding reports to show the quantity and effect comparison of versions A/B. Finally, the server can control switching all users to version A or B online.
-
Regardless of the method, version management work must be done well, assigning special version numbers for distinction. Of course, since it’s grayscale, data monitoring (routine data, new feature data, main business data) must also be in place, and necessary data points must be established. Additionally, the grayscale version should ideally have the ability to roll back, generally to the next official version.
Mandatory Update: Generally, the handling is to pop up a notification window informing users of a version update upon entering the application, which may not have a cancel button. Thus, users can only choose to update or close the app. Of course, a cancel button can be added, but if users choose to cancel, they are directly exited from the app.
Incremental Update: The binary differential tool bsdiff is a corresponding patch synthesis tool that generates a patch file based on two different versions of the binary files. Using bspatch, the old APK file is combined with the patch file to create a new APK. Note that the APK file’s MD5 value is used to distinguish versions.
-
Kotlin is a cross-platform, statically typed general-purpose programming language with type inference. Kotlin is designed to be fully interoperable with Java, relying on Java class libraries for its JVM version, but type inference allows for more concise syntax.
-
Flutter is an open-source mobile application development framework created by Google. It is used to develop applications for Android and iOS, as well as the primary method for creating applications for Google Fuchsia.
-
Regarding the importance of Kotlin, I believe everyone can experience it in daily development, and it is necessary to avoid syntax sugar (such as singleton patterns, null checks, higher-order functions, etc.) in practical development.
-
As for Flutter, the current official Google documentation is still incomplete, and there are few projects on the market developed using this language. For deeper understanding, please refer to Xianyu and the official documentation.
If you find this helpful, feel free to share it!Share this with more people!
Developer Community
Leave a Comment
Your email address will not be published. Required fields are marked *