阅读视图

发现新文章,点击刷新页面。

The Performance Design Of OS

1 Origin

I am embarking on a new series of articles addressing various considerations in OS architecture design. Indeed, these considerations are not limited to OS but are applicable to the design of any large-scale software.

I am limited by my capabilities and knowledge and bring a highly subjective view, and there are undoubtedly inadequacies. I am eager to hear different thoughts and perspectives, and through the collision of ideas, we can achieve a deeper understanding.

In my opinion, the core differences between Android and iOS from the OS perspective are primarily manifested in:

  1. The IPC mechanism between applications and core services
  2. Platform development environment, including programming languages, IDE tools, and the construction of the developer ecosystem
  3. Application lifecycle management mechanisms and strategies
  4. The runtime organizational structure of the kernel and core services

Why do they adopt different strategic decisions? It relates to the factors considered during architectural design. A software architectural decision is a selection of the most suitable decision for the present and foreseeable future amidst a series of current considerations; it is a collection of decisions. Thus, there’s no absolute right or wrong in architectural design, or rather, rational or irrational. Different projects and decision-makers face different considerations and prioritize different aspects. If architects of similar skill levels switch scenarios, they are likely to make similar decisions. This indicates that architectural design is an engineering process and a technical craft, learnable and following certain patterns.

The challenge in architectural design lies in accurately understanding the environment the organization operates within, current and foreseeable considerations, and finding the most suitable methodologies or tech stacks from existing engineering practices. It is evident that considerations play a significant role. What factors need to be considered in software architectural design? These include, but are not limited to, testability, component release efficiency, development efficiency, security, reliability, performance, scalability, etc. Experienced architects, especially those with operational experience in similar businesses, are better at discerning what to focus on at different times, what must be adhered to, and what can be relaxed or even abandoned. Considering the law of diminishing marginal returns, the consideration of influencing factors and decision-making behavior will permeate the entire lifecycle, introducing another art of decision-making.

We can summarize that:

  1. Under different stages and constraints of various considerations, software architectural designs on different projects may differ.
  2. During the entire cycle of software product iteration, such design decisions are always evolving.

Interestingly, one consideration may conflict with another, leading to a situation where one cannot have the best of both worlds. For example, improving component development efficiency might impact program performance. So, what were the considerations for the designers of Android and iOS in the context of mobile OS design? To answer this question, we first need to explain the relationship between the OS, application programs, and the kernel.

2 Mechanisms and Policies

First, it should be noted that the OS is part of the software stack above the hardware. It, along with application programs, constructs the complete software program stack, utilizing the hardware capabilities to provide services to users. From the hardware’s perspective, regardless of the OS or application programs, both are software; however, the OS has higher privileges, allowing it to operate in the CPU’s high-level modes, for tasks such as direct interaction with hardware and executing interrupt handling routines. From the software developer’s perspective, however, the OS and application programs are entirely different entities. Application programs utilize the capabilities provided by the OS to meet their business requirements or use hardware capabilities for tasks like playing music or storing data.

At a higher level, from the user experience perspective, application programs, OS, and hardware are all part of the same entity. When issues arise in their collaboration, ordinary consumers might simply feel that the device is less than ideal. Therefore, all three parties are obligated to cooperate and facilitate each other; only when the consumer is well-served can these entities sustainably profit.

In discussing the OS, it’s crucial to recognize that the vast majority of modern OSes consist of a kernel and system services.

  • In macOS/iOS, the kernel is Darwin, formed from the combination of XNU and the MACH microkernel. Its system services are provided by an array of daemon services, encapsulating kernel capabilities, data management, and higher-level APIs like display.
  • In Android, the kernel is the Linux kernel, with system services comprising C++ written daemon services (e.g., SurfaceFlinger) and Java written daemon services (e.g., SystemServer). They too encapsulate kernel capabilities, data management, and higher-level APIs concerning display rendering and composition.

Current mainstream operating systems include macOS, Windows, and various derivatives of Linux. Clearly, Android belongs to the Linux derivatives, with another renowned version in the developer community being Ubuntu. Due to the complexity and path-dependent nature of a complete OS, derivatives are often deployed on different hardware and application scenarios. For a while, Linux was lauded for its extensive device radiation and wide application scenario spectrum. However, being able to run an OS and running it well—connecting hardware and application programs to offer an optimal user experience—are two different things.

An increasingly common understanding is that the OS’s mechanisms, policies, and closely associated application programs vary greatly depending on the application scenario.

For instance, even if based on the same Linux kernel, the use and system services built upon it for embedded devices, smartwatches, smartphones, large servers, or even smart cars are distinctly different, as are the operating strategies of application programs. The Linux kernel and device drivers can be seen as the bridge between hardware and system services—a standard bridge—but the vehicles and pedestrians traversing it are entirely distinct.

This leads to another classic OS design concept: mechanism and policy.

The “separation principle” noted in UNIX programming specifies the separation of policy from mechanism and interface from the engine. Mechanisms provide capabilities; policies dictate how those capabilities are used.

In this context, the memory management, process management, VFS layer, and network programming interfaces that Linux provides are mechanisms. Memory allocation and release mechanisms, process schedulers, frequency and core allocators, and different file systems are policies. Going further, identifying which processes interact with users and which are background tasks, synchronizing this information to the scheduler to determine the optimal process for the next scheduling window is fundamentally a policy built upon the Linux process management mechanism. Similarly, notifying future computational task requirements to the CPU frequency allocator for dynamic adjustment (DVFS) involves a policy and a mechanism.

For instance, all modern OSes support memory compression capabilities, but different OSes use this mechanism according to their own characteristics to best meet business needs. iOS exhibits process-level compression, while Android relies on Linux’s ZRAM.

Though OS mechanisms might be similar, policies are diverse. The extent of their differences depends on the OS designers’ understanding of their own business—meaning the application programs running on the OS and the kind of experience and services they aim to provide users. Early Android was essentially a desktop architecture, but modifications, especially by domestic manufacturers, have made it increasingly resemble iOS, aligning more with the system capabilities required by mobile device operating systems.

The OS for smartphones and smart cars probably faces a similar scenario—they cannot be directly transplanted.

One interesting aspect of policy is that implementing one policy often brings up another issue, necessitating the introduction of an additional policy. When one policy compensates for another, a chain forms, eventually creating a closed-loop mechanism. In other words, all policies must be in effect simultaneously to maximize the system’s benefits. When learning about a competitor OS’s policies, remembering this aspect is essential; otherwise, we might only grasp the superficial aspects, leading to “negative optimization” once the features are launched.

