前言

Android应用的启动性能是用户体验的重要组成部分。一个启动缓慢的应用不仅会让用户感到烦躁,还可能导致用户放弃使用。

本文将深入探讨Android应用启动优化的各个方面,包括启动流程分析、优化方法、高级技巧和具体实现。

一、Android应用启动流程深度解析

在进行启动优化之前,我们需要深入了解Android应用的启动流程。Android应用的启动可以分为冷启动、温启动和热启动三种类型,其中冷启动是最耗时的,也是我们优化的重点。

冷启动流程详解

  1. Zygote进程启动

    • Zygote是Android系统的一个特殊进程,它是所有应用进程的父进程。
    • 当系统启动时,Zygote进程会被创建并加载常用的类和资源。
  2. 创建应用进程

    • 当启动一个应用时,系统会通过Zygote进程fork出一个新的应用进程。
    • 这个过程涉及到内存空间的分配和初始化。
  3. 创建Application对象

    • 应用进程创建后,会首先创建Application对象并调用其onCreate()方法。
    • 这是应用代码执行的起点,很多开发者会在这里进行各种初始化操作。
  4. 启动主线程

    • 创建主线程(UI线程)并初始化消息循环系统。
  5. 创建Activity对象

    • 系统会创建启动Activity的实例,并调用其生命周期方法。
  6. 加载布局

    • 解析XML布局文件,创建View树。
  7. 绘制界面

    • 计算View的大小和位置,进行绘制操作。
二、启动时间测量与分析

在优化之前,我们需要先准确测量应用的启动时间,以便确定优化的基准和效果。

1. 使用adb命令测量
adb shell am start -W com.example.app/com.example.app.MainActivity

输出结果中的关键指标:

  • ThisTime:最后一个Activity的启动耗时
  • TotalTime:应用的启动耗时
  • WaitTime:AMS启动Activity的总耗时
2. 代码埋点测量

在Application和Activity的关键生命周期方法中添加时间记录:

public class MyApplication extends Application {private static long sAppStartTime;@Overridepublic void onCreate() {super.onCreate();sAppStartTime = System.currentTimeMillis();Log.d("Startup", "Application onCreate start: " + sAppStartTime);// 执行初始化操作Log.d("Startup", "Application onCreate end: " + (System.currentTimeMillis() - sAppStartTime));}public static long getAppStartTime() {return sAppStartTime;}
}public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d("Startup", "Activity onCreate: " + (System.currentTimeMillis() - MyApplication.getAppStartTime()));}@Overrideprotected void onResume() {super.onResume();Log.d("Startup", "Activity onResume: " + (System.currentTimeMillis() - MyApplication.getAppStartTime()));}
}
3. 使用Systrace分析

Systrace是Android平台上强大的性能分析工具,可以详细记录系统各个组件的活动。

// 在代码中添加Trace标记
import android.os.Trace;public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();Trace.beginSection("Application onCreate");// 初始化操作Trace.endSection();}
}
三、启动优化核心方法
1. 减少Application初始化时间

Application的onCreate()方法是很多开发者进行全局初始化的地方,但过多的初始化会导致启动变慢。

优化策略

  • 延迟初始化:将非关键的初始化操作延迟到真正需要使用时再进行。
  • 异步初始化:将耗时的初始化操作放到后台线程。
  • 使用ContentProvider并行初始化:利用ContentProvider的并行初始化特性。
  • 按需初始化:根据用户的使用场景,选择性地初始化组件。

示例代码

public class MyApplication extends Application {private static final String TAG = "MyApplication";@Overridepublic void onCreate() {super.onCreate();long startTime = System.currentTimeMillis();// 关键初始化(必须在主线程执行)initCriticalComponents();// 非关键初始化(后台线程执行)Executors.newSingleThreadExecutor().execute(() -> {Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);initNonCriticalComponents();});Log.d(TAG, "Application onCreate: " + (System.currentTimeMillis() - startTime) + "ms");}private void initCriticalComponents() {// 初始化配置(主线程)ConfigManager.init(this);// 初始化数据库(主线程快速操作)DatabaseManager.init(this);}private void initNonCriticalComponents() {long startTime = System.currentTimeMillis();// 初始化推送服务PushService.init(this);// 初始化图片加载库ImageLoaderConfig.init(this);// 初始化统计分析工具Analytics.init(this);Log.d(TAG, "Non-critical components initialized: " + (System.currentTimeMillis() - startTime) + "ms");}
}
2. 优化布局加载与渲染

复杂的布局会显著影响启动速度,特别是首屏渲染时间。

优化策略

