<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>江</title>
        <link>https://tangly1024.com/</link>
        <description>江阔不见岸</description>
        <lastBuildDate>Sun, 23 Jun 2024 07:27:11 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <copyright>All rights reserved 2024, JiuJiang Liu</copyright>
        <item>
            <title><![CDATA[Android 移动应用开发基础教程 微课版]]></title>
            <link>https://tangly1024.com/article/c15c29f9-a51a-4dfc-a7e4-71f47f867bc5</link>
            <guid>https://tangly1024.com/article/c15c29f9-a51a-4dfc-a7e4-71f47f867bc5</guid>
            <pubDate>Sat, 22 Jun 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-c15c29f9a51a4dfca7e471f47f867bc5"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-d323f8dd0f5e4207a42add43df442880" data-id="d323f8dd0f5e4207a42add43df442880"><span><div id="d323f8dd0f5e4207a42add43df442880" class="notion-header-anchor"></div><a class="notion-hash-link" href="#d323f8dd0f5e4207a42add43df442880" title="下载地址"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">下载地址</span></span></h2><div class="notion-text notion-block-0ce41550fd14472c90a3fe887af35961">Download Link <a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://demo.msy.plus/apks/androidDev-app-debug.apk">https://demo.msy.plus/apks/androidDev-app-debug.apk</a></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-137c0949e6fc49b19ac60c894bf1fd89" data-id="137c0949e6fc49b19ac60c894bf1fd89"><span><div id="137c0949e6fc49b19ac60c894bf1fd89" class="notion-header-anchor"></div><a class="notion-hash-link" href="#137c0949e6fc49b19ac60c894bf1fd89" title="Android 移动应用开发基础教程 微课版"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Android 移动应用开发基础教程 微课版</span></span></h2><div class="notion-text notion-block-401f9b403d2444bb887c7f7e0b919fe7">这里将书上所有的代码示例整合进一个app，方便大家学习的时候有个参考。同时也将可能遇到的错误记录在这份文件中自己学也是ok的。</div><div class="notion-text notion-block-c9e03a4c8404420bb3218340a4e67f93">记录阅读 《Android 移动应用开发基础教程 微课版》 ISBN-978-7-115-47309-7 遇到的坑</div><ul class="notion-list notion-list-disc notion-block-f3610872ad6844fbaa5c3380c882b4eb"><li>android studio version 4.0.1</li></ul><ul class="notion-list notion-list-disc notion-block-b1a2b15950ba4bee928855aef2292854"><li>android SDK 10.0+</li></ul><div class="notion-text notion-block-594a06aa6f1245fbb13d5a2f563b48a3"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/moshuying/androidDev.git">git仓库</a></div><div class="notion-text notion-block-a0955142be034c068385e514a7cfdfc3">相关文章