Now, narrowing it down to Android and iOS, where do their strategy designs originate?

3 Butts Decide Heads

Apple has released a series of operating systems including macOS, iOS, iPadOS, watchOS, and tvOS. The distinctions between them are not merely in brand names but are characterized by specific strategy variations. While they largely share underlying mechanisms, their strategies are distinctly different. For instance, the background running mechanism on iOS is vastly different from that on macOS. iOS resembles a “restricted version of multitasking,” while macOS offers genuine multitasking. Hence, it’s not that iOS can’t implement multitasking but rather a deliberate design decision.

Android, as we refer to it, is actually a project open-sourced by Google, known as AOSP (Android Open Source Project). Device manufacturers adapt AOSP and integrate their services based on their business models and understanding of target users. Apple is singular, but there are numerous device manufacturers, each with their own profit models and interpretations of user needs. They modify AOSP accordingly, and the market decides which version prevails.

From a technical perspective, AOSP is rich in mechanisms but lacks in strategies. Google has implemented these strategies within its GMS services. Users outside mainland China, like those using Pixel or Samsung phones, would experience Google’s suite of services. Although the ecosystem is considered subpar in China due to the proliferation of substandard apps, the situation is somewhat mitigated overseas, but still not comparable to Apple’s ecosystem.

Given Google’s less-than-ideal strategic implementation, domestic manufacturers in China have carved out space for themselves. The intense competition and the sheer volume of phone shipments in mainland China have led manufacturers to prioritize consumer feedback and innovative adaptations of AOSP.

The most significant difference between iOS and Android stems from their respective strategies, rooted in their initial service objectives and developmental goals.

Books like “Steve Jobs” and “Becoming Steve Jobs” touch upon the development of the iPhone and the discussions around AppStore. Jobs was initially resistant to allowing third-party app development on mobile devices due to concerns about power consumption, performance, and security. The initial intent was to create a device that offered an unparalleled user experience, not necessarily catering to every user demand.

As Apple had written the first batch of apps themselves, they amassed a wealth of insights on designing excellent embedded device applications, leading to the creation of effective API systems. This comprehensive approach from hardware to software was not for the sake of exclusivity, but a necessary path to crafting the best user experience.

Contrastingly, during 2007-2008, Android was focused on getting the system up and running. Android’s initial aim was to accommodate a vast array of app developers, leading to its favoring of Java, a popular language among developers and in the embedded device domain. Although Android later shifted to Android Studio, improving the development experience, it still lagged behind Apple’s Xcode in terms of application development and debugging tools.

Apple’s strong control over its app ecosystem, partly attributed to its powerful IDE tools, aids developers in solving problems rather than imposing constraints. Further, initiatives like LLVM, Swift, and SwiftUI underscore Apple’s commitment to facilitating superior app development to enhance the user experience.

The purpose of designing an OS is profit-oriented, and it should facilitate app developers in crafting quality programs. Apple has showcased that offering quality developer services can be instrumental in achieving optimal device experiences. A summary of insights gleaned from Apple’s approach includes:

  1. Building an OS is a means; delivering a complete and excellent experience is the end goal. Both the OS and device manufacturers may need to put in extra effort to achieve this objective.
  2. Serve app developers well, assist them in improving app quality, and even identify and diagnose app issues.
  3. Provide faster and more user-friendly APIs to efficiently meet the needs of app developers.
  4. An excellent IDE tool can serve developers well, enabling the development of superior apps, and ensuring the OS’s survival.

While Apple exercises absolute control, it also offers software services that are significantly above industry standards. Offering an OS is merely a means; understanding the nature of the relationship with developers and providing developer services, such as IDE, is a more profound consideration at the cognitive level.

4 Strategy of “Overload Protection”

The greatest feature of mobile devices is their portability, enabled by battery power. Besides, as handheld devices, they primarily rely on passive cooling since they don’t have an active cooling mechanism (exceptional cases of gaming phones and attachable fans aside). Currently, there are two trends: one, the transistor fabrication process is inching closer to its physical limit, and two, more functionalities are being integrated into a single chip. This increase in the number of active transistors (or their area) leads to a corresponding rise in heat emission, although it wasn’t a primary concern during the early days of smartphones. Now, the balance between power consumption and performance has become a significant challenge for smartphones.

More active threads mean the CPU remains busy, resulting in reduced CPU time slices allocated to user-related programs, thus impacting performance. Therefore, the design of mobile device OSes naturally leads to restrictions on resource utilization by applications. If left unrestricted like servers or desktop computers, it would be impossible to maintain a balance between performance, power consumption, and heat dissipation. The more constrained a device is in terms of performance and power consumption, the stricter the control over application programs, as is the case with smartwatches.

Both Android and iOS have their resource protection mechanisms. In Android, the most common is the OOM (Out Of Memory) mechanism. When the heap memory usage of a Java application exceeds a certain threshold, the system terminates it. Although Android has a mechanism to detect excessive CPU usage, it is somewhat rudimentary and only monitors the CPU usage of regular applications, not system or native thread (written in languages other than Java).

In contrast, iOS has a plethora of mechanisms ranging from CPU, memory, to even restrictions on excessive IO writes, including:

  • Termination when the device overheats
  • Termination of VoIP class applications when there are excessive CPU awakenings
  • Termination during BackgroundTask execution if CPU use exceeds a threshold
  • Termination if BackgroundTask is not completed within the specified time
  • Termination if a program’s thread exceeds CPU use threshold
  • Termination if a program’s disk write volume exceeds a threshold
  • Termination if program’s inter-thread interactions within a unit time exceed a threshold
  • Termination if a program’s memory usage is exceeded
  • Termination under excessive system memory pressure
  • Termination if a program opens too many files
  • Termination during PageCache Thrashing

iOS outlines these behaviors in developer documentation to clarify the reasons for unexpected application exits.

Google’s lax approach to Android’s design has provided ample room for domestic manufacturers to introduce their overload protection strategies (similar to iOS’s, with minor variations) to ensure phones are not compromised by substandard applications. However, the issue lies in the lack of transparency about system termination behaviors. Developers are often in the dark about why their applications are terminated. Even if they are aware of the reasons, the lack of debugging information during termination impedes improvement efforts since no manufacturer releases this information.

Consequently, application developers resort to various “black technologies” to keep their applications alive and bypass the system’s detection mechanisms. What should have been a collaborative ecosystem building effort has turned into a battleground. In the end, both parties suffer, with consumers bearing the brunt of the damage.