  • 减少布局层级:使用ConstraintLayout替代多层嵌套的LinearLayout或RelativeLayout。
  • 避免过度绘制:移除不必要的背景和重叠的视图。
  • 使用ViewStub和merge标签:延迟加载非关键视图。
  • 优化自定义View:避免在onMeasure、onLayout和onDraw方法中进行耗时操作。

示例代码

<!-- 使用merge标签减少布局层级 -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!" />
</merge><!-- 使用ViewStub延迟加载不常用的视图 -->
<ViewStubandroid:id="@+id/stub_ad"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout="@layout/layout_ad" />
// 在需要显示广告时加载
ViewStub stub = findViewById(R.id.stub_ad);
if (shouldShowAd()) {stub.inflate();
}
3. 优化首屏数据加载

首屏数据加载是影响用户体验的关键因素。

优化策略

  • 预加载数据:在Application或Splash界面提前加载数据。
  • 缓存机制:使用内存缓存或磁盘缓存,避免重复加载相同数据。
  • 异步加载:将非关键数据的加载放到后台线程。
  • 懒加载:对于不可见区域的数据,延迟到用户滚动到该区域时再加载。

示例代码

public class MainActivity extends AppCompatActivity {private RecyclerView mRecyclerView;private ItemAdapter mAdapter;private List<Item> mItems = new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initViews();// 异步加载首屏数据loadFirstScreenDataAsync();// 首屏渲染完成后,加载其他数据new Handler(Looper.getMainLooper()).postDelayed(this::loadOtherData, 500);}private void initViews() {mRecyclerView = findViewById(R.id.recycler_view);mRecyclerView.setLayoutManager(new LinearLayoutManager(this));mAdapter = new ItemAdapter(mItems);mRecyclerView.setAdapter(mAdapter);}private void loadFirstScreenDataAsync() {long startTime = System.currentTimeMillis();Log.d("Startup", "Loading first screen data...");new Thread(() -> {// 模拟网络请求,只加载首屏需要的数据List<Item> firstScreenItems = DataLoader.loadFirstScreenItems();runOnUiThread(() -> {mItems.addAll(firstScreenItems);mAdapter.notifyDataSetChanged();Log.d("Startup", "First screen data loaded in " + (System.currentTimeMillis() - startTime) + "ms");});}).start();}private void loadOtherData() {long startTime = System.currentTimeMillis();Log.d("Startup", "Loading other data...");new Thread(() -> {// 模拟网络请求,加载其他数据List<Item> otherItems = DataLoader.loadOtherItems();runOnUiThread(() -> {mItems.addAll(otherItems);mAdapter.notifyDataSetChanged();Log.d("Startup", "Other data loaded in " + (System.currentTimeMillis() - startTime) + "ms");});}).start();}
}
四、高级启动优化技巧
1. 使用Android App Startup框架

Android App Startup是官方提供的启动优化框架,可以帮助我们更好地管理和优化组件的初始化顺序。

集成步骤