<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://www.msy.plus/2020/10/03/android-reading-book/">Java http请求及常见数据交互格式处理Android 文件分享</a></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-7f9878595f324ea99ad332bb9f77a790" data-id="7f9878595f324ea99ad332bb9f77a790"><span><div id="7f9878595f324ea99ad332bb9f77a790" class="notion-header-anchor"></div><a class="notion-hash-link" href="#7f9878595f324ea99ad332bb9f77a790" title="最终效果"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">最终效果</span></span></h2><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-4d18566284e84e368d8fbe36a4cb00b5"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F9edf71c7-930c-4f5a-b252-d1d4dfe4e407%2FUntitled.png?table=block&amp;id=4d185662-84e8-4e36-8d8f-be36a4cb00b5&amp;t=4d185662-84e8-4e36-8d8f-be36a4cb00b5&amp;width=668.5510864257812&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-row notion-block-c2c78a200fc940c19dc131190e189073"><div class="notion-column notion-block-65dd92ff5cf34a70a7f562121dd2610a" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-76c0c8bb5f654faab201faa068fe361b"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F635291a8-1190-4e0b-97dd-d637cca7b2f2%2FUntitled.png?table=block&amp;id=76c0c8bb-5f65-4faa-b201-faa068fe361b&amp;t=76c0c8bb-5f65-4faa-b201-faa068fe361b&amp;width=1488&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-ff52453b188e46d29bd850c728771fe0" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0526a9e07b744f559dd368dd78840174"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2Fcf327484-f5ea-42e1-841f-c5093925ae19%2FUntitled.png?table=block&amp;id=0526a9e0-7b74-4f55-9dd3-68dd78840174&amp;t=0526a9e0-7b74-4f55-9dd3-68dd78840174&amp;width=1483&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div></div><div class="notion-row notion-block-e5953ef3edab44d1868194787dc96282"><div class="notion-column notion-block-30741c98510f468989ac2b2bc1eb9202" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3004dc55b69d49319dcc48047865bc06"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:431px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F9379dc72-57dd-4635-933f-3875b8901e12%2FUntitled.png?table=block&amp;id=3004dc55-b69d-4931-9dcc-48047865bc06&amp;t=3004dc55-b69d-4931-9dcc-48047865bc06&amp;width=431&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-d40b042d365b4ea094392717f715bc3c" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-f6384707d77c498e9ad2858390aee8df"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:435px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F10ca479b-44ab-4029-9ea1-d4efeae21799%2FUntitled.png?table=block&amp;id=f6384707-d77c-498e-9ad2-858390aee8df&amp;t=f6384707-d77c-498e-9ad2-858390aee8df&amp;width=435&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div></div><div class="notion-row notion-block-4239f4e213234968953d31ba53857f8a"><div class="notion-column notion-block-825ff29c9359448e9247ed2324234434" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-af5a0b334f3f43c2beae716d2bf6f849"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:428px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F926dd9d7-f4a9-4eb5-b986-bb99b6aba3e1%2FUntitled.png?table=block&amp;id=af5a0b33-4f3f-43c2-beae-716d2bf6f849&amp;t=af5a0b33-4f3f-43c2-beae-716d2bf6f849&amp;width=428&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-5f0c64e76ab244c790abc484d4aef104" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0960496cd81f4833b49c387d224d0c43"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:424px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F74b3e061-1b8b-437b-93f7-61f489b21142%2FUntitled.png?table=block&amp;id=0960496c-d81f-4833-b49c-387d224d0c43&amp;t=0960496c-d81f-4833-b49c-387d224d0c43&amp;width=424&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div></div><div class="notion-row notion-block-a18176458aee4635ac5e5c10361012aa"><div class="notion-column notion-block-4e830624c1804294a7829f5d7091a4bc" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-52fd2c7b73404ec690f9161bac312354"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:431px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2Fc9f84fbd-4d8d-492c-a4ba-b242820f0ddf%2FUntitled.png?table=block&amp;id=52fd2c7b-7340-4ec6-90f9-161bac312354&amp;t=52fd2c7b-7340-4ec6-90f9-161bac312354&amp;width=431&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure></div><div class="notion-spacer"></div><div class="notion-column notion-block-37c8578953cc4518a36217801710beda" style="width:calc((100% - (1 * min(32px, 4vw))) * 0.5)"><div class="notion-blank notion-block-a57efe378ada4c289df730ce384401ca"> </div></div><div class="notion-spacer"></div></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-0a0bc1856c8f413cb51d04a6a5224722" data-id="0a0bc1856c8f413cb51d04a6a5224722"><span><div id="0a0bc1856c8f413cb51d04a6a5224722" class="notion-header-anchor"></div><a class="notion-hash-link" href="#0a0bc1856c8f413cb51d04a6a5224722" title="第一章 环境部署"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">第一章 环境部署</span></span></h2><div class="notion-text notion-block-2083d923a0624f519093fa04b98cb9f3">jetbrains会帮你安好android studio</div><div class="notion-text notion-block-9a780b762bce49eda9b6b4d541b6e235"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://www.jetbrains.com/toolbox-app/download/download-thanks.html">下载jetbrains toolbox</a></div><div class="notion-text notion-block-560fd2467bc34bb09da85e1f0f79eebf">安装好后列表里有Android studio 点击安装即可，也可以切换版本，我这里用的时4.0.1版</div><div class="notion-text notion-block-ef38ba9ff67847e2a0c0b37bf9c741bd">进入Android studio 后如果你没有安装 android sdk 会让你安装 sdk 这里可能需要科学上网，另外后面程序下载依赖也需要用到科学上网。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-4d0021b95589477c83b6c6ae4e8f7b76" data-id="4d0021b95589477c83b6c6ae4e8f7b76"><span><div id="4d0021b95589477c83b6c6ae4e8f7b76" class="notion-header-anchor"></div><a class="notion-hash-link" href="#4d0021b95589477c83b6c6ae4e8f7b76" title="gradle"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">gradle</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-e7d1e0f026c94d749366d4d8363c28e5" data-id="e7d1e0f026c94d749366d4d8363c28e5"><span><div id="e7d1e0f026c94d749366d4d8363c28e5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#e7d1e0f026c94d749366d4d8363c28e5" title="引用包"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">引用包</span></span></h3><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-4e40f4090eb847d8bbea4cb73abcde70" data-id="4e40f4090eb847d8bbea4cb73abcde70"><span><div id="4e40f4090eb847d8bbea4cb73abcde70" class="notion-header-anchor"></div><a class="notion-hash-link" href="#4e40f4090eb847d8bbea4cb73abcde70" title="设置版本号"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">设置版本号</span></span></h3><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-7d7006ee533940519f111114b39240c4" data-id="7d7006ee533940519f111114b39240c4"><span><div id="7d7006ee533940519f111114b39240c4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#7d7006ee533940519f111114b39240c4" title="第二章 核心组件-活动"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">第二章 核心组件-活动</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-ff7c20e47e8449898f6d5f7ca28cfe59" data-id="ff7c20e47e8449898f6d5f7ca28cfe59"><span><div id="ff7c20e47e8449898f6d5f7ca28cfe59" class="notion-header-anchor"></div><a class="notion-hash-link" href="#ff7c20e47e8449898f6d5f7ca28cfe59" title="活动是什么"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">活动是什么</span></span></h3><div class="notion-text notion-block-9f4ff604efb444448be90f0290d88c41">在android中运行任何应用都会看到不同的界面，这些界面及在界面中完成的各种操作，都通过活动完成</div><div class="notion-text notion-block-0c8aca141b8f4d99aed32d2404e8e0e5">活动具有一下特点</div><ul class="notion-list notion-list-disc notion-block-55340d594a57421682c819bf0e8c65fa"><li>可以通过返回键退出活动</li></ul><ul class="notion-list notion-list-disc notion-block-649d065401df4796bde265f465c2e713"><li>可通过home建返回桌面</li></ul><ul class="notion-list notion-list-disc notion-block-eae7d643364342e88745f82e491ff0ee"><li>可在活动中启动另一个界面，此时按返回键可返回前一个活动</li></ul><div class="notion-text notion-block-179fd1ca41f740ae8d5bf615b459878b">一个应用通常包含多个活动，活动之间相对独立，包含多个活动的应用，需要为其指定一个“主”活动，即启动应用时首先打开的活动。</div><div class="notion-text notion-block-4633cb7f0f1b4ca9ae0f3feb83f92dc3">Android 允许启动其他应用中的活动</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-f7413fbde1e0453ba3ab3d0ee0704f96" data-id="f7413fbde1e0453ba3ab3d0ee0704f96"><span><div id="f7413fbde1e0453ba3ab3d0ee0704f96" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f7413fbde1e0453ba3ab3d0ee0704f96" title="活动的基本操作"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">活动的基本操作</span></span></h3><div class="notion-text notion-block-dfc4812a24e4488ebe5b15522d10a869">为活动绑定自定义视图</div><div class="notion-text notion-block-a1b65dccfe6049959c07e1519a04763d">通常在活动的<code class="notion-inline-code">onCreate()</code>方法中使用<code class="notion-inline-code">setContentView()</code>方法来为活动绑定一个视图</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-159a5f0be6f4452899e7ad4b2cd4ce44" data-id="159a5f0be6f4452899e7ad4b2cd4ce44"><span><div id="159a5f0be6f4452899e7ad4b2cd4ce44" class="notion-header-anchor"></div><a class="notion-hash-link" href="#159a5f0be6f4452899e7ad4b2cd4ce44" title="启动另一个活动"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">启动另一个活动</span></span></h3><div class="notion-text notion-block-e6b07163a5e34e4482b4afc4e672c3de">启动活动使用的是<code class="notion-inline-code">startActivity()</code>方法</div><div class="notion-text notion-block-e4d711d7ed0a4e41905eaca7d11e16d4">结束活动则调用<code class="notion-inline-code">finish()</code>,为按钮绑定事件监听器后点击按钮调用<code class="notion-inline-code">finish()</code>即可实现点击按钮退出活动</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-52657fcc90144c9ea918796a1f02e035" data-id="52657fcc90144c9ea918796a1f02e035"><span><div id="52657fcc90144c9ea918796a1f02e035" class="notion-header-anchor"></div><a class="notion-hash-link" href="#52657fcc90144c9ea918796a1f02e035" title="在活动中使用Intent"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">在活动中使用Intent</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-e8cbae31cb75401c882ca7c96e0f3465" data-id="e8cbae31cb75401c882ca7c96e0f3465"><span><div id="e8cbae31cb75401c882ca7c96e0f3465" class="notion-header-anchor"></div><a class="notion-hash-link" href="#e8cbae31cb75401c882ca7c96e0f3465" title="显示Intent"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">显示Intent</span></span></h4><div class="notion-text notion-block-873b73612a9f4b2498c4851458e5031a">直接忽略1-6步，手动创建java class后给class继承<code class="notion-inline-code">Activity</code>后会自动引入相关包，后面步骤同理</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-6dd9afc002f140958e46183efc0344b8" data-id="6dd9afc002f140958e46183efc0344b8"><span><div id="6dd9afc002f140958e46183efc0344b8" class="notion-header-anchor"></div><a class="notion-hash-link" href="#6dd9afc002f140958e46183efc0344b8" title="隐式Intent"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">隐式Intent</span></span></h4><div class="notion-text notion-block-4f84c4d51ae740f5a38a16833fbb3fc0">这一步不要被<code class="notion-inline-code">android.support.v7.app.AppCompatActivity</code>吸引了注意力，后面的代码其实还是继承<code class="notion-inline-code">AppCompatActivity</code>,参照上面手动输入即可，android studio会自动引入，我这边自动引入为<code class="notion-inline-code">androidx.appcompat.app.AppCompatActivity</code>，如果过程中遇到问题，可以编辑<code class="notion-inline-code">build.gradle</code>**注意这个文件有两个，选(Module:app)**在<code class="notion-inline-code">dependencies</code>中加入一行<code class="notion-inline-code">compile &#x27;com.android.support:appcompat-v7:26.+&#x27;</code>然后android studio会报错，照着报错修复即可，这一步可能会操作到<code class="notion-inline-code">Migrate to AndroidX</code>，android studio也会自动为你备份，不必担心。</div><div class="notion-text notion-block-855ed8137743476e8aec44b3d9b24410"><b>这一步我在代码中做了各种分享功能</b>，<a target="_blank" rel="noopener noreferrer" class="notion-link" href="notion://www.notion.so/2020/10/03/android-document-sharing/">相关解析在这里</a>，<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/moshuying/androidDev/commit/8a91a9476387dcbc216f86e6ed553fecaa29f89c">源代码参考链接</a></div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1324689fa37845109b63460df8692a77" data-id="1324689fa37845109b63460df8692a77"><span><div id="1324689fa37845109b63460df8692a77" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1324689fa37845109b63460df8692a77" title="使用预定义操作"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">使用预定义操作</span></span></h4><div class="notion-text notion-block-99b9673d54b54a4dad1efe1a8108779b">这里使用隐式Intent打开联系人信息的地方可能不太一样了，参考官网给出的<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://developer.android.google.cn/training/contacts-provider/modify-data#java">样例</a></div><div class="notion-text notion-block-3214c018cabd4fe2a30049775e0dc8c0">我的代码实现也是参考官网给出的样例,实现也很简单，这里代码去掉了注释，<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/moshuying/androidDev/blob/8a91a9476387dcbc216f86e6ed553fecaa29f89c/app/src/main/java/com/example/moshuying/PredefinedOperation.java#L28">完整源代码在这里</a></div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-b443449d4c06459fbfc4de99ffd6bb75" data-id="b443449d4c06459fbfc4de99ffd6bb75"><span><div id="b443449d4c06459fbfc4de99ffd6bb75" class="notion-header-anchor"></div><a class="notion-hash-link" href="#b443449d4c06459fbfc4de99ffd6bb75" title="在活动之间传递数据"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">在活动之间传递数据</span></span></h3><div class="notion-text notion-block-5f689e7bd79f453f903629da904bbc64">这一章节本身简单学习了活动的传递方式，重点了解如何在活动中传递数据。</div><div class="notion-text notion-block-55b4f1f75def443494af3297fe5d8abf">书上的示例比较简单，new完intent后使用putExtra类似key,value的方式存进去就行。</div><div class="notion-text notion-block-7a2e4e94a60241ae9afc4879a0d330ce">当然在activity中传递数据的方法不止intent的，只是intent比较常用。</div><div class="notion-text notion-block-9823f66b86fd4b03a61f037fdb308eff">Activity之间传递数据的几种方式,参考自这篇<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://blog.csdn.net/ahuier/article/details/8953017">文章</a>,文章比较老了，方法竟然还是这些，甚至阿里巴巴的一些应用也是这样的。</div><ul class="notion-list notion-list-disc notion-block-55dc361acd0b4769a1f39387ac8b69cb"><li>使用Intent</li></ul><ul class="notion-list notion-list-disc notion-block-11fafe5dcb91450ab5b6be4dbd6f4ea3"><li>使用剪切板</li></ul><ul class="notion-list notion-list-disc notion-block-c4e74a931acf46a9a36990dbb47e87ee"><li>使用静态变量</li></ul><div class="notion-text notion-block-62adb887af92472aa68d413f58a8672b">Intent可以传递哪些类型的数据，<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://www.nowcoder.com/questionTerminal/c934814c65b049a6992bcbb246711022?from=14pdf">来源</a></div><ol start="1" class="notion-list notion-list-numbered notion-block-08da5fbc8fe04d5f8cf4c8c33dbebd09"><li>8种基本数据类型及其数组</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-258dd638d6914705aa30999873313a19"><li>String（String实现了 Serializable ）/CharSequence实例类型的数据及其数组</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-9b5bcdd75bd34f598c8b9fa7d345e9f9"><li>实现了Parcelable的对象及其数组</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-ecf8d128fdb046f5a49cbfba21d4eeee"><li>实现了 Serializable 的对象及其数组</li></ol><div class="notion-text notion-block-cc80e6b0a9e54e74bc3feecbbc4822e3">看到这里的时候我想到能在activity中获取数据，就能用这些数据动态创建页面，所以顺便看了下动态创建元素的办法。</div><div class="notion-text notion-block-d2d2d2e39f76432bac2c60045cea1659">实际上非常简单,先声明一个LinearLayout</div><div class="notion-text notion-block-4fdc582f1d7a4478b04745b44c663ef0">随后在对应的代码里获取到这个元素，再用<code class="notion-inline-code">addView()</code>即可动态添加</div><div class="notion-text notion-block-91fc4b13cf534d239caa78900025a983">想到动态添加元素就觉得可以根据后台请求的数据来生成各种界面，于是就又去了解了一下Android的网络请求方法<a target="_blank" rel="noopener noreferrer" class="notion-link" href="notion://www.notion.so/2020/10/09/android-document-sharing/">相关解析</a>，<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/moshuying/androidDev/blob/b11d9aa6e8ad6390583fb1b3571283e432d9803f/app/src/main/java/com/example/moshuying/network/ProductEntry.java#L107">关键代码</a></div><div class="notion-text notion-block-7d28099ec3c5450c86fd8be2fc137b3d">既然都动态元素添加了，肯定会遇到添加的元素超过容器大小（超出屏幕）的情况，这时在对应的布局文件外面放一层<code class="notion-inline-code">ScrollView</code>即可解决问题。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-32693154ed2643f5a4fde1a1135e65fb" data-id="32693154ed2643f5a4fde1a1135e65fb"><span><div id="32693154ed2643f5a4fde1a1135e65fb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#32693154ed2643f5a4fde1a1135e65fb" title="获取活动返回的数据"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">获取活动返回的数据</span></span></h4><div class="notion-text notion-block-1f9b94f080b44c8bbf99a06c61bda8da">这一小节有一小部分不太一样的地方，启动活动变成了<code class="notion-inline-code">startActivityforResult</code>,活动返回时也要用<code class="notion-inline-code">setResult</code>设置返回结果。</div><div class="notion-text notion-block-d4a87a347caa4900a201e2121bc9d2c0"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="notion://www.notion.so/msy-plus/25e24bdd97ec442db6585c69c4692b72?v=c878fb2247bb413d9cd3ab8d85b8bcb9&amp;p=c15c29f9a51a4dfca7e471f47f867bc5&amp;pm=s">完整代码</a></div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-64e6875d264344d1b3d0d4b537367ce6" data-id="64e6875d264344d1b3d0d4b537367ce6"><span><div id="64e6875d264344d1b3d0d4b537367ce6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#64e6875d264344d1b3d0d4b537367ce6" title="活动的生命周期"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">活动的生命周期</span></span></h3><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-24d74e0a6cfa4ae4a4329a4959d26167"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img src="https://www.notion.so/uploads/Android_Activity_Lifecycle.png?t=24d74e0a-6cfa-4ae4-a432-9a4959d26167" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-5b269e4f595241708c210bb1cb755b5f">这一章节有个检测活动生命周期的例子，这里我写了个直接获取的函数。</div><div class="notion-text notion-block-eb7d595dad0845459be7dfda26e1de1d">放到类里即可，记得把包名和类名换成你自己的即可</div><div class="notion-text notion-block-213b54963ee44cd0aa4ff48f5d875dac">随后在生命周期对应的地方（其实其他类中的函数都可以）调用一下即可</div><div class="notion-text notion-block-e26619928ece4e0c87795a6594755863">最后的效果如图</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-4d0eb8e73e7043edaddd7ca4a6344643"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img src="https://www.notion.so/uploads/Snipaste_2020-10-09_21-09-41.png?t=4d0eb8e7-3e70-43ed-addd-7ca4a6344643" alt="notion image" loading="lazy" decoding="async"/></div></figure><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-410fe76ad7934b9d81c6cdcaf0e1ba99" data-id="410fe76ad7934b9d81c6cdcaf0e1ba99"><span><div id="410fe76ad7934b9d81c6cdcaf0e1ba99" class="notion-header-anchor"></div><a class="notion-hash-link" href="#410fe76ad7934b9d81c6cdcaf0e1ba99" title="代码设置界面元素"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">代码设置界面元素</span></span></h3><div class="notion-text notion-block-be400c37219542819f3f4a5a36214dc4">比较喜欢用代码生成界面，免去一堆xml难以寻找自己定义的一些东西，同时debug起来也比较麻烦，这里总结一些代码操作ui元素的代码片段</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-75375ae2c9044cb5aed751030834119b" data-id="75375ae2c9044cb5aed751030834119b"><span><div id="75375ae2c9044cb5aed751030834119b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#75375ae2c9044cb5aed751030834119b" title="给某个活动设置主题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">给某个活动设置主题</span></span></h3><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-b967eb40211b44bdba1c4a22f82185b5" data-id="b967eb40211b44bdba1c4a22f82185b5"><span><div id="b967eb40211b44bdba1c4a22f82185b5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#b967eb40211b44bdba1c4a22f82185b5" title="创建弹出式对话框"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">创建弹出式对话框</span></span></h3><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-d1dae2c3683c4a6a9edbd3e281dcfe06" data-id="d1dae2c3683c4a6a9edbd3e281dcfe06"><span><div id="d1dae2c3683c4a6a9edbd3e281dcfe06" class="notion-header-anchor"></div><a class="notion-hash-link" href="#d1dae2c3683c4a6a9edbd3e281dcfe06" title="问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">问题</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-0b6ce0a775b1468d95ff18e51a450548" data-id="0b6ce0a775b1468d95ff18e51a450548"><span><div id="0b6ce0a775b1468d95ff18e51a450548" class="notion-header-anchor"></div><a class="notion-hash-link" href="#0b6ce0a775b1468d95ff18e51a450548" title="Android Studio --“Cannot resolve symbol” 解决办法"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Android Studio --“Cannot resolve symbol” 解决办法</span></span></h3><div class="notion-text notion-block-ff4df167b283454eb4c7ec09371ce6b0">Android Studio 无法识别同一个 package 里的其他类，将其显示为红色，但是 compile 没有问题。鼠标放上去后显示 “Cannot resolve symbol XXX”，重启 Android Studio，重新 sync gradle，Clean build 都没有用。</div><div class="notion-text notion-block-ffee872c49d44e439f33b82b0aa1010b">多半是因为 Android Studio 之前发生了错误，某些 setting 出了问题。解决方法如下：</div><div class="notion-text notion-block-355977378fd04720beab75af4bdfef57">点击菜单中的 “File” -&gt; “Invalidate Caches / Restart”，然后点击对话框中的 “Invalidate and Restart”，清空 cache 并且重启。语法就会正确的高亮了。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-9c48d9e3f62641969fd175158e6e5065" data-id="9c48d9e3f62641969fd175158e6e5065"><span><div id="9c48d9e3f62641969fd175158e6e5065" class="notion-header-anchor"></div><a class="notion-hash-link" href="#9c48d9e3f62641969fd175158e6e5065" title="参考文章"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">参考文章</span></span></h2><div class="notion-text notion-block-615f5434917f452aa88fa92a2914c735"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://maven.google.com/web/index.html#com.google.android.material:material">maven google 官方库</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://developer.android.google.cn/reference/com/google/android/material/packages">material 文档</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://blog.csdn.net/qq_35775053/article/details/108531983">Android Material Design全面解析（一）- MaterialButton篇</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://www.cnblogs.com/lijunamneg/archive/2013/04/18/3029356.html">设置TextView文字居中,代码实现android:layout_gravity</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://htmlcolorcodes.com/zh/yanse-ming/">HTML 颜色名</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://blog.csdn.net/u014649598/article/details/45580461">Android 动态设置margin</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://blog.csdn.net/chenxu6/article/details/41523943">Android color(颜色) 在XML文件和java代码中</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://juejin.im/post/6844903807046909966">Android 显示、隐藏状态栏和导航栏</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://blog.csdn.net/wumingxiaoqiang/article/details/52585530">Android修改APP版本号</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://blog.csdn.net/u013059863/article/details/49914513">Android之设置EditText输入类型(setInputType()方法和android:inputType属性)</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://blog.csdn.net/chaoyu168/article/details/51538996">Android Studio --“Cannot resolve symbol” 解决办法</a></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-417244cba5404703800cf5ac2fd6625f" data-id="417244cba5404703800cf5ac2fd6625f"><span><div id="417244cba5404703800cf5ac2fd6625f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#417244cba5404703800cf5ac2fd6625f" title="📎 参考文章"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📎 参考文章</span></span></h2><ul class="notion-list notion-list-disc notion-block-1074a8a5c9b94a9582deb9c7f19807b4"><li>一些引用</li></ul><ul class="notion-list notion-list-disc notion-block-3f27fffd792e4c208410aff59d9a27f9"><li>引用文章</li></ul><div class="notion-blank notion-block-332f0a6255a54d89afee2d603f7561a4"> </div><div class="notion-callout notion-gray_background_co notion-block-6a1068b7823b4a3f993d16098e3734ab"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">欢迎您在底部评论区留言，一起交流~</div></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[在WebGL中模仿UE场景效果]]></title>
            <link>https://tangly1024.com/article/309f154c-1795-400a-bc1b-840cc182238a</link>
            <guid>https://tangly1024.com/article/309f154c-1795-400a-bc1b-840cc182238a</guid>
            <pubDate>Sat, 09 Mar 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[在WebGL中模仿UE场景效果]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-309f154c1795400abc1b840cc182238a"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-gray_background_co notion-block-5903b6a9c76c4a40b40af23fadaeb7b1"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="😀">😀</span></div><div class="notion-callout-text">在WebGL中模仿UE场景效果</div></div><figure class="notion-asset-wrapper notion-asset-wrapper-embed notion-block-5d32401c492948539bc474f41312c9c7"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:320px"><iframe class="notion-asset-object-fit" src="https://demo.msy.plus/demo/r3f-animation-test/index.html" title="iframe embed" frameBorder="0" allowfullscreen="" loading="lazy" scrolling="auto"></iframe></div></figure><div class="notion-blank notion-block-389f6bbac40a44a9be5fe243a6cc0224"> </div><div class="notion-callout notion-gray_background_co notion-block-bcc6f9c1b2e844b882c0466e32efcd92"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">欢迎您在底部评论区留言，一起交流~</div></div><div class="notion-blank notion-block-2052c382c8334d4490ea86206f2158c0"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[使用pixi.js做特效]]></title>
            <link>https://tangly1024.com/article/fbc1f8ad-fbc8-4f6e-a3b8-215934bdf335</link>
            <guid>https://tangly1024.com/article/fbc1f8ad-fbc8-4f6e-a3b8-215934bdf335</guid>
            <pubDate>Sat, 09 Mar 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-fbc1f8adfbc84f6ea3b8215934bdf335"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><figure class="notion-asset-wrapper notion-asset-wrapper-embed notion-block-ad508d60f18f424498999ae0f33f6feb"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:320px"><iframe class="notion-asset-object-fit" src="https://demo.msy.plus/demo/raw-pixi.js-thebrain/index.html" title="iframe embed" frameBorder="0" allowfullscreen="" loading="lazy" scrolling="auto"></iframe></div></figure><div class="notion-blank notion-block-adb0f0c6c04446aa807079fc2a71d1f9"> </div><div class="notion-callout notion-gray_background_co notion-block-0b8ccd7abc2d47228402331450729e0c"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">欢迎您在底部评论区留言，一起交流~</div></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mapbox获取各种经纬度]]></title>
            <link>https://tangly1024.com/article/47a172ef-f89b-4358-9864-6f90190e9130</link>
            <guid>https://tangly1024.com/article/47a172ef-f89b-4358-9864-6f90190e9130</guid>
            <pubDate>Sat, 09 Mar 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-47a172eff89b435898646f90190e9130"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><figure class="notion-asset-wrapper notion-asset-wrapper-embed notion-block-c8b57b462f694965aa01980f5de7fe68"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:320px"><iframe class="notion-asset-object-fit" src="https://demo.msy.plus/demo/mapbox-getLngLat/index.html" title="iframe embed" frameBorder="0" allowfullscreen="" loading="lazy" scrolling="auto"></iframe></div></figure><div class="notion-callout notion-gray_background_co notion-block-24f1271c6682436b824364cae55449a2"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">欢迎您在底部评论区留言，一起交流~</div></div><div class="notion-blank notion-block-a548ccb3180446238e5b4700dc01a117"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WebGL大场景性能优化]]></title>
            <link>https://tangly1024.com/article/a670ef30-0fb5-47b5-ae7c-4c8366b67710</link>
            <guid>https://tangly1024.com/article/a670ef30-0fb5-47b5-ae7c-4c8366b67710</guid>
            <pubDate>Sat, 09 Mar 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[性能优化是一个比较大的话题，会涉及多个技术点，本篇文章旨在总结相关优化思路和方向，很多阐述都是浅尝辄止，并不对每项技术点做具体的深入剖析。对于大场景来说，一般优化可以分为以下几个大的优化方向。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-full-width notion-block-a670ef300fb547b5ae7c4c8366b67710"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-callout notion-gray_background_co notion-block-e0520a85799d460ca89332be7f0aeb21"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="😀">😀</span></div><div class="notion-callout-text">随着项目越来越复杂，很多对大场景渲染支持已经成为了“刚需”。但是，对于很多经验有限的同学，似乎找不到相关思路。那么，我们就来聊聊，如何进行 webgl 的性能优化。</div></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-8a218040de224684b79cca7bf3ce1400" data-id="8a218040de224684b79cca7bf3ce1400"><span><div id="8a218040de224684b79cca7bf3ce1400" class="notion-header-anchor"></div><a class="notion-hash-link" href="#8a218040de224684b79cca7bf3ce1400" title="📝 性能优化"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">📝 性能优化</span></span></h2><div class="notion-text notion-block-471dd65e8c8441b7aedfec806cd60345">首先性能优化是一个比较大的话题，会涉及多个技术点，本篇文章旨在总结相关优化思路和方向，很多阐述都是浅尝辄止，并不对每项技术点做具体的深入剖析。对于大场景来说，一般优化可以分为以下几个大的优化方向。</div><ol start="1" class="notion-list notion-list-numbered notion-block-5f131ae7997344e0adf9f00d2ac48009"><li>加载性能优化</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-7ee3a1a85fd54cfd9e5f8715f467ebd3"><li>渲染帧率优化</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-a159a0356c484538be5ddb2afb4a2952"><li>内存管理优化</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-d5d6745b8b594d22bf47212844fb4512"><li>交互操作优化</li></ol><div class="notion-blank notion-block-dee5855402fd4a65b6a3d26ee5df07ba"> </div><div class="notion-text notion-block-42707789b36d42599ff0cc8ffa243362">我们会根据每个大的方向，讲讲如何具体的采取哪些策略进行优化。我们首先需要知道造成渲染帧率不高的性能瓶颈在哪里。一般来说，我们使用 chrome 的开发者工具中的 performance 模块进行性能诊断，找到性能瓶颈主要是在 CPU 执行效率方面，还是在 GPU 渲染阶段的性能方面。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-898172aa784d41038f49748a426bcacd"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2Ff6198002-b6ce-4d1a-b8cd-0fd29895252f%2FUntitled.png?table=block&amp;id=898172aa-784d-4103-8f49-748a426bcacd&amp;t=898172aa-784d-4103-8f49-748a426bcacd&amp;width=720&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-ae6e3514848441e1a744a2e13e4f7665">如果主要性能瓶颈在 cpu 执行阶段，我们其实非常容易找到某段 js 的执行效率较低，可以通过各种算法优化，降低 js 算法的时间复杂度，从而达到优化执行时间的目的。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-989699537d354c488efcecb48fce55f4" data-id="989699537d354c488efcecb48fce55f4"><span><div id="989699537d354c488efcecb48fce55f4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#989699537d354c488efcecb48fce55f4" title="1. 加载优化"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b><b>1. 加载优化</b></b></span></span></h2><div class="notion-blank notion-block-1fe8a23ac1a244d58f47e1854b99f26f"> </div><div class="notion-text notion-block-68decd8046514a81a9e2ef564c05ec78">受制于网络速度，对于没有缓存的大场景首次加载，可能是比较费时的，所以我们需要尽量减少加载时间，提高用户体验。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-6cf134f2618e41358bb6126cf7a2a764" data-id="6cf134f2618e41358bb6126cf7a2a764"><span><div id="6cf134f2618e41358bb6126cf7a2a764" class="notion-header-anchor"></div><a class="notion-hash-link" href="#6cf134f2618e41358bb6126cf7a2a764" title="模型压缩"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">模型压缩</span></span></h3><div class="notion-text notion-block-461d9334208a452d844e28adffedf6fe">模型和贴图数据是整个加载过程中最为“重量级”的数据。因此，我们应当从建模阶段就定好规范，在保证外观效果的前提下，尽量使用较为精简(面数较少)的模型。在模型制作完成之后，我们也应当尽量选取一些压缩比较高的模型格式，例如 fbx、gltf 等进行模型传输。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-cc13089f3862447e8325bce83613cc8d" data-id="cc13089f3862447e8325bce83613cc8d"><span><div id="cc13089f3862447e8325bce83613cc8d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#cc13089f3862447e8325bce83613cc8d" title="gzip"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">gzip</span></span></h3><div class="notion-text notion-block-383722f9e6aa4be4990e2d8203953a08">对于一些纯字符编码的模型，如 obj，dae 等，在服务端开启 gzip 压缩，可以带来较好的压缩比。而且，使用 gzip 压缩是服务器与浏览器直接默认完成的，无需任何额外操作，对于开发者可以做到无感知。所以建议使用这类模型的都加上 gzip 压缩。其实我们可以默认所有文件都开启，因为 js css 文件经过 gzip 压缩后传输量也会小很多。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-f7d5ac946edf44f59ab22fd256f46416" data-id="f7d5ac946edf44f59ab22fd256f46416"><span><div id="f7d5ac946edf44f59ab22fd256f46416" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f7d5ac946edf44f59ab22fd256f46416" title="gltf draco"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">gltf draco</span></span></h3><div class="notion-text notion-block-0bc2105f053e43a98a47b365b353765d">对于可以选取导出格式的项目来说，选取 gltf 格式加 draco 压缩的方式，可以得到较高的压缩比。不过使用 draco 压缩并不是没有代价的，有时候可能会造成模型的外观损坏。同时，解压模型也需要一些的时间。所以，对于小的模型，网络传输较快的，也就没必要上 draco 压缩了。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-f74f65e56c4a49babf237f427dab31d5" data-id="f74f65e56c4a49babf237f427dab31d5"><span><div id="f74f65e56c4a49babf237f427dab31d5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f74f65e56c4a49babf237f427dab31d5" title="自定义格式"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">自定义格式</span></span></h3><div class="notion-text notion-block-70aa8928880343b0b1424d8d32bdd771">对于有能力的开发者，完全可以使用自定义的格式，将模型数据做成二进制的形式进行传输和加载，这样灵活性比较高，而且如果再前端解模型的时候使用 wasm 进行解压，可以保证自己的模型内部格式对外是一个黑盒子。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-fc2948898c89414ca4f8cd607592a9e9" data-id="fc2948898c89414ca4f8cd607592a9e9"><span><div id="fc2948898c89414ca4f8cd607592a9e9" class="notion-header-anchor"></div><a class="notion-hash-link" href="#fc2948898c89414ca4f8cd607592a9e9" title="贴图压缩"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">贴图压缩</span></span></h3><div class="notion-text notion-block-6716c051f9ab4c7b8f93098b0a14bc96">上述描述的模型压缩只针对模型网格数据，不会对贴图进行处理，然而很多时候贴图文件往往大于模型。贴图尺寸也应该根据需要选取，不应该过大，一般最好不要超过 4k，保持 1024 或者 2048 较好。贴图也最好使用 2 的 n 次幂的尺寸。下面介绍如何优化用于应用程序渲染的贴图文件。</div><div class="notion-text notion-block-048a1f85728d49f1990b279de3834eb7">对于常见的贴图一般使用 jpg、png、tga 等格式，但是这部分格式加载完毕后，png/jpg 仍需要全部转码为纹理(texture)才能开始渲染，而具有相同尺寸的贴图纹理 GPU 占用内存大小相同，故压缩后的 png/jpg 对于渲染过程并没有优化。庆幸的是许多设备都有可直接用于渲染的 GPU 压缩纹理(compress texture)格式，压缩纹理可比由 png 直接转换的纹理减少 5 倍或以上的大小。如果直接提供压缩纹理格式，则不需要进行 png 的转码过程且可大大减少纹理内存。但由于 GPU 芯片提供商太多，设备的压缩纹理格式多种多样（例如安卓设备常用格式是 ETC1/ETC2，苹果设备是 PVRTC…）</div><div class="notion-text notion-block-3ffd7565c83242f6a8b9b6f7d67a205b">2019 五月份，Binomial 公司和 google 联合推出了 Basis Universal 压缩 GPU 纹理技术，Basis Universal 支持多种常用的压缩纹理格式，将 png 转换为 basis 文件后，大小与 jpg 格式差不多，但在 GPU 上比 png/jpg 小 6-8 倍。在保持 GPU 性能效率的同时，提升 Web、桌面端和移动应用程序中图像传输的性能。此版本填补了图形压缩生态系统中的一个关键技术空白，同时也补充了 Draco 几何压缩的部分早期工作。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1dd93d5503aa46f38ad207d9755d1311"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F61916085-6fec-4e15-be23-8598b739504c%2FXxMgUQ369nZ78qZNrLK38z90XCmAhbIdTB9R4BWTU7N.png?table=block&amp;id=1dd93d55-03aa-46f3-8ad2-07d9755d1311&amp;t=1dd93d55-03aa-46f3-8ad2-07d9755d1311&amp;width=1060&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-31cf7fb4325542f6864817561fe457b4"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F5c60d660-d5a4-4ec2-8af2-303f5e4496fc%2FUntitled.png?table=block&amp;id=31cf7fb4-3255-42f6-8648-17561fe457b4&amp;t=31cf7fb4-3255-42f6-8648-17561fe457b4&amp;width=733&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-1bf7e22cbf654a0e90a8ebac39c65ae1">另外可以看看这篇腾讯模型和材质压缩的经验分享：<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://isux.tencent.com/articles/isux-optimizing-3d-model.html">如何在页面极速渲染3D模型 - Tencent ISUX Design</a></div><div class="notion-text notion-block-e7c95ba4f5244363a816b89dd7f43c02">对于大型 3D 资源，我们一般会通过在模型设计时进行“减面”来减少模型几何体的大小，但也会带来模型精致度的缺失。如下图所示：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-fe78a7535f6f4633983d6e68bbe4a20e"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2Fa677995e-9c59-4dbf-bdd3-1338f4a2401a%2F467bdvhFeNUViApJtNRLiWhNpuQqTzxbTzm3VyHEa40.png?table=block&amp;id=fe78a753-5f6f-4633-983d-6e68bbe4a20e&amp;t=fe78a753-5f6f-4633-983d-6e68bbe4a20e&amp;width=2522&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-5d9239f4d70141479c77eda713399639">而通过 glTF 配合 Draco 压缩的方式，可以在视觉效果近乎一致的情况下，让3D模型文件成倍缩小。下面具体介绍 glTF 格式及 Draco 压缩工具。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-19eba9d462c945c9827455b9d790c956" data-id="19eba9d462c945c9827455b9d790c956"><span><div id="19eba9d462c945c9827455b9d790c956" class="notion-header-anchor"></div><a class="notion-hash-link" href="#19eba9d462c945c9827455b9d790c956" title="1. 将模型导出为 glTF 格式"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">1. 将模型导出为 glTF 格式</span></span></h4><div class="notion-text notion-block-c10d46eb86894bf1843b48851c0371b0"><b>glTF 介绍</b></div><div class="notion-text notion-block-9bdb2b8ba2a84d968cc05416a5af4344">glTF 称为“ 3D 界的 JPEG”，使用了更优的数据结构，为应用程序实时渲染而生。glTF 有以下几大特点：</div><ul class="notion-list notion-list-disc notion-block-bd957d5de7124f52b44084c34d8ee498"><li>由现有 OpenGL 的维护组织 Khronos 推出，目的就是为了统一用于应用程序渲染的 3D 格式，更适用于基于 OpenGL 的引擎；</li></ul><ul class="notion-list notion-list-disc notion-block-6b74c280fcb044fc8deb84640668ad0e"><li>减少了 3D 格式中除了与渲染无关的冗余信息，最小化 3D 文件资源；</li></ul><ul class="notion-list notion-list-disc notion-block-ea73081984fe4ec18545f83ed4831364"><li>优化了应用程序读取效率和和减少渲染模型的运行时间；</li></ul><ul class="notion-list notion-list-disc notion-block-16ab18fc17eb4cb0bc0c5c83ff19d5c7"><li>支持 3D 模型几何体、材质、动画及场景、摄影机等信息。</li></ul><div class="notion-text notion-block-a782501085a64968bada9be65ed5f969">glTF 导出格式有两种后缀格式可供选择：.gltf 和 .glb：</div><ul class="notion-list notion-list-disc notion-block-54123d95bfff49a49c2c242c5d421b65"><li>.gltf 文件导出时一般会输出两种文件类型，一是 .bin 文件，以二进制流的方式存储顶点坐标、顶点法线坐标和贴图纹理坐标、贴图信息等模型基本数据信息；二是 .gltf 文件，本质是 json 文件，记录对bin文件中模型顶点基本数据的索引、材质索引等信息，方便编辑，可读性较好；</li></ul><ul class="notion-list notion-list-disc notion-block-919d461175354bb689142f0b70c37350"><li>.glb 文件格式只导出一个 .glb 文件，将所有数据都输出为二进制流，通常来说会更小一点，若不关心模型内的具体数据可直接选择此类型。</li></ul><div class="notion-text notion-block-eacaf21b600648ffa5f90f29e312ef16"><b>glTF 转换</b></div><div class="notion-text notion-block-4feb68d0d6d947f5808b7c8e9c9c3c2b">目前有些建模工具还不具备导出 glTF 格式功能，可以输出 FBX / Collada 格式后通过以下工具进行转换：</div><ul class="notion-list notion-list-disc notion-block-961c8054ea844db9b0981222a7d996e8"><li>FBX 转 glTF</li></ul><div class="notion-text notion-block-efcdfcd79fca4c86b6c05cbf1b68cb91">a. Facebook 推出的 FBX2glTF 命令行工具，可直接从github 官网下载 release 版本；</div><div class="notion-text notion-block-d0d662020283449aa9d8b00ca1a4cb2f">b. 通过 Paint 3D、Substance Painter 等可视化编辑工具进行转换。</div><ul class="notion-list notion-list-disc notion-block-96848a7f001f48f3a2014cf50441f8a7"><li>Collada 转 glTF</li></ul><div class="notion-text notion-block-7f196eb44013446aa6959c64b370c1f8">COLLADA2GLTF 命令行工具，可转换 .dae 格式的文件，从 GitHub 官网直接下载 release 版本，解压后在命令行进入目录即可调用。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-9a08c482a1f94d92983b63cc746acadc" data-id="9a08c482a1f94d92983b63cc746acadc"><span><div id="9a08c482a1f94d92983b63cc746acadc" class="notion-header-anchor"></div><a class="notion-hash-link" href="#9a08c482a1f94d92983b63cc746acadc" title="2. 通过 Draco 进行压缩"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">2. 通过 Draco 进行压缩</span></span></h4><div class="notion-text notion-block-ec91bf13573b49d78c312e5cfd32395f"><b>Draco 及 gltf-pipeline 介绍</b></div><div class="notion-text notion-block-f97841688eac4953b738a0491743997d">Draco 是 Google 推出的一个用于 3D 模型压缩和解压缩的工具库，上述介绍的 FBX2glTF 及 COLLADA2GLTF 工具也嵌入了 Draco 压缩功能，除此之外，glTF 资源可通过基于 Draco 开发的命令行工具 gltf-pipeline 进行编码压缩，gltf-pipeline 可通过 npm 的方式安装使用。使用方法如下：</div><div class="notion-text notion-block-f51cea0d0ae343ab905d04fa9669c1ce"><b>Draco 压缩分析</b></div><div class="notion-text notion-block-156548af564740bb9efee91d2b86ecfe">通过 Draco 进行压缩基本上是有损的，有两点表现：</div><ul class="notion-list notion-list-disc notion-block-45ef5ca097244b17a218868c3ef00344"><li>Draco 通过 Edge breaker 3D 压缩算法改变了模型的网格数据的索引方法，缺少了原来的网格顺序；</li></ul><ul class="notion-list notion-list-disc notion-block-dee0a96d6bb84635bd85998b92f478e6"><li>Draco 通过减少顶点坐标、顶点纹理坐标等信息的位数，以减少数据的存储量。</li></ul><div class="notion-text notion-block-2fdef817508d49b88e2e8a2f312eaebd">但在 gltf-pipeline 或其他压缩工具中，压缩程度可通过设置参数进行调整，如下所示：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-d36fb2e2ec6c4294ab029729878a1e8e"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2Fb94d98e5-1fb1-4512-bfab-382b423897c3%2FWL7Zg7umuvjHKLKCLqBM7hbaMvOij2UexiCtuPMmCja.png?table=block&amp;id=d36fb2e2-ec6c-4294-ab02-9729878a1e8e&amp;t=d36fb2e2-ec6c-4294-ab02-9729878a1e8e&amp;width=1262&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-0e8b4ba681ba4fcabbb4434d79e1e30b">当 --draco.compressionLevel 为0时，将保留原来的网格顺序，网格数据索引的压缩力度最小，--draco.quantizeXXXBits 可控制坐标等基本数据值的位数，位数越少压缩力度越大。由于一个三角形网格对应多个顶点坐标、顶点法线坐标、颜色坐标等数据，一般来说 --draco.quantizeXXXBits 对文件的大小影响会更大。</div><div class="notion-text notion-block-ce5d6b6314c446eb945190384e05447c">若不设置参数，gltf-pipeline 会直接以默认值压缩。</div><div class="notion-text notion-block-b7fa8e21f1a0495ebfb2bc5d72b909db">虽说 Draco 是有损的，但相对于直接为模型减面来说，采用 Draco 压缩方法视觉偏差会小很多。</div><div class="notion-text notion-block-93bf8aea5dc048c6919e9a5019fde540">压缩后的 glTF 模型需要通过在应用中嵌入 Draco 解码工具包，主要是对 edge breaker 算法部分进行解码，解码时间一般比编码时间少，但必须考量模型与工具包的大小对比。例如 ThreeJS 提供了 draco_decoder 模块进行解码，draco_decoder 约600KB，若模型资源文件比工具包还小，就没有必要再引入 Draco 压缩了。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-a99b61e2d0da4260ac770b3dd56225ba" data-id="a99b61e2d0da4260ac770b3dd56225ba"><span><div id="a99b61e2d0da4260ac770b3dd56225ba" class="notion-header-anchor"></div><a class="notion-hash-link" href="#a99b61e2d0da4260ac770b3dd56225ba" title="3. 效果测试"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">3. 效果测试</span></span></h4><div class="notion-text notion-block-217affbfa9864498aa59804f0da6a6e1">我们以太空鹅模型为例，只加载模型几何体，不带入材质属性，通过ThreeJS 分别加载 FBX / glTF / 压缩后的glTF 的格式，第三种格式以默认参数压缩。测试效果对比如下：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-8a00ae4f5e464cb5b072cd6d0fa92c77"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F260f34ce-67c4-407c-8b99-dda5b2b93254%2FEm34Yje31ln8dnA0pqIGnv1ob3tgzdGWb0bo92psAv6.png?table=block&amp;id=8a00ae4f-5e46-4cb5-b072-cd6d0fa92c77&amp;t=8a00ae4f-5e46-4cb5-b072-cd6d0fa92c77&amp;width=2030&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-e8fbd9b0c3b2467e9050195cb5fc649e"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F0e83882b-6c88-4211-af2a-49dcf91474b2%2FpoPDjj5kynEXjoV3g3HRDyUSMNwse8mmsbCbhmpLwoQ.png?table=block&amp;id=e8fbd9b0-c3b2-467e-9050-195cb5fc649e&amp;t=e8fbd9b0-c3b2-467e-9050-195cb5fc649e&amp;width=2082&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-24f77e741d444cdba38d04b1223f22a9">从图中可以看出，文件从 FBX 转换为 glTF 后大小差异不大，但是渲染速度有了明显提升。</div><div class="notion-text notion-block-f7ba2be00a7d4c41921ede7b421f9731">另外经过压缩的 glTF 文件仅为正常 FBX 和 glTF 文件的1/10左右，而在视觉上三者几何体结构没有明显的差异，压缩后的 glTF 开启了 worker 线程做 Draco 解码，多了一小部分模型解码时间。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-eafd7189496d49d2bd66b28433a05267" data-id="eafd7189496d49d2bd66b28433a05267"><span><div id="eafd7189496d49d2bd66b28433a05267" class="notion-header-anchor"></div><a class="notion-hash-link" href="#eafd7189496d49d2bd66b28433a05267" title="模型贴图优化"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>模型贴图优化</b></span></span></h4><div class="notion-row"><a target="_blank" rel="noopener noreferrer" class="notion-bookmark notion-block-9a909958cb6a483998e3735bddd38907" href="https://zhuanlan.zhihu.com/p/351712352"><div><div class="notion-bookmark-title">【纹理优化（三）】善用Mipmap</div><div class="notion-bookmark-description">【纹理优化（三）】善用Mipmap 声明：这里是个人学习笔记，仅作学习交流使用，学习过程中参考和借鉴了大量他人的学习成果（只要我搜得到的）甚至有些连图带文字都是直接复制的（也懒得改） 有写的不好或者不对的地…</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fstatic.zhihu.com%2Fheifetz%2Fassets%2Fapple-touch-icon-152.81060cab.png?table=block&amp;id=9a909958-cb6a-4839-98e3-735bddd38907&amp;t=9a909958-cb6a-4839-98e3-735bddd38907" alt="【纹理优化（三）】善用Mipmap" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://zhuanlan.zhihu.com/p/351712352</div></div></div><div class="notion-bookmark-image"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fpic1.zhimg.com%2Fv2-ef5c0f6a1694e55a897da109594cd000_720w.jpg%3Fsource%3D172ae18b?table=block&amp;id=9a909958-cb6a-4839-98e3-735bddd38907&amp;t=9a909958-cb6a-4839-98e3-735bddd38907" alt="【纹理优化（三）】善用Mipmap" loading="lazy" decoding="async"/></div></a></div><div class="notion-row"><a target="_blank" rel="noopener noreferrer" class="notion-bookmark notion-block-cfe22a1c882e44ec9fc08feb9e33d392" href="https://zhuanlan.zhihu.com/p/367345699"><div><div class="notion-bookmark-title">WebGL 中的图片解码优化</div><div class="notion-bookmark-description">作者 - Oasis 团队-月木虽然 WebGL 支持 压缩纹理，上传 GPU 不存在解码耗时的问题，但日常应用中还是会用到 png/jpg/webp 等压缩过的图片格式。这些格式在 WebGL 中渲染需要转换成位图，即每个像素使用 RGB 或 RG…</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fstatic.zhihu.com%2Fheifetz%2Fassets%2Fapple-touch-icon-152.81060cab.png?table=block&amp;id=cfe22a1c-882e-44ec-9fc0-8feb9e33d392&amp;t=cfe22a1c-882e-44ec-9fc0-8feb9e33d392" alt="WebGL 中的图片解码优化" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://zhuanlan.zhihu.com/p/367345699</div></div></div><div class="notion-bookmark-image"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fpica.zhimg.com%2Fv2-a737fe4fcae94e1389a8e0834649480d_720w.jpg%3Fsource%3D172ae18b?table=block&amp;id=cfe22a1c-882e-44ec-9fc0-8feb9e33d392&amp;t=cfe22a1c-882e-44ec-9fc0-8feb9e33d392" alt="WebGL 中的图片解码优化" loading="lazy" decoding="async"/></div></a></div><div class="notion-text notion-block-c82f7eef95d84f7dad7b3bb70afefca1">上述描述的模型压缩只针对模型网格数据，不会对 glTF 文件里的贴图进行处理。然而很多时候贴图文件往往大于模型。此时则需要将模型和贴图分开进行处理（建模时分开输出一个打好 UVtag 纹理坐标的“白模”和需要用到的纹理贴图）。下面介绍如何优化用于应用程序渲染的贴图文件。</div><div class="notion-text notion-block-f2a903a66a7b44c69a39bbfd7fb0c57a"><b>1. 贴图加载过程分析</b></div><div class="notion-text notion-block-a16d16cddaae4add99e6ed31a8a3b379">以一个基于物理引擎渲染的电视机 Demo 模型为例，一般会输出几种尺寸较大的贴图文件：颜色贴图，法线贴图，金属粗糙贴图，如下图例子所示：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-802f8cc9e2cd4852a6c72b875a57f4b8"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2Fc3f10e5f-ca22-4fdb-9943-9fa336dded1a%2F5mjTbA3LzpvHvwQ7MvRcWcxNa5a1blys3Vx8jIWswHb.png?table=block&amp;id=802f8cc9-e2cd-4852-a6c7-2b875a57f4b8&amp;t=802f8cc9-e2cd-4852-a6c7-2b875a57f4b8&amp;width=1564&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-8b7c9645e06c447bb4419d46a009c382">输出贴图一般为 png 格式，许多同学会通过压缩 png 或者将 png 转成 jpg 格式减少纹理大小，其实这种处理方式只优化了图片加载速度，加载完毕后，png/jpg 仍需要全部转码为纹理(texture)才能开始渲染，而具有相同尺寸的贴图纹理 GPU 占用内存大小相同，故压缩后的 png/jpg 对于渲染过程并没有优化。</div><div class="notion-text notion-block-edc3483beaa8447586fa9d6b02a797b9">庆幸的是许多设备都有可直接用于渲染的 GPU 压缩纹理(compress texture)格式，压缩纹理可比由 png 直接转换的纹理减少5倍或以上的大小。如果直接提供压缩纹理格式，则不需要进行 png 的转码过程且可大大减少纹理内存。如下图方案2所示：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-95ac3ce313bf454f993d7e1ead5978f9"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2Fe2a23317-4e01-405e-8d1b-3b2c277d5ad8%2FRxPSM1pSFhHV8etR5wJANGCKHBQ0FilHgrRfDX1JDmQ.png?table=block&amp;id=95ac3ce3-13bf-454f-993d-7e1ead5978f9&amp;t=95ac3ce3-13bf-454f-993d-7e1ead5978f9&amp;width=2080&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-764aa2b0dcff4fd1aecb45fc1892cbdb">但由于 GPU 芯片提供商太多，设备的压缩纹理格式多种多样（例如安卓设备常用格式是 ETC1/ETC2，苹果设备是 PVRTC…），手动输出多种格式代价大，导致方案2较难落地。</div><div class="notion-text notion-block-1bd87268cadc4979bd663e5f2d86d467"><b>2. Basis Universal 压缩</b></div><div class="notion-text notion-block-b2ee2ad6eeed434eb41d5239e11684ab">转折点在于今年五月份，Binomial 公司推出了 Basis Universal 压缩 GPU 纹理技术，Basis Universal 支持多种常用的压缩纹理格式，将 png 转换为 basis 文件后，大小与 jpg 格式差不多，但在 GPU 上比 png/jpg 小6-8倍。</div><div class="notion-text notion-block-3f65a8cb042e48a4b1a24199437b3a54">应用程序加载 basis 文件后，可通过 basis 转码器快速转换成适用于设备的压缩纹理格式。如下图(图片来自Google Blog)所示：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-57da2ff708bf4d1a8ba695a03d38f6fc"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2Fd778fb86-e986-4997-a218-0b2833cd344b%2FXxMgUQ369nZ78qZNrLK38z90XCmAhbIdTB9R4BWTU7N.png?table=block&amp;id=57da2ff7-08bf-4d1a-8ba6-95a03d38f6fc&amp;t=57da2ff7-08bf-4d1a-8ba6-95a03d38f6fc&amp;width=1060&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-fb92599602c448489e8fd0e14cb8f535">Basis 用法也比较简单，可通过 basisu 命令行工具压缩 png，直接从github 官网下载Release版本或者通过 CMake 编译源码，以 Mac 系统为例（Windows 系统将命令改为 basis.exe），列举几种常用用法：</div><div class="notion-text notion-block-2f965d1c36a04c84afc4a317a1035ca6">生成的 .basis 文件需要在程序中通过转码器转成设备的压缩纹理格式，例如在ThreeJS 中可通过 basisTextureLoader 转换，具体用法可查阅ThreeJS 官网。</div><div class="notion-text notion-block-a04d15040ab3405b9a2454f5463e5682"><b>3. 效果测试</b></div><div class="notion-text notion-block-2bf593118f22436aa036c68ba92cf932">为了数据更加明显，我们在Mac Chrome 浏览器performance模式下，针对同一个电视机模型利用 ThreeJS 各自加载了 4096 x 4096 大小的颜色贴图、法线贴图、金属与粗糙贴图，对比如下：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0d77c3d261ab4510a33245f1fdb97e08"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F6eeb8441-d241-48d8-942a-e515e4293144%2FWG68CTSKTXAtpyz5b2kRNkt8mxNxIa32xAscGrUH8I0.png?table=block&amp;id=0d77c3d2-61ab-4510-a332-45f1fdb97e08&amp;t=0d77c3d2-61ab-4510-a332-45f1fdb97e08&amp;width=2094&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-59424c810a694a5c9be091e19a32e860"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2Fabb68b4b-fc5a-479d-aaeb-1c84aeb11f28%2FBxJNMq5AG0IueVlREP2RUTaS0DwHCvxwPpPd6dJaPOO.png?table=block&amp;id=59424c81-0a69-4a5c-9be0-91e19a32e860&amp;t=59424c81-0a69-4a5c-9be0-91e19a32e860&amp;width=2092&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-94f8e8f68acc439090cdec2284bc8e89">由上图使用 basis 贴图资源文件大小比 png 减少了11倍以上，同时主线程的脚本时间和绘制时间花销也小于 png/jpg 贴图。</div><div class="notion-text notion-block-cca24f2df767405e9cd74a81672c2076">需要注意的是，同样由于不同的压缩纹理格式不同，在 basis 文件一致的情况下，不同设备的渲染表现可能会出现不一致，需要进行多端测试，且目前部分格式不支持 alpha 通道，带半透明的颜色贴图若不生效可考虑单独拆出 alpha 贴图。</div><div class="notion-text notion-block-3d162c8bdfc94030ac959badccaabd4f">除了基于 webGL 的 H5，glTF 与 Basis 亦可用于其它基于 OpenGL 渲染的应用程序。值得期待的是，目前 Google 与 Binomial 公司正在推进 Basis Universal 与 glTF 3D 传输标准的合作，或许在不久的将来就可以迎来结合了 basis 贴图的 glTF 格式，不需要做另外的处理可以直接导入模型到应用程序中。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-55e56ae926d149a3934148a457758352" data-id="55e56ae926d149a3934148a457758352"><span><div id="55e56ae926d149a3934148a457758352" class="notion-header-anchor"></div><a class="notion-hash-link" href="#55e56ae926d149a3934148a457758352" title="分包流式加载"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">分包流式加载</span></span></h3><div class="notion-text notion-block-1c5eb428e485460294f1c5a4ba27aeed">对于大的场景，特别是 BIM 或者 GIS 场景，如果把所有模型全部加装完成了再展示，可能会让用户等待时间过久。所以，我们可以采取分包流式加载的方式，每个包加载一部分的模型，加载解析完成后即丢到渲染主线程中进行渲染。这种方式大家之前经常看到的是百度地图的数据的分片加载。</div><div class="notion-text notion-block-4be7c13ee07f46bd97f8db75c9b521ea">想要达到这样的效果，我们一般会使用 worker 进行数据的加载和解析处理，这样保证了加载的线程和主线程是隔离的，不会产生加载解析模型过程中产生的 UI 卡顿。同时，如果我们想要达到并行加载和解析的效果，可以开多个 worker 进行多线程的同时加载解析工作。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-79d436eb74454567af708989663a79c2" data-id="79d436eb74454567af708989663a79c2"><span><div id="79d436eb74454567af708989663a79c2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#79d436eb74454567af708989663a79c2" title="使用缓存"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">使用缓存</span></span></h3><div class="notion-text notion-block-fc8550651c844b0087887b7660a13b42">优化完了首次加载的耗时操作，我们可以继续优化加载，采用各种缓存技术是也常见的加载优化方法，其中包括使用 cdn 文件服务，浏览器文件缓存、indexeddb 前端缓存等。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-26448d94796644b6ab5f416185e2e79a" data-id="26448d94796644b6ab5f416185e2e79a"><span><div id="26448d94796644b6ab5f416185e2e79a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#26448d94796644b6ab5f416185e2e79a" title="使用 CDN"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">使用 CDN</span></span></h3><div class="notion-text notion-block-90ef1e109c9c4d3b8ba17871ba5d6f61">对于加载来说，除了文件本身大小因素，我们不得不考虑的一点就是文件所放置的服务器带宽问题。如果模型场景都放在一台服务器上，加载过程中必定会对服务器的带宽带来一定压力。所以使用 cdn 做静态资源的文件服务，变得顺理成章。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-6b1c495b91224fbdaca102a4fde57de0" data-id="6b1c495b91224fbdaca102a4fde57de0"><span><div id="6b1c495b91224fbdaca102a4fde57de0" class="notion-header-anchor"></div><a class="notion-hash-link" href="#6b1c495b91224fbdaca102a4fde57de0" title="使用 indexedDB"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">使用 indexedDB</span></span></h3><div class="notion-text notion-block-98f3b4d45d444afeba06bb2893c5d18c">因为模型数据变化其实不大，所以再首次加载的过程中，我们可以使用 indexedDB 做前端的数据缓存。indexedDB 具有良好的查询性能，超大的存储空间（理论上磁盘剩余空间的一半左右），以及支持二进制存储等优良特性，比较适合做前端模型以及贴图数据的缓存。</div><div class="notion-text notion-block-22403ab7d5c04a5699af37f1c4b54172">在使用了 indexeddb 做前端缓存之后，可以做到首次加载之后，对大场景的数据进行秒级加载的超高性能 ，大大提升了用户频繁打开大场景的操作体验。</div><div class="notion-text notion-block-46ed4a2bf12140e293c013d79c0322e6">因此，有些 Webgl 引擎(例如 Babylon.js )已经在引擎级别对 indexedDB 缓存做了支持。开发者只需要简单配置，即可打开缓存，优化加载体验。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-87a0c7febd91442ca2e97525dce6348d" data-id="87a0c7febd91442ca2e97525dce6348d"><span><div id="87a0c7febd91442ca2e97525dce6348d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#87a0c7febd91442ca2e97525dce6348d" title="2. 渲染帧率优化"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">2. 渲染帧率优化</span></span></h3><div class="notion-text notion-block-7c2ef70f61ba443eb9002517920d12bf">在加载优化完成之后，我们继续聊聊如何进行大场景的渲染优化。如果主要性能瓶颈在 GPU 渲染部分，那么我们就需要仔细看看到底是什么造成的渲染瓶颈。我们可从以下方案入手，看看是否已经使用相关优化手段。总的来说，目的是尽量减少 drawcall（opengl state 切换带来的性能损耗），减少向 GPU 提交的数据量（带宽压力）。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-574f070d668e434fbc4f81aaad307956" data-id="574f070d668e434fbc4f81aaad307956"><span><div id="574f070d668e434fbc4f81aaad307956" class="notion-header-anchor"></div><a class="notion-hash-link" href="#574f070d668e434fbc4f81aaad307956" title="各种剔除 Culling"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b><b>各种剔除 Culling</b></b></span></span></h4><div class="notion-text notion-block-29d71eb00eb147ad9004222510541af5">为了达到尽量减少 DrawCall 的目的，我们应当尽量的做好剔除工作，将最少的数据提交给 GPU 进行渲染。一般常见的剔除方式有视椎体剔除，背面剔除、遮挡剔除等。</div><div class="notion-text notion-block-b499e0073c754f4290faabdfb7be94b0"><b>视椎体剔除（Frustum Culling）</b>：一般是指只有在视椎体内的物体才能被渲染出来，不在视椎体内的物体将被剔除不作渲染。这也比较符合我们一般的视觉逻辑，不在可见范围内的物体，渲染了也看不见，纯粹属于性能浪费。这部分的剔除，大部分的引擎都已经自带了，而且都是默认开启的。如果需要自己实现，算法也比较简单，一般是遍历视椎体的 6 个面，算出物体的中心到面的最小距离（带正负方向的）与包围球的半径做比较，如果小于半径，就表示在外面。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0b74d6df9ca9407cb24466d595269fea"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:335px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F1b4db544-3fdd-4dab-8f80-f68453647ee6%2Fv2-cefca2a7203607545a0f91a096ea9f73_720w.webp?table=block&amp;id=0b74d6df-9ca9-407c-b244-66d595269fea&amp;t=0b74d6df-9ca9-407c-b244-66d595269fea&amp;width=335&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-bcbb3fe402f841779ac9502e83a3abbc"><b>背面渲染剔除(Backface Culling)</b> ：一般来讲，渲染引擎大多会开启背面剔除。原生 webgl 中使用 <code class="notion-inline-code">gl.enable(gl.CULL_FACE);</code> 来开启背面剔除。在 threejs 中可以使用 material 的 side 属性指定 front 进行单面渲染。</div><div class="notion-text notion-block-2c23f3f0a42d41528bb4f74afd8581fc"><b>遮挡剔除(Occlusion Culling)</b> ：遮挡剔除是指在相机剔除后，在视野范围内仍然有许多物体直接有遮挡关系的，不需要进行渲染，虽然 gpu 有深度测试，会将有遮挡的物体进行剔除，但是我们仍然希望在提交 GPU 之前对遮挡关系进行判断，提前剔除掉一些东西，减少渲染压力。</div><div class="notion-text notion-block-b46fd366f3d14c978929cebc62eed97b">遮挡剔除相关的文章：</div><div class="notion-text notion-block-efb11a550da342279c3166f79b4e8700"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch29.html">https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch29.html</a></div><div class="notion-text notion-block-d218b75fdaf547fc8c4045e04e67b676"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter06.html">https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter06.html</a></div><div class="notion-text notion-block-f9d60179d88b4f329e61da0dbaa41d29">这篇文章也非常好 里面描述了如何用八叉树和 zbuffer 配合做剔除</div><div class="notion-text notion-block-eb9710e14daa46c3820bab7272082d7c"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://www.gamasutra.com/view/feature/131801/occlusion_culling_algorithms.php">https://www.gamasutra.com/view/feature/131801/occlusion_culling_algorithms.php</a></div><div class="notion-text notion-block-17940181820b48ed8c7bd7ebe23d1441">webgl2 OcclusionQuery 遮挡查询</div><div class="notion-text notion-block-d9376957af97483ba75622ecfb1757f1"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/tsherif/webgl2examples/blob/master/occlusion.html">https://github.com/tsherif/webgl2examples/blob/master/occlusion.html</a></div><figure class="notion-asset-wrapper notion-asset-wrapper-video notion-block-0f627d2fbc5143239c006685e6e79153"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:708px;max-width:100%;flex-direction:column;height:320px"><video playsinline="" controls="" preload="metadata" src="https://notion.so/signed/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F392e73c4-2c93-496d-8306-bc98f3cf3d12%2Fca486e8a-234d-11eb-9ba8-86b142f84435.mp4?table=block&amp;id=0f627d2f-bc51-4323-9c00-6685e6e79153" title="video"></video></div></figure><div class="notion-blank notion-block-13c33201ff9f4c8d94dcd96bbfc4ba30"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-c8ed1f5d348c42158a7f11cf7bf616c5" data-id="c8ed1f5d348c42158a7f11cf7bf616c5"><span><div id="c8ed1f5d348c42158a7f11cf7bf616c5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#c8ed1f5d348c42158a7f11cf7bf616c5" title="合批 batch"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b><b>合批 batch</b></b></span></span></h4><div class="notion-text notion-block-acdd21ddcc974fd8aa68ecb6aed921ba">除了剔除以外，合批次提交也能提高渲染性能，原因也非常的简单，就是合批后提交 GPU 的 DrawCall 减少了。对于合批，我们应该遵循以下原则。</div><div class="notion-text notion-block-95ba385922fe4a02b9e1ab638053db72">首先是材质相同的进行 batch，材质不同的无法进行 batch。一个 batch 其实就是一个 drawcall，对应的其实一种材质，不同种材质效果需要使用不同的 shader 实现所以无法实现合批展示。</div><div class="notion-text notion-block-d41e674f9ca64668bd6dc2ae57a275ea">其次 batch 的定点格式限制问题。因为 webgl 的 index 数据使用的 short 类型，所以最好不要超过 65535 个顶点索引。但是，由于 webgl 的扩展 <code class="notion-inline-code">OES_element_index_uint</code> 已经有了非常良好的兼容性（99%兼容），所以其实你已经可以使用超过 65535 个顶点 index。</div><div class="notion-text notion-block-6e633d3ae38041d797ccdc28dff79884"><b>stackoverflow 关于顶点限制的讨论</b>：<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://stackoverflow.com/questions/4998278/is-there-a-limit-of-vertices-in-webgl">https://stackoverflow.com/questions/4998278/is-there-a-limit-of-vertices-in-webgl</a></div><div class="notion-text notion-block-26b3d8df5c55466aaf9cc53959a9f9dc"><b>如果你使用的 threejs，可以选择使用 geometry 的 merge 方法在前端进行合并 batch，或者手工进行顶点 loop 循环合并顶点，其实原理差不多。</b>

<b><b>instance</b></b></div><div class="notion-text notion-block-3db39c7aae76428f915c37631b3f4a6f">instance 实例化其实也是渲染优化中常用的技术，特别适合那些外观一致大量重复的渲染。比如小树组成的森林，一个发布会场景中的大量椅子等等。在 instance 渲染的时候，我们不需要传入大量的顶点数据（只需要传入每个 instance 的 matrix 数据），而是共享一份顶点数据，这样可以大大降低显存的使用率，降低显存带宽。</div><div class="notion-text notion-block-d736a3f285a441f29c173e767660672b">在 webgl 中我们使用的是<code class="notion-inline-code">ANGLE_instanced_arrays</code>扩展来实现 instance 渲染。但是值得注意的是，instance 的使用所带来一些额外处理，比如单个物体的选择操作等问题。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-f8a65604b0ef43a99e1939ceaaf3fe16" data-id="f8a65604b0ef43a99e1939ceaaf3fe16"><span><div id="f8a65604b0ef43a99e1939ceaaf3fe16" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f8a65604b0ef43a99e1939ceaaf3fe16" title="LOD"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>LOD</b></span></span></h4><div class="notion-text notion-block-eb8a01d052ca42efa0c11fd4d3bfb7f7">LOD（Level of Details）技术指的是将场景中的模型按不同精度分为 N 套,按照模型与相机的距离远近，动态切换模型的精度，距离相机较近的模型采用精细模型展示，而距离相机较远的模型使用较为粗糙的模型进行展示。对于大的场景，使用 LOD 技术也是一个有效提升帧率的手段，可以有效减少整个场景中的渲染三角面数。</div><div class="notion-text notion-block-6f0fc43465f24ad6a48a336a3558f67a">使用 LOD 技术一个非常重要的问题是如何生成多套不同精度的模型。一般来说有一下几个方案：</div><div class="notion-text notion-block-50417ac180cf42e1996d1fd9f53380cb">一是在建模软件中，使用减面工具，直接生成多套模型，这部分一般是美术建模人员来完成。虽然程序少了很多事儿，但是通用性较差，自动化程度较低。</div><div class="notion-text notion-block-cae074081bfc45a2a9e2e377ebc56bc3">二是选用开源的减面方案进行程序减面。一般来说可以选取一些 QEM 算法减面，使用程序进行减面。但是一般这类算法都有一些局限，比如贴图问题，破面问题。需要程序不断调试，找到比较合适的参数。</div><a target="_blank" rel="noopener noreferrer" href="https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification" class="notion-external notion-external-block notion-row notion-block-5408bd7f7bb4428d9939645eae7acb56"><div class="notion-external-image"><svg viewBox="0 0 260 260"><g><path d="M128.00106,0 C57.3172926,0 0,57.3066942 0,128.00106 C0,184.555281 36.6761997,232.535542 87.534937,249.460899 C93.9320223,250.645779 96.280588,246.684165 96.280588,243.303333 C96.280588,240.251045 96.1618878,230.167899 96.106777,219.472176 C60.4967585,227.215235 52.9826207,204.369712 52.9826207,204.369712 C47.1599584,189.574598 38.770408,185.640538 38.770408,185.640538 C27.1568785,177.696113 39.6458206,177.859325 39.6458206,177.859325 C52.4993419,178.762293 59.267365,191.04987 59.267365,191.04987 C70.6837675,210.618423 89.2115753,204.961093 96.5158685,201.690482 C97.6647155,193.417512 100.981959,187.77078 104.642583,184.574357 C76.211799,181.33766 46.324819,170.362144 46.324819,121.315702 C46.324819,107.340889 51.3250588,95.9223682 59.5132437,86.9583937 C58.1842268,83.7344152 53.8029229,70.715562 60.7532354,53.0843636 C60.7532354,53.0843636 71.5019501,49.6441813 95.9626412,66.2049595 C106.172967,63.368876 117.123047,61.9465949 128.00106,61.8978432 C138.879073,61.9465949 149.837632,63.368876 160.067033,66.2049595 C184.49805,49.6441813 195.231926,53.0843636 195.231926,53.0843636 C202.199197,70.715562 197.815773,83.7344152 196.486756,86.9583937 C204.694018,95.9223682 209.660343,107.340889 209.660343,121.315702 C209.660343,170.478725 179.716133,181.303747 151.213281,184.472614 C155.80443,188.444828 159.895342,196.234518 159.895342,208.176593 C159.895342,225.303317 159.746968,239.087361 159.746968,243.303333 C159.746968,246.709601 162.05102,250.70089 168.53925,249.443941 C219.370432,232.499507 256,184.536204 256,128.00106 C256,57.3066942 198.691187,0 128.00106,0 Z M47.9405593,182.340212 C47.6586465,182.976105 46.6581745,183.166873 45.7467277,182.730227 C44.8183235,182.312656 44.2968914,181.445722 44.5978808,180.80771 C44.8734344,180.152739 45.876026,179.97045 46.8023103,180.409216 C47.7328342,180.826786 48.2627451,181.702199 47.9405593,182.340212 Z M54.2367892,187.958254 C53.6263318,188.524199 52.4329723,188.261363 51.6232682,187.366874 C50.7860088,186.474504 50.6291553,185.281144 51.2480912,184.70672 C51.8776254,184.140775 53.0349512,184.405731 53.8743302,185.298101 C54.7115892,186.201069 54.8748019,187.38595 54.2367892,187.958254 Z M58.5562413,195.146347 C57.7719732,195.691096 56.4895886,195.180261 55.6968417,194.042013 C54.9125733,192.903764 54.9125733,191.538713 55.713799,190.991845 C56.5086651,190.444977 57.7719732,190.936735 58.5753181,192.066505 C59.3574669,193.22383 59.3574669,194.58888 58.5562413,195.146347 Z M65.8613592,203.471174 C65.1597571,204.244846 63.6654083,204.03712 62.5716717,202.981538 C61.4524999,201.94927 61.1409122,200.484596 61.8446341,199.710926 C62.5547146,198.935137 64.0575422,199.15346 65.1597571,200.200564 C66.2704506,201.230712 66.6095936,202.705984 65.8613592,203.471174 Z M75.3025151,206.281542 C74.9930474,207.284134 73.553809,207.739857 72.1039724,207.313809 C70.6562556,206.875043 69.7087748,205.700761 70.0012857,204.687571 C70.302275,203.678621 71.7478721,203.20382 73.2083069,203.659543 C74.6539041,204.09619 75.6035048,205.261994 75.3025151,206.281542 Z M86.046947,207.473627 C86.0829806,208.529209 84.8535871,209.404622 83.3316829,209.4237 C81.8013,209.457614 80.563428,208.603398 80.5464708,207.564772 C80.5464708,206.498591 81.7483088,205.631657 83.2786917,205.606221 C84.8005962,205.576546 86.046947,206.424403 86.046947,207.473627 Z M96.6021471,207.069023 C96.7844366,208.099171 95.7267341,209.156872 94.215428,209.438785 C92.7295577,209.710099 91.3539086,209.074206 91.1652603,208.052538 C90.9808515,206.996955 92.0576306,205.939253 93.5413813,205.66582 C95.054807,205.402984 96.4092596,206.021919 96.6021471,207.069023 Z" fill="#161614"></path></g></svg></div><div class="notion-external-description"><div class="notion-external-title">Fast-Quadric-Mesh-Simplification</div><div class="notion-external-subtitle"><span>sp4cerat</span><span> • </span><span>Updated <!-- -->Mar 28, 2024</span></div></div></a><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-d6bd93c539ea45afbe6f1a14d5e8fd2d"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:640px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2Fa7124ac4-3a4d-44b8-ad12-564c9cfa0456%2Fv2-33215b7289f720a4d17cc080fda2bc1b_b.webp?table=block&amp;id=d6bd93c5-39ea-45af-be6f-1a14d5e8fd2d&amp;t=d6bd93c5-39ea-45af-be6f-1a14d5e8fd2d&amp;width=640&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-fa3c4d13cdeb49808c00a5916e87fafc">三就是使用商业的 sdk 进行减面。一般常用的是 simplygon、instlod 等都是比较成熟的商用减面工具，也广泛的使用在游戏等行业。</div><div class="notion-text notion-block-9703b115077e43799a0bc0095303cccc">另外，很多游戏引擎也自带一些减面工具，比如 u3d、ue4 等都支持 LOD 减面。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-df30ae6ea5ab4e919e21cb464b9ef540" data-id="df30ae6ea5ab4e919e21cb464b9ef540"><span><div id="df30ae6ea5ab4e919e21cb464b9ef540" class="notion-header-anchor"></div><a class="notion-hash-link" href="#df30ae6ea5ab4e919e21cb464b9ef540" title="3. 内存管理优化"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b><b>3. 内存管理优化</b></b></span></span></h3><div class="notion-text notion-block-6dc930aedb074427b09fef307b0e27ba">对于重型 webgl 应用，特别是 BIM、GIS 场景，有时候，我们需要加载多个大型模型，例如 cesium 还需要能够支持整个地球数据的加载。这就需要我们对内存有着较好的控制。</div><div class="notion-text notion-block-6e6b66eed6bb4e308a0c7366eab8c0ad">一方面，我们需要在不适用对象的时候，及时销毁对象，释放 JS 内存。同时对于模型数据，大量的顶点、法线等等 buffer 数据也是非常占用内存的。我们可以在 js 推送完数据之后，将这部分数据从内存中释放掉，从而降低 JS 的内存压力。对于 v8 引擎来说 32 位的 JS heap 最多能到 3.8G 左右，如果不及时释放内存，很容易内存爆掉，导致浏览器 crash。</div><div class="notion-text notion-block-669c415a413c4f1fab9b6197cf2fcb22">如果使用 threejs 进行渲染，<code class="notion-inline-code">BufferAttribute</code>包含一个<code class="notion-inline-code">onUpload</code>回调函数，在数据推送到 GPU 之后调用。我们可以在回调函数中释放掉 js 中的数据。值得注意的是，这个回调函数只在第一次的时候有用，在 attribute 更新 update 的时候，并不会触发，笔者认为这是个 bug 提交了 pr 给 threejs，不过似乎 threejs 的作者<code class="notion-inline-code">doob</code>并不十分感冒，最终没有 merge 这个 pr。</div><div class="notion-text notion-block-37d62fe012354dbca5d9f58d10b818c4">另一方面，在大场景的渲染下，我们要尽量引导客户使用 64 位的浏览器 chrome 或者 Firefox，这样可以最大限度的使用 js 内存，防止 V8 js Heap 爆了的情况。</div><div class="notion-text notion-block-607249b34c774f1f8ae0860fc9869f96">好消息是，最新的 v8 又加入了指针压缩，将内存的使用量降低了不少。</div><div class="notion-text notion-block-727c8a2c682c4d8f9ab3d9d0dce2008b"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://v8.dev/blog/pointer-compression">https://v8.dev/blog/pointer-compression</a></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-d1b6610fe7b14514a3e36ecb7d5a61c1"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbb87b075-e3ed-4d65-a522-73884d87f37d%2F49452edd-676b-4b30-b19c-4da6ec162370%2Fv2-5794dd4c556c821d61267e8a90da2fe0_720w.webp?table=block&amp;id=d1b6610f-e7b1-4514-a3e3-6ecb7d5a61c1&amp;t=d1b6610f-e7b1-4514-a3e3-6ecb7d5a61c1&amp;width=708&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-47f092ac85ab4ee583f5b038bece0335" data-id="47f092ac85ab4ee583f5b038bece0335"><span><div id="47f092ac85ab4ee583f5b038bece0335" class="notion-header-anchor"></div><a class="notion-hash-link" href="#47f092ac85ab4ee583f5b038bece0335" title="4. 交互操作优化"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b><b>4. 交互操作优化</b></b></span></span></h4><div class="notion-text notion-block-bf9bec9c837c407fa1d99b5e54fa95ee">除了加载，渲染等方面，普通的交互操作在大场景的条件下，也容易带来非常大的挑战。交互操作中一个常见的问题就是模型拾取。一般普通的三维场景中，我们常用的是射线拾取的方式，遍历场景中的模型，进行模型的相交测试。但是由于场景非常大，可能会导致整个遍历非常耗时。所以我们要对拾取进行优化。常见的优化方式有使用 GPU 拾取、以及使用八叉树进行加速遍历等方式。</div><div class="notion-text notion-block-c624ca600cac46048ca59b6483b805fd"><b>gpu 拾取</b> 一般做法是给每个 mesh 一种颜色 然后渲染绘制一遍，在鼠标点所在的位置调用 readPixel 读取像素颜色，根据颜色与模型的对应关系，反推当前拾取到的颜色对应的 mesh。</div><div class="notion-text notion-block-6a0846340ce8445cbcfe3e08194e252a"><b>八叉树优化</b> 一般是使用八叉树的数据结构，将整个场景中的模型放入八叉树的不同 cell 中，由于八叉树类似空间范围内的二分查找，所以能够非常迅速将查找范围落在最终需要遍历的模型上。从而达到加速模型场景遍历的目的。对于八叉树的实现网上有很多的版本，大家可以参考，一般笔者使用的是稀松八叉树。</div><div class="notion-blank notion-block-63ba01e18858411b9158b27eedafd843"> </div><div class="notion-callout notion-gray_background_co notion-block-d232388775a644b9bb68b2fe24f32afa"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">欢迎您在底部评论区留言，一起交流~</div></div><div class="notion-blank notion-block-ec197362016b42538e5077c30be4781a"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[遍历算法]]></title>
            <link>https://tangly1024.com/article/1cfd09a8-8a5d-46c3-b2a7-1d7d5a3faaf8</link>
            <guid>https://tangly1024.com/article/1cfd09a8-8a5d-46c3-b2a7-1d7d5a3faaf8</guid>
            <pubDate>Thu, 21 Mar 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1cfd09a88a5d46c3b2a71d7d5a3faaf8"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-9912f3a56cc045e78d981672d9d4366e" data-id="9912f3a56cc045e78d981672d9d4366e"><span><div id="9912f3a56cc045e78d981672d9d4366e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#9912f3a56cc045e78d981672d9d4366e" title="树状结构遍历算法"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">树状结构遍历算法</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2a257fac8c184fe0bbe3491b83a69ed4" data-id="2a257fac8c184fe0bbe3491b83a69ed4"><span><div id="2a257fac8c184fe0bbe3491b83a69ed4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2a257fac8c184fe0bbe3491b83a69ed4" title="reference"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">reference</span></span></h3><ul class="notion-list notion-list-disc notion-block-823f6cc1710a4e95b61c8b5221188b31"><li><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://blog.csdn.net/weixin_44704691/article/details/102639587">https://blog.csdn.net/weixin_44704691/article/details/102639587</a></li></ul><div class="notion-callout notion-gray_background_co notion-block-e914c19b84474bc7b5aa1f3096966d19"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">欢迎您在底部评论区留言，一起交流~</div></div><div class="notion-to-do notion-block-1050a3723ea746fb8288eec588a3518e"><div class="notion-to-do-item"><span class="notion-property notion-property-checkbox"><div class="notion-property-checkbox-unchecked"></div></span><div class="notion-to-do-body">如何遍历时重建树状结构</div></div><div class="notion-to-do-children"></div></div></main></div>]]></content:encoded>
        </item>
    </channel>
</rss>