In an ideal world:

  1. Overload protection mechanisms should be documented and explained in application development guides.
  2. Debugging information context should be saved when the system executes overload protection, and developers should have access to this information (with specific permissions, scope, and validity to be determined).
  3. Manufacturers should provide convenient and user-friendly debugging tools for developers to fix issues locally during development.
  4. Developers should be mandated to fix issues when they exceed the quality standards set by the manufacturers, failing which their applications should be delisted.

Manufacturers and developers should be partners. Manufacturers may need to do more to assist developers, as many capabilities are exclusive to them. Blaming developers solely for poor quality is not a competitive approach for manufacturers.

The fault, in this case, is at the cognitive level.

5 Strategy on “Lifecycle Management”

Different device forms pursue varied user experience requirements, leading to diverse OS design necessities. In desktop OS, the lifecycle of an application is entirely under its control, aiming to maximize the program’s potential. This design is viable because desktop computers are not constrained by power consumption and heat dissipation and rarely face performance bottlenecks. Their primary concern is exploiting the machine’s capabilities to the fullest.

On the contrary, smartphones are a different story due to their limitations in power consumption and heat generation. Similarly, smartwatches also suffer from these restrictions but to a more stringent degree. No one desires a watch that heats up their wrist and cannot last a day on a full charge. Moreover, their performance and memory limitations mean that too many apps can’t remain active in the background, necessitating a centralized management module to uniformly implement services for most common applications, known as a hosted architecture. While smart cars aren’t constrained by performance, power, or heat, they require high stability. Unless completely powered down, core system services must remain operational, emphasizing the importance of system anti-aging design.

A core strategy in smartphone OS design revolves around lifecycle management, determining the entire journey of an application from its inception to termination. Android leans towards desktop system design, offering a “looser” strategy and more room for developers to maneuver. In contrast, iOS imposes more restrictions; an application relegated to the background only has about 5 seconds to perform background tasks before entering the Suspend state. In this state, the application is denied CPU scheduling, rendering it “quiet” when in the background.

Chinese manufacturers, after obtaining AOSP code, have replicated a mechanism similar to iOS’s Suspend. However, due to the lack of native support in AOSP, compromises were made, resulting in an implementation not as thorough as iOS. Android interprets this running strategy as the developers’ responsibility to create well-crafted applications – a notion I find naive and impractical. By this logic, human societal development would never have required laws, an idea that contradicts human nature. Fortunately, Google might have realized this issue, gradually enhancing the so-called “freezing” strategy in their annual updates, albeit less effective than improvements made by domestic manufacturers. The progress in AOSP is slow, and substantial changes in this area aren’t expected in the next two to three years.

So, if an application is Suspended in the background on iOS, how can it perform required background computations? iOS introduced the BackgroundTask mechanism, allowing applications to request permission for background task execution, with the system intelligently scheduling these tasks. Hence, iOS offers a strategy for application background operation but places the final decision in the system’s hands. This allows the system to schedule background tasks based on the phone’s current status, avoiding task execution during high system load periods to reduce overall load. The system also assigns daily quotas to each application, incorporating execution frequency and duration as crucial factors. Generally, tasks are allowed about 30 seconds of execution before being terminated by the system.

However, background tasks aren’t limited to computations. How are requirements like playing music or location tracking addressed? Applications needing these services must declare them explicitly in the IDE, with the App Store checking for a match between the application and requested permissions – a mismatch leads to rejection. The App Store is central to iOS’s lifecycle management mechanism, enabling quality control during the application’s listing and operational phases. Applications identified as subpar are flagged for the developers to fix, facing delisting otherwise. Post-Suspend, the system may also terminate applications as part of overload protection. The most common reason is memory reclamation, especially given the expense of memory chips; without opting for larger memory, terminating applications is the only way to free up more memory.

So, if the application isn’t even running, how are background tasks executed, and messages received? Thanks to BackgroundTask design, even if an application is terminated, the system will automatically restart it to execute background tasks when conditions are met. Message reception is achieved through notification mechanisms, with two kinds: one displaying detailed content in the notification bar, activating the application only upon user interaction; the other is for VoIP class applications, capable of actively restarting terminated applications.

Android possesses a similar mechanism but requires the integration of its GMS service. Due to uncertain reasons, this service is inaccessible in China, forcing domestic apps to rely on various “dark arts” and commercial collaborations to keep their programs alive in the background for message reception. This has led to a grotesque scenario where head applications, often used by users, are greenlit by manufacturers, who, upon realizing this trend, keep intensifying various services, treating the phone as their playground and squeezing every bit of system memory. Could manufacturers offer a notification service akin to this? They could, but the construction and operational costs are disproportionately high compared to their sales profits, leading to the only option of increasing memory capacity, passing the price pressure onto consumers. The overall cost of a complete machine has an upper limit; bolstering memory means cutting corners elsewhere. For domestic manufacturers to break into the high-end market, recognizing the issues in the entire loop and co-building the ecosystem with applications is the sole breakthrough.

Looking at iOS’s design, compared to macOS, it restricts application freedom but isn’t a one-size-fits-all solution. It offers various “windows of opportunity” or “unified solutions” to cater to different developers’ needs. The objective is to allow developers to operate within reasonable boundaries, not to drain users’ battery and performance.

Summarizing the principles beyond the technology:

  • Mobile devices have many constraints; therefore, application “freedom” must be restricted but not completely cut off, requiring corresponding solutions.
  • Common tasks among applications should be provided uniformly by the system, saving overall system load, especially crucial for devices with many constraints.
  • The final execution power of a program should be determined by the system, which, after synthesizing various information, schedules uniformly, benefiting the ultimate user experience protection.

At this point, it seems like a clash between two regimes: one valuing freedom and individual priority, and the other advocating unified arrangement and scheduling. Regardless of the regime type, the ultimate objective must be considered. If the aim is to offer the best device user experience, evidently, the latter regime has been proven right by the market.

6 Above Design

Looking back at the history of electronic consumer products, the development has mainly followed two themes: the democratization of professional equipment and the integration of multifunctionality (N in 1 style). The reliance on CPU computation is gradually being replaced by Domain Specific Architecture (DSA). Upon DSA, domain-specific programming languages and compilers are constructed, with GPU and Shader Language in the graphic processing domain serving as prime examples. The era where software reaps the benefits of CPU performance enhancement is drawing to a close, and DSA appears to be the opportunity for the next “great leap” in the coming decade.