  1. 添加依赖:
implementation 'androidx.startup:startup-runtime:1.1.1'
  1. 创建Initializer:
public class MyServiceInitializer implements Initializer<MyService> {@NonNull@Overridepublic MyService create(@NonNull Context context) {// 初始化MyServiceMyService service = new MyService(context);service.initialize();return service;}@NonNull@Overridepublic List<Class<? extends Initializer<?>>> dependencies() {// 指定依赖关系return Arrays.asList(OtherServiceInitializer.class);}
}
  1. 在AndroidManifest.xml中配置:
<providerandroid:name="androidx.startup.InitializationProvider"android:authorities="${applicationId}.androidx-startup"android:exported="false"tools:node="merge"><meta-dataandroid:name="com.example.MyServiceInitializer"android:value="androidx.startup" />
</provider>
2. 使用ContentProvider并行初始化

ContentProvider的onCreate()方法会在Application的onCreate()之前被调用,并且多个ContentProvider的初始化是并行的。

示例代码

public class MyContentProvider extends ContentProvider {@Overridepublic boolean onCreate() {// 执行初始化操作BackgroundTaskExecutor.execute(() -> {// 后台线程初始化HeavyLibrary.init(getContext());});return true;}// 其他方法省略...
}
3. 优化类加载

类加载是启动过程中的一个重要环节,可以通过以下方法优化:

  • 减少启动时加载的类:避免在启动路径上加载不必要的类。
  • 使用MultiDex:对于方法数超过65536的应用,合理配置MultiDex。
  • 预加载类:在Application的attachBaseContext()方法中预加载关键类。
public class MyApplication extends Application {@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);// 预加载关键类preloadClasses();}private void preloadClasses() {try {Class.forName("androidx.core.content.ContextCompat");Class.forName("androidx.appcompat.app.AppCompatActivity");// 预加载其他关键类} catch (ClassNotFoundException e) {e.printStackTrace();}}
}
五、Splash Screen优化

使用Splash Screen可以在应用真正启动前显示一个简单的界面,给用户一种应用启动很快的感觉。

实现方法