M1 epitomizes the dividends brought by regular microarchitecture and manufacturing process, but its impact is magnified due to the subpar performance of competing products. When a product’s core components are supplied by specific manufacturers, its developmental ceiling is essentially predetermined. This underscores the oft-repeated adage that core technologies must be self-controlled. Besides its CPU capabilities, M1 excels in multimedia processing, especially in video stream processing scenarios, outperforming Intel chips substantially. These performance enhancements are attributed to the processor’s performance uplift in specific scenarios.

However, this doesn’t signify the end of the road for performance enhancements based on CPUs. As CPU performance enhancements stagnate, precise understanding of demands and optimizations of matrices and architectural designs to boost performance on existing CPUs become imperative. Profound insights into hardware, compilers, algorithms, and operating systems (both frameworks and kernels) are increasingly crucial. After optimizing business codes to a certain extent, focus inevitably shifts towards the underlying layers.

Accumulated experience from numerous failures is essential to anticipate issues and design optimal architectures and optimization matrices proactively. An optimization matrix refers to the necessity of an ensemble of complementary technologies, not just an OS, to deliver an exceptional experience. This includes IDEs, cloud collaboration, and accurate cognition. Offering a supreme experience is a daunting task, but the more one learns, the more possibilities become apparent. By the same token, maintaining a perpetual “awareness of one’s unawareness” is equally pivotal.

However, all these are contingent upon the designers’ ability to keep pace with their cognition.

Charlie Munger once articulated that investment isn’t merely about scrutinizing financial statements and trend charts. Psychology, sociology, political science, and even biology are intricately linked to it. Only by dismantling the barriers between disciplines and integrating contents from multiple fields without reservations can one perceive a world invisible to others. While I haven’t attained such an enlightenment, Munger’s insights offer invaluable lessons worthy of our learning. Deliberate cross-disciplinary and cross-field practice, coupled with reflective thinking, significantly augments the learning process.

“I leave my sword to those who can wield it.” - Charlie Munger

About Me && Blog

  1. About Me: I am eager to interact and progress together with everyone.
  2. Follow me on Twitter
  3. Blog Content Navigation
  4. Record of Excellent Blog Articles - Essential Skills and Tools for Android Performance Optimization

An individual can move faster, a group can go further.

OS 设计之性能设计

本文是之前星球里 Yingyun 大佬的文章,由于星球已经关闭,所以把这个关于 OS 性能设计的系列文章发到博客上

Yingyun 是资深性能优化专家,他对于系统优化有非常深入的见解,本身在国内各个手机大厂都呆过,他本人的博客还在休整中,等休整好了我再发出来,目前他在我们的微信群里很活跃,对本文有什么建议或者意见,或者说想咨询问题的可以加我们的微信群(加我微信 553000664,备注博客加群,我会拉你进去)

1 缘起

新开系列文章,OS 架构设计中的各种考量因素。其实不止 OS,在设计任何大型软件都涉及到此类内容。

能力与知识面有限,而且还带了非常主观的看法,肯定有不足之处。希望听到不同的思路与观点,通过观点的碰撞进而达到更进一步的认知。

我认为,Android 与 iOS 在 OS 角度看最核心的差异主要表现为:

  1. 应用与核心服务之间的 IPC 机制
  2. 平台开发环境,包括编程语言、IDE 工具、开发者生态建设
  3. 应用生命周期管理机制与策略
  4. 内核与核心服务的运行时组织结构

他们为什么会有不同的策略决策呢?这跟架构设计时的考量因素有关。一个软件架构设计决策是在当前一系列考虑因素中选择了对当前与可见的未来选择的最合适的决策,是一系列决策的集合。所以,架构的设计上没有绝对的对与错,或者说合理与不合理。因为不同项目、不同决策者所面临的的考虑因素、追求的侧重点是都不尽相同。 如果架构师的水平差不多,把他们互换下情境,那大概率上所做出的决策是差不多的。这说明架构设计是一个工程,是一个技术手艺,它是可以被习得且有规律可循的。

架构设计中的挑战,可能更多的是在于准确理解组织所处的环境、当前与可见未来所要满足的考量因素,并从已有工程实践中找出最合适的方法论或者技术栈。由此可见,考虑因素就起了重要作用,在软件架构设计里要考虑哪些因素呢?包括但不限于,可测试性、组件发布效率、组件开发效率、安全性、可靠性、性能、扩容性等等。有经验的架构师,特别是对类似业务有操盘经验,他更能把握好不同时期应该注重什么,哪些是必须坚持的,哪些是适当放开甚至舍弃的。考虑到边际收益递减,影响因素的考量与决策行为会贯穿整个生命周期,这又是另一个「决策艺术」了。

我们可以总结出:

  1. 不同阶段与不同考量因素的限制下,不同项目上软件架构设计可能是不同的。
  2. 软件产品迭代的整个周期内,这种设计决策是随时会发生。

有意思的是一个考量因素可能会与另外一个有冲突,会造成鱼和熊掌不可兼得局面。比如提高了组件开发效率但有可能会影响到程序性能。那针对一个移动 OS 的设计,当初 Android 与 iOS 的设计者们的考量因素是什么呢?为了回答这个问题,首先要解释 OS 与应用程序以及内核之间的关系。

2 机制与策略

首先要说明的是 OS 是硬件之上的软件栈的一部分,它与应用程序一道构建了完整的软件程序栈,通过发挥硬件的能力为用户提供服务。从硬件的角度看,甭管 OS 还是应用程序,它们都是软件,只是 OS 的权限比较高,可以在 CPU 的高级别模式下运行,比如用于直接跟硬件打交道、执行中断处理程序等。但是在软件开发者角度来看,OS 与应用程序,那可是完全不一样的。应用程序利用 OS 提供的能力,实现自身的业务需求、或者使用硬件的能力,比如播放音乐、存储数据等。

再拔高一个层次,在用户体验角度来看,应用程序、OS 以及硬件,都是一回事情。 当他们协作出现问题,普通消费者可能就觉得这台设备就不够理想。因此,这三方都有义务互相配合好、互相为彼此提供便利,只有把消费者伺候舒服了,这三家才有可持续的利润可赚。

当我们说到 OS,绝大部分现代 OS 是由内核(Kernel)跟系统服务组成。

  • 在 macOS/iOS 中,它的内核是 Darwin,而 Darwin 又是由 XNU 与 MACH 微内核组而成。它的系统服务,是由一大堆 daemon 服务提供,他们封装了内核的能力与数据管理、界面显示等级别的 API。
  • 在 Android 中,它的内核是 Linux 内核,它的系统服务既有 C++ 编写的 daemon 服务(如,SurfaceFlinger)、又有 Java 编写的 daemon 服务( 如,SystemServer),他们同样也是封装了内核的能力与数据管理、界面渲染与合成等级别的 API。

现在主流的操作系统有 macOS、Windows 以及基于 Linux 的各种衍生版本。显然,Android 是属于基于 Linux 的衍生版本,当然还有个在开发者圈子中更有名的延伸版本,那就是 Ubuntu。 由于一个完整的 OS 的复杂性与路径依赖特性,往往会将一个延伸版本部署到不同的硬件与不同的应用场景上。有一阵子 Linux 就是以此为标榜,即它辐射到了多少台设备,应用场景有多广之类。能运行一个 OS 跟是否运行得好,即把硬件、应用程序连接起来,提供了最优的用户体验,完全是两码事情。

一个越来越普遍的认知是,不同的应用场景下,所需要的 OS 的机制与策略以及与之紧密配合的应用程序,是完全不一样的。

比如,即使是基于同一个 Linux 内核,用于嵌入式设备、智能手表以及智能手机、大型服务器甚至是智能汽车,它们使用 Linux 的方式与构建在它之上的系统服务均是不一样的,当然应用程序的运行策略也不尽相同。Linux 内核与设备驱动可以理解为硬件与系统服务之间的桥梁,是一个标准的桥梁,但是跑在它上面的车辆与行人,是完全不同的。

这就要引申出另一个非常经典的 OS 设计理念,机制与策略。

UNIX 编程一书中有提到「分离原则:策略同机制分离,接口同引擎分离」,机制提供能力,策略提供能力方法。

其中,Linux 提供的内存管理、进程管理、VFS 层、网络编程接口均是机制。内存分配与释放机制、进程调度器、频率与核分配器以及不同的文件系统,他们均是策略。更进一步,由系统服务识别出哪些进程是跟用户有交互、哪些是后台任务,将此类信息同步到调度器后找出下一个调度周期窗口里的最佳进程,本质上也是基于 Linux 进程管理机制之上的策略。 同样道理,根据未来所需的计算任务需求将其信息通知到 CPU 频率分配器进行动态调整(DVFS),前者是策略后者是机制。

再比如,所有的现代 OS 都支持内存压缩能力,但是不同的 OS 要根据自身的特点来使用此机制,目的是尽可能满足业务特点。iOS 中可以看到针对进程级别的压缩,而 Android 中反倒是依赖 Linux 的 ZRAM。

OS 的机制或许类似,但是策略千差万别,他们之间的差异有多大,取决于 OS 设计者对自身业务的理解。 自身业务,指的是运行在 OS 之上的应用程序,到底要为用户提供什么样的体验与服务。早期的 Android 其实就是桌面机架构,随着国内厂家对它的魔改,反倒是越来越像 iOS 了,越来越符合一个移动设备操作系统所需要的系统能力了。

智能手机、智能汽车的 OS,估计也是同样局面,不可生搬硬套。

策略还有个很有意思的特点,当你实施一个策略的时候会引申出另外一个问题,为此你要引入另一种策略。当一个策略弥补另外一个策略,逐渐会形成一个链条,你会发现你形成了一个闭环的机制。 即,所有的策略同时生效,才能使你的系统发挥出最大的效益。 当我们学习竞品 OS 的策略的时候,一定要记得这一点,否则只学会了皮毛,功能上线后会带来更大的「负优化」。

那具体到 Android 与 iOS,他们的策略设计是从何而来?

3 屁股决定脑袋

苹果推出的操作系统有 macOS,iOS,iPadOS,watchOS 与 tvOS。它们之间并不是单纯的品牌名称差别,而是有具体的策略差异。底层的机制大部分有共享,但是策略却截然不同。iOS 上的后台运行机制与 macOS 就截然不同,iOS 更像是「限制版多任务」,而 macOS 是真正的多任务。所以,iOS 并不是不能实现多任务,而是它的有意为之。

我们所说的安卓,其实是谷歌开源的项目,即 AOSP(Android Open Source Project)。设备厂商拿到 AOSP 与与硬件的相关的代码之后会在此基础上加入自己的各种服务,这是基于它们自身的商业模式、目标用户的理解,所创造出来的。苹果只有一个苹果,但是设备厂家就有很多,它们各自的盈利模式跟对目标用户理解不同,在 AOSP 基础上魔改了一遭,至于哪个好,就让市场先生来做判断吧。

回到技术本身,AOSP 中有大量的机制但是缺乏策略。谷歌把这些策略实现在了 GMS 服务中。 如果你在非大陆地区使用安卓手机,如 Pixel、三星手机,会体验到谷歌的全家桶。大家都会说国内的生态比较烂,垃圾应用比较多,到了海外这可能会缓解。 其实也就那样,谷歌的生态控制跟影响能力跟苹果是没法比的。

连谷歌自身的策略表现不尽人意,那就更为国内厂商发挥拳脚腾出了空间。中国大陆手机出货量累计合是全球最大的,而且竞争尤为激烈。因此它们会非常重视消费者的反馈,各种各样的需求与痛点挖掘,自然不在话下。 简单总结就是,国内厂商更懂消费者的需求,这也是国内厂家对 AOSP 做各种魔改与优化成立的底层逻辑了。

所以当你再次见到有个老板说 “做手机很简单嘛,拿开源安卓跟厂商的方案整合一下就行了”,离他赶紧远一点,跟着他混简直就是枉费青春。

iOS 与安卓之间两者最大的差异来自于策略,他们之间拥有的机制都差不多顶多效率上可能有差异,但更大的差异来自于策略上。

这跟它两刚开始时不同的服务对象与发展目标,导致了技术选型上的巨大差异。

「乔布斯传」与「成为乔布斯」两本书中,关于 iPhone 研发章节中都提到过关于 AppStore 的讨论。起初乔布斯坚持认为移动设备上由于功耗 / 性能与安全的考虑,不允许让三方应用开发程序。由于系统复杂,所以直接采用了 macOS 的内核以此实现多媒体、浏览器,以及播放音频与视频等功能。macOS 内核本身的运行成本较高,在此情况下再让三方应用运行,硬件根本吃不消。他们原本可以基于 iPod 上的系统实现 iPhone,但乔布斯又要实现世人从未见过的智能手机,上马 macOS 也是他不得已的选择。