  1. 创建Splash主题:
<!-- res/values/styles.xml -->
<style name="SplashTheme" parent="Theme.MaterialComponents.Light.NoActionBar"><item name="android:windowBackground">@drawable/splash_background</item><item name="android:windowFullscreen">true</item><item name="android:windowContentOverlay">@null</item>
</style><!-- res/drawable/splash_background.xml -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"><item android:drawable="@android:color/white" /><itemandroid:gravity="center"android:drawable="@mipmap/ic_launcher" />
</layer-list>
  1. 在AndroidManifest.xml中设置Splash主题:
<activityandroid:name=".SplashActivity"android:theme="@style/SplashTheme"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter>
</activity>
  1. 创建SplashActivity:
public class SplashActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 启动主ActivityIntent intent = new Intent(this, MainActivity.class);startActivity(intent);finish();// 移除Activity切换动画overridePendingTransition(0, 0);}
}
六、启动优化实战案例

下面是一个完整的启动优化实战案例,展示了如何综合应用上述优化方法:

public class MyApplication extends Application {private static final String TAG = "MyApplication";private static long sAppStartTime;@Overridepublic void onCreate() {super.onCreate();sAppStartTime = System.currentTimeMillis();Log.d(TAG, "Application onCreate start: " + (System.currentTimeMillis() - sAppStartTime));// 关键初始化,必须在主线程执行initCriticalComponents();// 非关键初始化,放到后台线程执行Executors.newSingleThreadExecutor().execute(this::initNonCriticalComponents);Log.d(TAG, "Application onCreate end: " + (System.currentTimeMillis() - sAppStartTime));}private void initCriticalComponents() {// 初始化配置ConfigManager.init(this);// 初始化数据库DatabaseManager.init(this);// 初始化崩溃报告CrashReport.init(this);}private void initNonCriticalComponents() {long startTime = System.currentTimeMillis();// 初始化推送服务PushService.init(this);// 初始化图片加载库ImageLoaderConfig.init(this);// 初始化统计分析工具Analytics.init(this);Log.d(TAG, "Non-critical components initialized in " + (System.currentTimeMillis() - startTime) + "ms");}public static long getAppStartTime() {return sAppStartTime;}
}
public class MainActivity extends AppCompatActivity {private RecyclerView mRecyclerView;private ItemAdapter mAdapter;private List<Item> mItems = new ArrayList<>();private boolean mIsFirstScreenRendered = false;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d("Startup", "Activity onCreate: " + (System.currentTimeMillis() - MyApplication.getAppStartTime()));initViews();}@Overrideprotected void onResume() {super.onResume();Log.d("Startup", "Activity onResume: " + (System.currentTimeMillis() - MyApplication.getAppStartTime()));// 首屏渲染完成后再加载其他数据if (!mIsFirstScreenRendered) {mIsFirstScreenRendered = true;loadFirstScreenData();// 使用Handler.post()确保在首屏渲染完成后执行new Handler(Looper.getMainLooper()).post(this::loadOtherData);}}private void initViews() {mRecyclerView = findViewById(R.id.recycler_view);mRecyclerView.setLayoutManager(new LinearLayoutManager(this));// 使用预布局优化首屏渲染mAdapter = new ItemAdapter(mItems, true);mRecyclerView.setAdapter(mAdapter);}private void loadFirstScreenData() {Log.d("Startup", "Loading first screen data: " + (System.currentTimeMillis() - MyApplication.getAppStartTime()));new Thread(() -> {// 模拟网络请求,只加载首屏需要的10条数据List<Item> firstScreenItems = DataLoader.loadItems(10);long loadTime = System.currentTimeMillis() - MyApplication.getAppStartTime();Log.d("Startup", "First screen data loaded in " + loadTime + "ms");runOnUiThread(() -> {mItems.addAll(firstScreenItems);mAdapter.notifyDataSetChanged();Log.d("Startup", "First screen UI updated: " + (System.currentTimeMillis() - MyApplication.getAppStartTime()));});}).start();}private void loadOtherData() {Log.d("Startup", "Loading other data: " + (System.currentTimeMillis() - MyApplication.getAppStartTime()));new Thread(() -> {// 模拟网络请求,加载剩余数据List<Item> otherItems = DataLoader.loadItems(20, 10);Log.d("Startup", "Other data loaded: " + (System.currentTimeMillis() - MyApplication.getAppStartTime()));runOnUiThread(() -> {mItems.addAll(otherItems);mAdapter.notifyDataSetChanged();Log.d("Startup", "Other UI updated: " + (System.currentTimeMillis() - MyApplication.getAppStartTime()));});}).start();}
}
七、启动优化测试与监控

优化完成后,需要进行全面的测试和监控,确保优化效果和应用稳定性。

1. 性能测试工具
  • Systrace:分析系统级别的性能瓶颈
  • CPU Profiler:分析CPU使用情况
  • Memory Profiler:监控内存使用情况
  • Startup Profiler:专门用于分析应用启动性能
2. 监控指标
  • 冷启动时间:从点击应用图标到首屏完全可见的时间
  • 温启动时间:应用在后台时的启动时间
  • 热启动时间:应用已经在内存中时的启动时间
  • 关键渲染时间:首屏内容渲染完成的时间
3. 线上监控

在生产环境中持续监控启动性能,可以使用以下方法:

public class StartupTimer {private static final String TAG = "StartupTimer";private static long sAppStartTime;private static long sFirstDrawTime;public static void start() {sAppStartTime = System.currentTimeMillis();Log.d(TAG, "Startup timer started");}public static void markFirstDraw() {if (sFirstDrawTime == 0) {sFirstDrawTime = System.currentTimeMillis();long startupTime = sFirstDrawTime - sAppStartTime;Log.d(TAG, "First draw completed in " + startupTime + "ms");// 上报启动时间到服务器reportStartupTime(startupTime);}}private static void reportStartupTime(long timeMs) {// 将启动时间上报到服务器Analytics.reportStartupTime(timeMs);}
}
八、启动优化总结

启动优化是一个系统工程,需要从多个方面入手,综合应用各种优化方法。在进行启动优化时,建议遵循以下步骤:

  1. 测量与分析:使用各种工具准确测量启动时间,找出瓶颈点。
  2. 优先级排序:根据耗时情况和优化难度,确定优化的优先级。
  3. 实施优化:应用本文介绍的各种优化方法。
  4. 验证效果:再次测量启动时间,验证优化效果。
  5. 持续监控:在应用发布后,持续监控启动性能,确保优化效果的持续性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/88779.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/88779.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/88779.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前沿重器[69] | 源码拆解:deepSearcher动态子查询+循环搜索优化RAG流程

前沿重器栏目主要给大家分享各种大厂、顶会的论文和分享&#xff0c;从中抽取关键精华的部分和大家分享&#xff0c;和大家一起把握前沿技术。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。&#xff08;算起来&#xff0c;专项启动已经…

Vue+axios

1. axios简介axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;主要用于浏览器和 Node.js 环境中发送 HTTP 请求。它是目前前端开发中最流行的网络请求库之一&#xff0c;被广泛应用于各种 JavaScript 项目&#xff08;如 React、Vue、Angular 等框架或原生 JS 项目&#x…

通过Tcl脚本命令:set_param labtools.auto_update_hardware 0

1.通过Tcl脚本命令&#xff1a;set_param labtools.auto_update_hardware 0 禁用JTAG上电检测&#xff0c;因为2016.1 及更高版本 Vivado 硬件管理器中&#xff0c;当 FPGA正连接编程电缆时 重新上电&#xff0c;可能会出现FPGA无法自动加载程序的故障。 2.还可以通过 hw_serv…

Spring Boot 安全登录系统:前后端分离实现

关键词&#xff1a;Spring Boot、安全登录、JWT、Shiro / Spring Security、前后端分离、Vue、MySQL 详细代码请参考这篇文章&#xff1a;完整 Spring Boot Vue 登录 ✅ 摘要 在现代 Web 应用中&#xff0c;用户登录与权限控制是系统安全性的基础环节。本文将手把手带你实现…

Docker高级管理--Dockerfile 镜像制作

目录 一&#xff1a;Docker 镜像管理 1:Docker 镜像结构 &#xff08;1&#xff09; 镜像分层核心概念 &#xff08;2&#xff09;镜像层特性 &#xff08;3&#xff09;关键操作命令 &#xff08;4&#xff09;优化建议 2&#xff1a;Dockerfile介绍 &#xff08;1&…

Leetcode力扣解题记录--第42题 接雨水(动规和分治法)

题目链接&#xff1a;42. 接雨水 - 力扣&#xff08;LeetCode&#xff09; 这里我们可以用两种方法去解决巧妙地解决这个题。首先来看一下题目 题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。…

宝塔配置pgsql可以远程访问

本地navicat premium 17.0 可以远程访问pgsql v16.1宝塔的软件商店里&#xff0c;找到pgsql管理器&#xff1b;在pgsql管理器里找到客户端认证&#xff1a;第二步&#xff1a;配置修改&#xff0c;CtrlF 查找listen_addresses关键字&#xff1b;第三步&#xff1a;在navicat里配…

小架构step系列12:单元测试

1 概述 测试的种类很多&#xff1a;单元测试、集成测试、系统测试等&#xff0c;程序员写代码进行测试的可以称为白盒测试&#xff0c;单元测试和集成测试都可以进行白盒测试&#xff0c;可以理解为单元测试是对某个类的某个方法进行测试&#xff0c;集成测试则是测试一连串的…

SpringBoot3-Flowable7初体验

目录简介准备JDKMySQLflowable-ui创建流程图要注意的地方编码依赖和配置控制器实体Flowable任务处理类验证启动程序调用接口本文源码参考简介 Flowable是一个轻量的Java业务流程引擎&#xff0c;用于实现业务流程的管理和自动化。相较于老牌的Activiti做了一些改进和扩展&…

phpMyAdmin:一款经典的MySQL在线管理工具又回来了

phpMyAdmin 是一个免费开源、基于 Web 的 MySQL/MariaDB 数据库管理和开发工具。它提供了一个直观的图形用户界面&#xff0c;使得我们无需精通复杂的 SQL 命令也能执行大多数数据库管理任务。 phpMyAdmin 项目曾经暂停将近两年&#xff0c;不过 2025 年又开始发布新版本了。 …

存储服务一NFS文件存储概述

前言&#xff1a; 网络文件系统&#xff08;Network File System&#xff0c;NFS&#xff09;诞生于1984年&#xff0c;由Sun Microsystems首创&#xff0c;旨在解决异构系统间的文件共享需求。作为一种基于客户端-服务器架构的分布式文件协议&#xff0c;NFS允许远程主机通过T…

libimagequant 在 mac 平台编译双架构

在 macOS 上编译 libimagequant 的双架构&#xff08;aarch64 x86_64&#xff09;通用二进制库&#xff0c;以下是完整步骤&#xff1a;​​1. 准备 Rust 工具链​​ # 安装两个目标平台 rustup target add aarch64-apple-darwin x86_64-apple-darwin# 确认安装成功 rustup ta…

暑期自学嵌入式——Day01(C语言阶段)

点关注不迷路哟。你的点赞、收藏&#xff0c;一键三连&#xff0c;是我持续更新的动力哟&#xff01;&#xff01;&#xff01; 主页&#xff1a; 一位搞嵌入式的 genius-CSDN博客https://blog.csdn.net/m0_73589512?spm1011.2682.3001.5343感悟&#xff1a; 今天我认为最重…

Flutter基础(前端教程⑧-数据模型)

这个示例展示了如何创建数据模型、解析 JSON 数据&#xff0c;以及在 UI 中使用这些数据&#xff1a;import package:flutter/material.dart; import dart:convert;void main() {// 示例&#xff1a;手动创建User对象final user User(id: 1,name: 张三,age: 25,email: zhangsa…

SSRF10 各种限制绕过之30x跳转绕过协议限制

ssrf漏洞在厂商的处理下可能进行一些特殊处理导致我们无法直接利用漏洞 有以下四种&#xff1a; 1.ip地址限制绕过 2.域名限制绕过 3.30x跳转绕过域名限制 4.DNS rebinding绕过内网ip限制 本章我们讲30x跳转绕过域名限制 30x跳转绕过域名限制 之前我们使用ssrf漏洞时可以…

DNS解析过程和nmap端口扫描

目录 DNS解析流程&#xff1a; nmap端口扫描 指定扫描方式 TCP全连接扫描 -sT SYN半连接扫描 -sS -sT和 -sS的区别 Linux提权 利用好谷歌语法查找敏感信息 如果自己搭建了网站文件要放在phpstudy_pro\WWW下。 如果想要使用域名访问网站&#xff0c;需要在phpstudy_pro…

【基于开源大模型(如deepseek)开发应用及其发展趋势的一点思考】

1. 开源大模型技术发展现状1.1 DeepSeek等主流开源大模型的技术特性分析 DeepSeek作为当前最具代表性的开源大模型之一&#xff0c;其技术架构具有多项创新特性。该模型采用混合专家架构(MoE)&#xff0c;通过将视觉编码分离为"理解"和"生成"两条路径&…

java8 ConcurrentHashMap 桶级别锁实现机制

Java 8 ConcurrentHashMap 桶级别锁实现机制 Java 8 中的 ConcurrentHashMap 抛弃了分段锁设计&#xff0c;采用了更细粒度的桶级别锁&#xff08;bucket-level locking&#xff09;实现&#xff0c;这是其并发性能提升的关键。下面详细解析其实现原理&#xff1a; 1. 基本实现…

Python正则表达式实战指南

一 正则表达式库正则表达式是文本处理中不可或缺的强大工具&#xff0c;Python通过re模块提供了完整的正则表达式支持。本文将详细介绍re模块中最常用的match()、search()和findall()函数&#xff0c;以及贪婪模式与非贪婪模式的区别&#xff0c;帮助读者掌握Python中正则表达式…

使用球体模型模拟相机成像:地面与天空的可见性判断与纹理映射

在传统相机模拟中&#xff0c;地面通常被建模为一个平面&#xff08;Plane&#xff09;&#xff0c;这在低空场景下是合理的。但在更大视场范围或远距观察时&#xff0c;地球的曲率不可忽视。因此&#xff0c;我们需要将地面模型从平面升级为球体&#xff0c;并基于球面与光线的…