随着第一代 iPhone 在用户侧的成功(商业上成功还没有开始),大家都在呼吁在 iTunes 上可以下载应用程序。其实第一代黑客们就是这么做的,因为很多 API 是跟 macOS 共享,因此通过一些逆向手段观察到了 iPhone 上编写应用程序的方法。但乔布斯坚决反对,因为还是担心这会破坏安全性跟设备使用体验。从此处就能看出,乔布斯的目的是打造一个拥有完美用户体验的设备,用户的呼吁或者需求,其实是并不是首要的。

但 VP 们不这么想,由于之前的 iTunes + iPod 组合的成功,VP 们私底下开始安排相关的工程研究了。直到后来来乔布斯也没有再坚持反对,只是说自己不再管了。

对 UX 界面的优雅性,特别是图标与界面的一流体验是刻在苹果骨子里的基因。即使是开放出 API,他们也对整个应用运行机制做了修改,从此开始与 macOS 上的程序执行策略有差异了。

由于第一批应用都是苹果自己写的,因此他们积攒了大量在嵌入式设备上良好设计的应用应该是怎样的,也设计出了非常有效的 API 体系。 在这种局面下,苹果有自己的 OS、自己的 IDE、自己的商店系统(当时还是跟 iTunes 共用),自然而然会设计出「最佳应用」应该长什么样。

这里头缺一不可,还记得前面提到过的观点吗? 当你要实施一个策略的时候,可需要另一个策略来解决前一个策略带来的问题,当策略变多的时候就有可能形成了一个环路。

从这儿可看出,为了打造出一个完美的体验,从硬件到软件全部打通,是必然的结果。 这不是为了封闭而封闭,而是为了打造最佳体验而做出的唯一一条路。

反观 07 - 08 年的 Android 阵营,他们还在忙着如何让系统跑起来。

在由 Cheet 著作的 「The Team That Built the Android Operating Systems」书中讲述了安卓从零开始被 Andy Rubin 创建的过程。它是由 Andy Rubin 离职后创业的公司,起初目标是给相机提供 OS。但是随着市场的演变,他们的目标变成了提供给移动设备,特别是手机的移动操作系统。Andy 当初的目标是创建一种可以平衡开发者与设备制造商以及运营商利益的真正意义上的完全开放的操作系统。这就要求它尽可能采用市面上已有的组件,将他们简单改在后适应嵌入式环境后快速部署上线。毕竟是有创业压力嘛,也不可能精雕细琢,只能用快速发展来解决各种体验问题了(主要是这时候 iPhone 还没出来)。再加上从零开始,他们没有配套的 IDE,也只能先提供简单的基于命令行的工具来构建应用程序, 因此从开始他们就缺乏整个应用运行环境的管控能力。不过这也是相对于苹果而言,毕竟两者的目的完全不同。

Android 的目标之一就是有大量应用程序可用,因此照顾到市面上人群最多的开发者是很想当然的思考方式。当时市场上开发者最多的编程语言是 Java,而 Java 在嵌入式领域里也是很受欢迎的。你可能很难相信,07 年的时候嵌入式设备性能很差,但为什么会是受欢迎的编程语言呢? 个人理解原因是,除特殊设备之外大部分设备其实不关心性能,基本维持在能用就行的程度。而且 Java 的可移植性,也大大降低了开发者的负担。为了使 Java 速度更快,Andy 团队还聘请了一位大神重新写一套适合嵌入式设备的虚拟机,这在当时看来都是正确的选择,只是没有 iPhone 出来之前。 不过运气好在,智能手机刚好碰上了黄金的 CPU 单核性能与制程爆发的十年,因此它跟 iOS 相比并没有逊色太多。

在安卓开发的早期,使用的是基于 Eclipse 的构建与开发工具,虽然谈不上非常优秀但是够用。但是痛苦的根源来源于对比,苹果很早之前开始构建自己的 IDE 工具,即 Xcode。这套工具里集成了大量的应用开发与调试以及分发功能,这极大的提高了应用开发工程师的效率。 虽然安卓将开发环境切入到 AndroidStudio 之后相比之前进步了很多,但这主要还是得益于 IntelliJ 本身的优秀,单纯应用开发角度来看跟 Xcode 相比还是弱了一些。Xcode 提供了非常方便的编写与调试程序性能的工具,这会使开发者通过简单的学习就能快速找出程序上低性能的代码段,大大提高了程序的质量而且还使整个过程很愉快,这在安卓上可不是这么一回事了。

由此可见,苹果能够在应用生态管控上的强势,其中一个重要的原因是得益于它的强大的 IDE 工具,提供方便的来帮助开发者解决问题,而不是一味地给他们压力。更进一步,LLVM、Swift 以及 SwiftUI,这些都是苹果了编写出更好地应用而所做的基础工具与语言。 目的当然是为了自身应用生态的发展,为消费者提供最佳的体验。 它的思路是尽可能服务好开发者,让他们编写更能契合系统机制的应用程序,即给你限制又给你解决方案。

设计 OS 的目的是盈利,因此要想办法帮助应用开发者开发好程序。很多 OS 提供了能力之后,应用如何编写就不归他们管了,他们往往会把这个责任放到应用开发者身上,从苹果身上可以看到,这种做法可能不利于整个设备的最优体验,吃亏的还是消费者自己以及厂商,因为把消费者给磨没了。

所以当我看到苹果的强大时候,除了硬件的强大,在软件生态的建设上面的思路是非常值得借鉴的,简单总结就是:

  1. 构建 OS 是手段,提供完整且优秀的体验是目的,双方通力合作才能达到此目的。特别是 OS 与设备制造商,可能要做出更多的努力。
  2. 服务好应用开发者,努力帮助他们写好应用,甚至发现与诊断能出应用的问题,协助开发者改进应用程序。
  3. 提供更快更好用的 API,尽可能高效的满足应用开发者的需求。
  4. 通过优秀的 IDE 工具服务好开发者,让他们在此基础上开发更多更优秀的应用,OS 才能有更好的机会存活下去。

可以看到虽然苹果有绝对的话语权,但同时也提供了远超于行业平局水平的软件服务。 再次强调,提供 OS 只是手段,要认识到与开发者建立怎样的关系、提供怎样的开发者服务(如 IDE),是在认知层面更为有意的事情。

4 策略之「过载保护」

移动设备的最大特性就是可移动,它是由电池供电实现了可移动性。除此之外,由于是手持设备因此散热基本靠被动散热,没有主动散热一说(奇葩的游戏手机与外挂式风扇另说)。现在有两个趋势,其一是晶体管的制作工艺越来越趋近于物理极限,其二是越来越多的功能直接封装在同一颗芯片上。处于活跃状态的晶体管数量变大(或者面积)之后发热量也是蹭蹭往上涨,这在智能手机刚开始普及那一会儿倒不是主要的矛盾。对于智能手机来说,现在更大的矛盾是,功耗与性能的平衡。

活跃的线程多了,就会使 CPU 一直处于工作状态,当然分给用户相关程序的 CPU 时间片也会少一些,性能也就受到影响了。所以移动设备 OS 的设计上,就自然的引申出了要对应用程序的资源使用上的限制,如像服务器、台式机一样完全放开,性能与功耗是无法保证了(当然还有发热)。越是性能跟功耗约束大的设备,对应用程序的管控就越严苛,比如智能手表。

Android 与 iOS 各自均有资源保护机制,Android 中最为常见的当属 OOM 机制了。当 Java 应用的堆内存使用超过一定阈值之后,就会被系统终止。它也有 CPU 使用过度的检测,但从实现上比较简陋,而且只会监听普通应用程序的 CPU 使用量,不监控系统以及由 Native 编写的线程(不用 Java 写)。

iOS 中可谓百花齐放,从 CPU 到内存,甚至 IO 过多写入也有限制,具体为:

  • 设备过热时被终止
  • VoIP 类应用有过多 CPU 唤醒时被终止
  • 执行 BackgroundTask 时使用 CPU 超过阈值时会被终止(备注: BackgroundTask 是 iOS 上后台执行时的状态)
  • 执行 BackgroundTask 时没有在规定时间内完成时会被终止
  • 程序的线程使用 CPU 超过阈值时被终止
  • 程序写数据到磁盘的量超过阈值时被终止
  • 单位时间内程序的线程之间的交互超过阈值时被终止(如两个线程互相唤醒)
  • 程序的内存超标时被终止
  • 系统内存压力过大时被终止
  • 程序打开了过多的文件时被终止
  • 系统遭受 PageCache Thrashing 时被终止

显然不止于此(可见未来,iOS 会增加更多限制),但以上是跟普通应用开发者最为密切的。iOS 将这些行为写在了开发者文档,让开发者知道自己的应用被异常退出时的原因。

谷歌对 Android 设计上的” 放松 “ ,可就给国内厂家留出了很多发挥空间。各家都有各自的资源过载保护策略(与 iOS 的类似,仅有或多或少的差异),它们尽可能保护手机不会被垃圾应用给搞坏了。但问题也恰恰出在这部分,由于系统的查杀行为没有明文化,开发者不知道自己的应用为什么会被终止。 即使知道了原因,也无法获取被终止时的调试信息,也就没办法做改进,因为没有哪家会把这类信息开放给应用开发者。

这导致的结果是,应用开发者不得不想出各种各样的所谓黑科技来使自己保活、绕过系统的各种检测机制。本应该由开发者一起共建的生态,现在变成了两家的攻防战了。到最后,是两败俱伤,而其中最受伤的就是消费者。

理想中的世界:

  1. 明文化资源过载保护机制,写在应用开发文档上。
  2. 当系统进行过载保护时将上下文调试信息保存下来,开发者可以查阅此类信息(具体权限、范围、有效期可以另定。总之要有方法,可以由开发者拿到此类调试信息)。
  3. 厂商提供方便好用的调试工具,可以由开发者在本地进行开发时修复问题使用。
  4. 当超出厂商制定的质量标准水位线,责令开发者进行修复,若不修复则下架应用。

厂商跟开发者应当是合作关系,可能厂商要做更多的事情用于帮助开发者,因为很多能力只有厂家才有。只是一味地责怪质量差是开发者的问题,这种厂商我觉得不太会有竞争力。

在认知维度上,就已经错了。

5 策略之「生命周期管理」

不同的设备形态所追求的体验要求不同,因此对 OS 的设计要求也不尽相同。在桌面机 OS 中一个应用程序的生命周期完全是由自己掌控的,目的是尽可能发挥程序的能力。能这么设计的原因是桌面机里没有功耗跟散热的限制,很少也会有性能上的瓶颈。对它来说首要考虑的问题是如何把机器的能力榨干。

而在智能手机上却是另一种情况,原因是它有功耗跟发热的限制。与此类似,智能手表上也有功耗与散热的限制,但是它比手机设备更为严苛。谁都不希望手腕里戴着一个会发热的表,而且续航一天都撑不了。除此之外,由于它的性能跟内存受限,不能驻留太多的程序在后台,因此需要有一个集中式管理的模块来统一实现绝大部分常见应用的服务,即所谓的托管式架构。智能汽车虽然没有性能、功耗以及发热的限制,但是它对稳定性的要求非常高。除非是汽车彻底断电,核心系统服务需要保持一直运行,因此对系统防老化的设计尤为重要。

智能手机 OS 设计中一个核心策略是关于生命周期管理的,它决定了应用程序的由生到死的整个过程。Android 的设计上更偏向于桌面机系统,因此策略的设计上比较「宽松」,留给开发者的发挥空间比较多。而 iOS 上限制比较多,一个应用退到后台之后只有 5 秒左右的时间用于执行后台任务,随后便进入到 Suspend 状态。在这种状态下应用程序是得不到 CPU 调度执行,因此在后台的时候应用会比较「安静」。

国内厂家拿到 AOSP 代码之后实现了类似 iOS 的这种 Suspend 机制,不过碍于 AOSP 原生的不支持,因此做了很多让步,这导致了整体效果上不如 iOS 来的彻底。Android 把这种运行策略解释为把应用写好应用开发者的责任,而我觉得这个想法是幼稚且不切实际的。如果这个逻辑成立的话,人类发展历史上都不需要有法律了,这是违背人性的事情。 不过好在谷歌可能意识到了问题,每年的更新中也逐步完善了俗称「冻结」的策略。不过能力奇差,远不及国内厂商所做的改进。AOSP 的进步也是比较缓慢,目测未来两三年内,这部分的进步速度也会一直缓慢,没有实质性的改变。

如果应用在后台被 Suspend 住了,那在 iOS 上如何实现需要后台计算的任务呢? 它引入了 BackgroundTask 的机制,让应用程序申请后台执行任务权限,由系统智能的调度执行应用程序的后台任务。所以,iOS 是给了一套策略让应用程序执行后台任务,但是决定权是交给系统执行的。这有利于系统根据当时手机的不同的状态调度后台任务,比如系统负载比较高的时候就不会执行后台任务了,目的是降低系统整体的负载。系统给每个应用还设置了每日配额的概念,比如一天之内允许你最多执行多少次等等,当然执行时间也是其中一个很重要的考量因素。一般允许执行 30 秒左右,超过之后就会被系统终止了。

但是后台任务不止于计算一种,播放音乐、位置定位,这种需求如何处理? 应用想要使用此类服务,需要在 IDE 中显示声明之后才可以使用,App Store 会检查应用与所申请权限是否匹配,不匹配时不给予通过。App Store 是 iOS 能够实现这套生命周期管理机制的很核心的一环,通过它实现了在上架期与运行期间的质量管控。当发现一个应用的质量较差的时候,会通知开发者让其修复,否则就会下架应用。 继 Suspend 之后系统也会随之终止应用程序,目的是过载保护。最常见的理由是回收内存,毕竟内存芯片非常贵,不上大内存的前提 下只能通过终止应用来腾出更多的内存了。

那我连应用程序都不执行了,又怎么执行后台任务?接收消息呢? 得益于 BackgroundTask 的设计,即使应用被终止了,当条件满足的时候系统还会自动的拉起你的程序执行后台任务。至于消息接收,则是通过消息通知机制来实现。分为两种,一种是在通知栏里能看到具体的内容,只有用户当点击此通知的时候才会唤醒应用程序。另一种则是 VoIP 类应用,可以主动拉起被终止的应用程序。

其实 Android 也有类似的机制,但是需要配合它的 GMS 服务使用,由于不确定的原因这个服务在国内是无法使用。因此国内的 App 不得不又要上各种各样的黑科技、商业合作等手段,使自己的程序在后台保活用于接收消息。这就造成了一个非常畸形的局面,头部应用由于是用户常用的,因此厂商也对它一路开绿灯。而厂家也发现这个现象之后,不断加码各类的服务,把手机当做自己的来用,尽可能榨干系统内存。 那有没有可能厂商自己提供类似的通知服务呢? 有,但是由于建设与运营成本跟自己的销售利润完全不成比例,大家也就只能加大内存容量了,可以把价格压力传递到消费者身上。整机的成本是有上限的,在内存这部分加大那就要在其他地方减弱。国内厂家要突破高端,首先要意识到整个环路的问题所在,与应用共建整个生态才是唯一的破局之道。

纵观 iOS 的设计,相比于 macOS, 限制了应用的自由度,但也不是一刀切方案。而是尽可能的提供了各种各样的「窗口期」或者「统一的解决方案」,以满足开发者的不同需求。目的是尽可能让开发者在合理的范围内做事情,而不是榨干用户的电量与性能。

总结下技术之上的设计原则

  • 移动设备受限因素多,因此要对应用「自由度」给予限制但不能一刀切,需要给予对应的解决方案。
  • 应用之间的共性任务要由系统统一提供,可节省系统整体负担,这对限制因素较多设备尤为重要。
  • 程序最终执行权交由系统而定,由他综合各类信息之后统一调度,这有利于保护最终的用户体验。

写到此处,似乎是两个体制的碰撞,一个崇尚自由,个体优先、一个崇尚统一安排与调度。无论是哪种体制,要看最终目的是什么,如果是要提供最佳的设备用户体验,显然后者体制被市场证明是正确的。

6 设计之上

纵观电子消费品的发展史,以专业设备平民化、 N in 1 式的功能整合,两个主旋律方向发展。纯靠 CPU 的计算也会被专用硬件代替,比如 DSA (Domain Specific Architecture)。在 DSA 之上,会构建领域专用的编程语言与编译器,与之最接近的就是图形处理领域,如 GPU 与 Shader Language。 软件吃 CPU 性能提升的红利已经接近了尾声,下一个十年的「大飞跃」,目前看来也就这 DSA 机会点了。

M1 本身代表了正常的微架构与制程工艺带来的红利,只是友商阵营太拉跨,把这种差距拉大了。当一个产品的核心部件由某几个特定供应商提供的时候,基本上也判定了其发展上限。这也就是常说的核心技术要掌握自己手里,是同样的道理。M1 中除 CPU 能力外,它在多媒体处理,特别是视频流的处理场景下相比 Intel 芯片性能指标超出一大截,能实现这些性能提升的得益于处理器的特定场景下的性能提升。

但这不代表基于 CPU 的性能提升已经走到尽头,正是因为 CPU 性能提升的停滞,通过对需求的准确理解,优化矩阵与架构设计来实现在已有 CPU 上的性能提升,变得更为紧迫。对硬件、编译器、算法以及操作系统(框架与内核)的理解,变得越来越重要。因为当你优化到一定层度的业务代码之后,注意力必然会往底层走。

只有相当多的失败的经验,才能未雨绸缪,以高屋建瓴的方式设计出最佳的架构与优化矩阵。 优化矩阵是指为了提供一个极佳的体验,并不是由一个 OS 就能搞定,他要有相配套的其他技术一起支撑才能做好。比如 IDE、比如云端配合、以及正确的认知。 想要提供一个极致的体验,是非常难的事情,但也正因为如此,你会发现了解越多可做的事情就越多。同样道理,也有别样的说法 → 使自己始终处于「知道自己不知道」的状态。

不过以上成立的前提,是设计者的认知要跟得上。

查理芒格说过,投资不是看看财报,看看走势图就能做好的。除经济学与金融学外,心理学、社会学、政治学、甚至生物学都有关系。只有当你踏平学科间的隔阂,不设边界地把多个学科内容融合在一起之后,才能看到别人看不到的世界。 我当然也没达到这种境界,芒格给世人的经验是我们值得学习的宝贵经验。努力跨学科、跨领域的刻意练习,如果能在此基础上做到思考输出,那对学习更有帮助。

我的剑留给能够挥舞它的人 - 查理 芒格

关于我 && 博客

下面是个人的介绍和相关的链接,期望与同行的各位多多交流,三人行,则必有我师!

  1. 博主个人介绍 :里面有个人的微信和微信群链接。
  2. 本博客内容导航 :个人博客内容的一个导航。
  3. 个人整理和搜集的优秀博客文章 - Android 性能优化必知必会 :欢迎大家自荐和推荐 (微信私聊即可)
  4. Android性能优化知识星球 : 欢迎加入,多谢支持~

一个人可以走的更快 , 一群人可以走的更远

微信扫一扫

❌