<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발자 도도</title>
    <link>https://nyaang.tistory.com/</link>
    <description>더 나은 개발자가 되고 싶은 도도의 개발 블로그
https://github.com/kdh0503</description>
    <language>ko</language>
    <pubDate>Thu, 16 Apr 2026 21:07:16 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>냥냥냥냥냥냥</managingEditor>
    <item>
      <title>앱 개발 중 Trouble shooting</title>
      <link>https://nyaang.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Compose를 이용해서 앱 짜보면서 발생한 trouble shooting 정리해보는 건입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;build.gradle.kts에 implementation 안 되는 사항&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1739024034652&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// build.gradle.kts

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.kotlin.android) apply false
}

dependencies {
    implementation(&quot;androidx.navigation:navigation-compose:2.8.6&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unresolved reference: implementation 과 같은 상황이 발생 시,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739024063522&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// build.gradle.kts

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;apply false 삭제 하면 됩니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cause:&amp;nbsp;compileSdkVersion&amp;nbsp;is&amp;nbsp;not&amp;nbsp;specified.&amp;nbsp;Please&amp;nbsp;add&amp;nbsp;it&amp;nbsp;to&amp;nbsp;build.gradle&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1739024376302&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// build.gradle.kts (:app)

android {
  // 여기에 compileSdk 설정 안 되어 있어서 발생&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 compileSdk가 빠져있어서 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739024417011&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// build.gradle.kts (:app)

android {

  compileSdk = 34 // 이렇게 추가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project/Compose 이용해서 app 짜보기</category>
      <author>냥냥냥냥냥냥</author>
      <guid isPermaLink="true">https://nyaang.tistory.com/41</guid>
      <comments>https://nyaang.tistory.com/41#entry41comment</comments>
      <pubDate>Sat, 8 Feb 2025 23:14:56 +0900</pubDate>
    </item>
    <item>
      <title>Data Science | numpy, pandas</title>
      <link>https://nyaang.tistory.com/40</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;사내 데이터 싸이언스 시험을 위해 정리하기 위한 용도로 남겨 둡니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 분들께도 유용할 수 있다면 좋겠네요&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주피터 노트북을 설치하고 로컬로 사용을 하셔도 되지만 저의 개발 환경은 코랩을 사용하고 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://colab.research.google.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://colab.research.google.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738683300343&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Google Colab&quot; data-og-description=&quot;&quot; data-og-host=&quot;colab.research.google.com&quot; data-og-source-url=&quot;https://colab.research.google.com/&quot; data-og-url=&quot;https://colab.research.google.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/weqXT/hyX7WlmXE3/7uck4t4S5IOFqVBC5rkVDK/img.png?width=260&amp;amp;height=260&amp;amp;face=0_0_260_260&quot;&gt;&lt;a href=&quot;https://colab.research.google.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://colab.research.google.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/weqXT/hyX7WlmXE3/7uck4t4S5IOFqVBC5rkVDK/img.png?width=260&amp;amp;height=260&amp;amp;face=0_0_260_260');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Google Colab&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;colab.research.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 라이브러리 중 데이터 분석을 유용하게 하기 위한 라이브러리로 numpy, pandas 가 있습니다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;numpy&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numpy는 아래처럼 import를 합니다&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;262&quot; data-origin-height=&quot;66&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WKA6a/btsL6rxGYlM/PKHB78zYFdGPEEB4R9Y3N1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WKA6a/btsL6rxGYlM/PKHB78zYFdGPEEB4R9Y3N1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WKA6a/btsL6rxGYlM/PKHB78zYFdGPEEB4R9Y3N1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWKA6a%2FbtsL6rxGYlM%2FPKHB78zYFdGPEEB4R9Y3N1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;262&quot; height=&quot;66&quot; data-origin-width=&quot;262&quot; data-origin-height=&quot;66&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;(as np는 안써도 상관 없는데 이렇게들 많이 쓰더라구요)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numpy로는 array만 사용해보긴 했습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 1차원 배열을 만들 수 있습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUgxyu/btsL586iV24/qMbJEi1k083PeqF8wkjHYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUgxyu/btsL586iV24/qMbJEi1k083PeqF8wkjHYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUgxyu/btsL586iV24/qMbJEi1k083PeqF8wkjHYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUgxyu%2FbtsL586iV24%2FqMbJEi1k083PeqF8wkjHYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;271&quot; height=&quot;127&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차원 배열 뿐만 아니라 그 이상의 차원의 배열도 만들 수 있습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;288&quot; data-origin-height=&quot;145&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bb40DN/btsL6sXFVQB/xHVxxWIymVGcQ2qoQscmoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bb40DN/btsL6sXFVQB/xHVxxWIymVGcQ2qoQscmoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bb40DN/btsL6sXFVQB/xHVxxWIymVGcQ2qoQscmoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbb40DN%2FbtsL6sXFVQB%2FxHVxxWIymVGcQ2qoQscmoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;288&quot; height=&quot;145&quot; data-origin-width=&quot;288&quot; data-origin-height=&quot;145&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;array 첫 대괄호 [ 의 개수가 몇 개인지 보면 몇 차원 배열인 지 알 수 있습니다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학창 시절 배웠던 통계를 기억해보면,&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;합(sum), 최대값(max), 최소값(min), 평균(mean), 분산(var), 표준편차(std)&quot; 등등을 확인을 했던 거 같네요&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUgxyu/btsL586iV24/qMbJEi1k083PeqF8wkjHYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUgxyu/btsL586iV24/qMbJEi1k083PeqF8wkjHYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUgxyu/btsL586iV24/qMbJEi1k083PeqF8wkjHYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUgxyu%2FbtsL586iV24%2FqMbJEi1k083PeqF8wkjHYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;271&quot; height=&quot;127&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1차원 배열 1 , 2, 3이 들어 있는 array에서 쭈욱 한 번 구해봤습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;290&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgbcyr/btsL8nf9rri/NexFmF10h27FeMIonsEInk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgbcyr/btsL8nf9rri/NexFmF10h27FeMIonsEInk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgbcyr/btsL8nf9rri/NexFmF10h27FeMIonsEInk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbgbcyr%2FbtsL8nf9rri%2FNexFmF10h27FeMIonsEInk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;290&quot; height=&quot;255&quot; data-origin-width=&quot;290&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;pandas&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pandas는 아래와 같이 import 합니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;245&quot; data-origin-height=&quot;48&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpXxfS/btsL8m9nZTi/W3cSOdHaTkcko1ucKaiixk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpXxfS/btsL8m9nZTi/W3cSOdHaTkcko1ucKaiixk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpXxfS/btsL8m9nZTi/W3cSOdHaTkcko1ucKaiixk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpXxfS%2FbtsL8m9nZTi%2FW3cSOdHaTkcko1ucKaiixk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;245&quot; height=&quot;48&quot; data-origin-width=&quot;245&quot; data-origin-height=&quot;48&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pandas에도 array가 존재하지만 여기선 좀 더 이쁜 Series 라는 애가 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Series&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;series는 1차원 배열과 같은 자료 구조입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;array를 출력했을 때와 달리 레이블로 이쁘게 보여집니다&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Series 출력 시&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Array 출력시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q15D2/btsL6Yu0tkO/ptKzKLOEIsNGjMNSwniuzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q15D2/btsL6Yu0tkO/ptKzKLOEIsNGjMNSwniuzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q15D2/btsL6Yu0tkO/ptKzKLOEIsNGjMNSwniuzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq15D2%2FbtsL6Yu0tkO%2FptKzKLOEIsNGjMNSwniuzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;258&quot; height=&quot;234&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;234&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;248&quot; data-origin-height=&quot;126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oWUuV/btsL6XQqHZF/riyrzxTEXbmrAZflhiSQ70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oWUuV/btsL6XQqHZF/riyrzxTEXbmrAZflhiSQ70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oWUuV/btsL6XQqHZF/riyrzxTEXbmrAZflhiSQ70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoWUuV%2FbtsL6XQqHZF%2FriyrzxTEXbmrAZflhiSQ70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;460&quot; height=&quot;234&quot; data-origin-width=&quot;248&quot; data-origin-height=&quot;126&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numpy의 array에서 구해본 것 처럼 여기서도 통계에서 기초적으로 쓰이는 값들을 구해봤습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LBgKD/btsL5PTtbm5/UtSCYkSvAaohzCV45WFiW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LBgKD/btsL5PTtbm5/UtSCYkSvAaohzCV45WFiW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LBgKD/btsL5PTtbm5/UtSCYkSvAaohzCV45WFiW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLBgKD%2FbtsL5PTtbm5%2FUtSCYkSvAaohzCV45WFiW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;260&quot; height=&quot;264&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DataFrame&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;dataFrame은 2차원 배열과 같은 자료 구조입니다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사실 dataFrame을 많이 사용 합니다&lt;/p&gt;</description>
      <category>Python/Data Science</category>
      <author>냥냥냥냥냥냥</author>
      <guid isPermaLink="true">https://nyaang.tistory.com/40</guid>
      <comments>https://nyaang.tistory.com/40#entry40comment</comments>
      <pubDate>Wed, 5 Feb 2025 09:27:37 +0900</pubDate>
    </item>
    <item>
      <title>torchrun을 실행 했을 때 일어나는 동작들</title>
      <link>https://nyaang.tistory.com/39</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;딥러닝이 한창 열풍인 요즘, 딥러닝 관련 프레임워크로는&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파이토치&lt;/b&gt;, &lt;b&gt;텐서플로&lt;/b&gt;, &lt;b&gt;케라스&lt;/b&gt; 등등이 많이 쓰이고 있는 것으로 보입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 ai 관련 교육을 들으며 파이토치 library 사용하는 것을 배웠는데, 그 때부터 파이토치에 대해 호기심이 생기더라구요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이번에는 파이토치에서 근간이 되는 torchrun을 실행할 때 어떤 일이 일어나는 지 한 번 알아보고자 합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pytorch.org/docs/stable/elastic/run.html&quot;&gt;torchrun (Elastic Launch) &amp;mdash; PyTorch 2.5 documentation&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735451651171&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;torchrun (Elastic Launch) &amp;mdash; PyTorch 2.5 documentation&quot; data-og-description=&quot;torchrun (Elastic Launch) Superset of torch.distributed.launch. torchrun provides a superset of the functionality as torch.distributed.launch with the following additional functionalities: Worker failures are handled gracefully by restarting all workers. W&quot; data-og-host=&quot;pytorch.org&quot; data-og-source-url=&quot;https://pytorch.org/docs/stable/elastic/run.html&quot; data-og-url=&quot;https://pytorch.org/docs/stable/elastic/run.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://pytorch.org/docs/stable/elastic/run.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pytorch.org/docs/stable/elastic/run.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;torchrun (Elastic Launch) &amp;mdash; PyTorch 2.5 documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;torchrun (Elastic Launch) Superset of torch.distributed.launch. torchrun provides a superset of the functionality as torch.distributed.launch with the following additional functionalities: Worker failures are handled gracefully by restarting all workers. W&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;pytorch.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 pytorch 사이트에서 보면 torchrun api에 대한 설명이 있습니다&lt;/p&gt;
&lt;p style=&quot;background-color: #54c7ec; color: #ffffff; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Note&lt;/p&gt;
&lt;p style=&quot;color: #262626; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f3f4f7; color: #6c6c6d;&quot;&gt;torchrun&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a python&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #ee4c2c;&quot; href=&quot;https://packaging.python.org/en/latest/specifications/entry-points/#use-for-scripts&quot;&gt;console script&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to the main module&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #ee4c2c;&quot; href=&quot;https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py&quot;&gt;torch.distributed.run&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;declared in the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f3f4f7; color: #6c6c6d;&quot;&gt;entry_points&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;configuration in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #ee4c2c;&quot; href=&quot;https://github.com/pytorch/pytorch/blob/master/setup.py&quot;&gt;setup.py&lt;/a&gt;. It is equivalent to invoking&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f3f4f7; color: #6c6c6d;&quot;&gt;python&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f3f4f7; color: #6c6c6d;&quot;&gt;-m&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f3f4f7; color: #6c6c6d;&quot;&gt;torch.distributed.run&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 실제 코드에서 어떻게 연결되는지 한 번 보려고 합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;torchrun의 위치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/pytorch/pytorch/blob/main/setup.py#L1056&quot;&gt;pytorch/setup.py at main &amp;middot; pytorch/pytorch&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735451686685&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;pytorch/setup.py at main &amp;middot; pytorch/pytorch&quot; data-og-description=&quot;Tensors and Dynamic neural networks in Python with strong GPU acceleration - pytorch/pytorch&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/pytorch/pytorch/blob/main/setup.py#L1056&quot; data-og-url=&quot;https://github.com/pytorch/pytorch/blob/main/setup.py&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bwLgd0/hyXSDeWwXQ/gb8bTAcY4yHNG9bVcubcL0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/M1mfj/hyXSxy00Z5/Q8DEj6owKocQnxc7p02Uhk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/pytorch/pytorch/blob/main/setup.py#L1056&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/pytorch/pytorch/blob/main/setup.py#L1056&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bwLgd0/hyXSDeWwXQ/gb8bTAcY4yHNG9bVcubcL0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/M1mfj/hyXSxy00Z5/Q8DEj6owKocQnxc7p02Uhk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;pytorch/setup.py at main &amp;middot; pytorch/pytorch&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Tensors and Dynamic neural networks in Python with strong GPU acceleration - pytorch/pytorch&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1735451072046&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    entry_points = {
        &quot;console_scripts&quot;: [
            &quot;torchrun = torch.distributed.run:main&quot;,
        ],&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;torchrun은 결국 torch.distributed.run의 main함수와 연결 됩니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;torch.distributed.run의 main함수는 아래와 같이 구성 되어 있습니다&lt;/p&gt;
&lt;pre id=&quot;code_1735451895714&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def run(args):
    torch.multiprocessing._set_thread_name(&quot;pt_elastic&quot;)

    if args.standalone:
        args.rdzv_backend = &quot;c10d&quot;
        args.rdzv_endpoint = &quot;localhost:0&quot;
        args.rdzv_id = str(uuid.uuid4())
        logger.info(
            &quot;\n**************************************\n&quot;
            &quot;Rendezvous info:\n&quot;
            &quot;--rdzv-backend=%s &quot;
            &quot;--rdzv-endpoint=%s &quot;
            &quot;--rdzv-id=%s\n&quot;
            &quot;**************************************\n&quot;,
            args.rdzv_backend,
            args.rdzv_endpoint,
            args.rdzv_id,
        )

    config, cmd, cmd_args = config_from_args(args)
    elastic_launch(
        config=config,
        entrypoint=cmd,
    )(*cmd_args)


@record
def main(args=None):
    args = parse_args(args)
    run(args)


if __name__ == &quot;__main__&quot;:
    main()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&amp;lrm;&lt;/span&gt;&lt;span style=&quot;list-style-type: unset; background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;config_from_args 내부에서 --nnode = 1 --nproc-per-node=$NUM_TRAINERS 등과 같은 arg로 넘겨준 값들을 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;list-style-type: unset; background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;parsing을 하고 elastic_launch에 인자로 넘겨줍니다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735452396565&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class elastic_launch:
...

    def __init__(
        self,
        config: LaunchConfig,
        entrypoint: Union[Callable, str, None],
    ):
        self._config = config
        self._entrypoint = entrypoint

    def __call__(self, *args):
        return launch_agent(self._config, self._entrypoint, list(args))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;elastic_launch 객체를 실제 call 해줄 때,&amp;nbsp; launch_agent가 실행되며 코드는 아래와 같습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735452769000&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def launch_agent(
    config: LaunchConfig,
    entrypoint: Union[Callable, str, None],
    args: List[Any],
) -&amp;gt; Dict[int, Any]:
...

    agent = LocalElasticAgent(
        spec=spec,
        logs_specs=config.logs_specs,  # type: ignore[arg-type]
        start_method=config.start_method,
        log_line_prefix_template=config.log_line_prefix_template,
    )

    shutdown_rdzv = True
    try:
        metrics.initialize_metrics(metrics.MetricsConfig(config.metrics_cfg))

        result = agent.run()
        # records that agent.run() has succeeded NOT that workers have succeeded
        events.record(agent.get_event_succeeded())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;launch_agent에서는 LocalElasticAgent를 run을 해줍니다&lt;/p&gt;
&lt;pre id=&quot;code_1735452824053&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class LocalElasticAgent(SimpleElasticAgent):&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LocalElasticAgent는 simpleElasticAgent를 상속받고 있고 거기에 run 메서드가 있습니다&lt;/p&gt;
&lt;pre id=&quot;code_1735452880134&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @prof
    def run(self, role: str = DEFAULT_ROLE) -&amp;gt; RunResult:
        start_time = time.monotonic()
        shutdown_called: bool = False
        try:
            result = self._invoke_run(role)
            self._total_execution_time = int(time.monotonic() - start_time)
            self._record_metrics(result)
            self._record_worker_events(result)
            return result&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;run 안에서는 _invoke_run을 실행해주는게 핵심인데,&lt;/p&gt;
&lt;pre id=&quot;code_1735452959237&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def _invoke_run(self, role: str = DEFAULT_ROLE) -&amp;gt; RunResult:
        # NOTE: currently only works for a single role

        spec = self._worker_group.spec
        role = spec.role

        logger.info(
            &quot;[%s] starting workers for entrypoint: %s&quot;, role, spec.get_entrypoint_name()
        )

        self._initialize_workers(self._worker_group)
        monitor_interval = spec.monitor_interval
        rdzv_handler = spec.rdzv_handler

        while True:
            assert self._worker_group.state != WorkerState.INIT
            time.sleep(monitor_interval)
            run_result = self._monitor_workers(self._worker_group)
            state = run_result.state
            self._worker_group.state = state

            put_metric(f&quot;workers.{role}.remaining_restarts&quot;, self._remaining_restarts)
            put_metric(f&quot;workers.{role}.{state.name.lower()}&quot;, 1)

            if state == WorkerState.SUCCEEDED:
                logger.info(
                    &quot;[%s] worker group successfully finished.&quot;
                    &quot; Waiting %s seconds for other agents to finish.&quot;,
                    role,
                    self._exit_barrier_timeout,
                )
                self._exit_barrier()
                return run_result
            elif state in {WorkerState.UNHEALTHY, WorkerState.FAILED}:
                if self._remaining_restarts &amp;gt; 0:
                    logger.info(
                        &quot;[%s] Worker group %s. &quot;
                        &quot;%s/%s attempts left;&quot;
                        &quot; will restart worker group&quot;,
                        role,
                        state.name,
                        self._remaining_restarts,
                        spec.max_restarts,
                    )
                    self._remaining_restarts -= 1
                    self._restart_workers(self._worker_group)
                else:
                    self._stop_workers(self._worker_group)
                    self._worker_group.state = WorkerState.FAILED
                    return run_result
            elif state == WorkerState.HEALTHY:
                # membership changes do not count as retries
                num_nodes_waiting = rdzv_handler.num_nodes_waiting()
                group_rank = self._worker_group.group_rank
                if num_nodes_waiting &amp;gt; 0:
                    logger.info(
                        &quot;[%s] Detected %s &quot;
                        &quot;new nodes from group_rank=%s; &quot;
                        &quot;will restart worker group&quot;,
                        role,
                        num_nodes_waiting,
                        group_rank,
                    )
                    self._restart_workers(self._worker_group)
            else:
                raise Exception(  # noqa: TRY002
                    f&quot;[{role}] Worker group in {state.name} state&quot;
                )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_monitor_workers를 실행하고 그로 부터 받은 state에 따라 처리를 해주는 식이네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_monitor_workers는 abstractmethod 입니다 구현부가 따로 어딘가에 있겠죠 ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국, 아래와 같이 명령어를 입력해주면&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tochrun뒤에 붙은 argument 들은 config_from_args을 통해 parsing 되어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Elastic_Launch 객체를 실행하고, 내부적인 동작은 LocalElasticAgent의 _invoke_run을 실행하는 것입니다&lt;/p&gt;
&lt;pre id=&quot;code_1735453440444&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;torchrun
    --standalone
    --nnodes=1
    --nproc-per-node=$NUM_TRAINERS
    YOUR_TRAINING_SCRIPT.py (--arg1 ... train script args...)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽어 주셔서 감사합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 번엔, llama에서 torchrun을 통해 example script 파이썬 파일을 실행 했을 때 어떤 동작이 일어나는지 한 번 알아보도록 하겠습니다&lt;/p&gt;</description>
      <category>AI/Llama</category>
      <author>냥냥냥냥냥냥</author>
      <guid isPermaLink="true">https://nyaang.tistory.com/39</guid>
      <comments>https://nyaang.tistory.com/39#entry39comment</comments>
      <pubDate>Sun, 29 Dec 2024 15:29:17 +0900</pubDate>
    </item>
    <item>
      <title>안드로이드 인풋 프레임워크 학습(InputManager) 1</title>
      <link>https://nyaang.tistory.com/38</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 안드로이드의 input (터치, 제스처 등)을 관리하는 inputManager에 대해서 알아보려고 합니다&lt;br&gt;&amp;nbsp;&lt;br&gt;inputflinger의 시작은 init.rc가 시작 되는 시점에 inputflinger.rc가 시작 됩니다&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// inputflinger.rc
service inputflinger /system/bin/inputflinger
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;class main
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;user system
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;group input wakelock
#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onrestart restart zygote&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;InputFlinger로 빌드 되어 있는 main.cpp가 시작되고 아래의 코드 순서대로 호출이 됩니다&lt;br&gt;여기서 봐야할 부분은InputDriver에서 input_open을 호출해주는 부분입니다&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;// main.cpp
int main(int, char**) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ProcessState::self()-&amp;gt;setThreadPoolMaxThreadCount(4);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BinderService&amp;lt;InputFlinger&amp;gt;::publishAndJoinThreadPool(true);&amp;nbsp;&amp;nbsp;// inputflinger 생성 및 service에 추가
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
}

// inputflinger.cpp
InputFlinger::InputFlinger() :
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BnInputFlinger() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ALOGI(&quot;InputFlinger is starting&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mHost = new InputHost();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mHost-&amp;gt;registerInputDriver(new InputDriver(INPUT_INSTANCE_EVDEV));
}

// inputDriver.cpp
InputDriver::InputDriver(const char* name) : mName(String8(name)) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const hw_module_t* module;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int err = input_open(&amp;amp;module, name);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LOG_ALWAYS_FATAL_IF(err != 0, &quot;Input module %s not found&quot;, name);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mHal = reinterpret_cast&amp;lt;const input_module_t*&amp;gt;(module);
}

// input.h
static inline int input_open(const struct hw_module_t** module, const char* type) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return hw_get_module_by_class(INPUT_HARDWARE_MODULE_ID, type, module);
}


// hardware.c&amp;nbsp;&amp;nbsp;
int hw_get_module_by_class(const char *class_id, const char *inst,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const struct hw_module_t **module)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;snprintf(prop_name, sizeof(prop_name), &quot;ro.hardware.%s&quot;, name);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (property_get(prop_name, prop, NULL) &amp;gt; 0) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goto found;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* Loop through the configuration variants looking for a module */
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (i=0 ; i&amp;lt;HAL_VARIANT_KEYS_COUNT; i++) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (property_get(variant_keys[i], prop, NULL) == 0) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;continue;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goto found;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* Nothing found, try the default */
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (hw_module_exists(path, sizeof(path), name, &quot;default&quot;) == 0) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goto found;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return -ENOENT;
found:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* load the module, if this fails, we're doomed, and we should not try
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; * to load a different variant. */
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return load(class_id, path, module);
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;input_open을 호출해주면 hw_get_module_by_class를 통해서 &quot;input&quot; 이라는 이름의 module을 찾는데&lt;br&gt;/system/lib/hw, /vendor/lib/hw (64비트면 lib64) path에 라이브러리가 있으면 동적으로 로딩을 해주는 식입니다&amp;nbsp;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;// hardware/libhardware/hardware.c
#if defined(__LP64__)
#define HAL_LIBRARY_PATH1 &quot;/system/lib64/hw&quot;
#define HAL_LIBRARY_PATH2 &quot;/vendor/lib64/hw&quot;
#else
#define HAL_LIBRARY_PATH1 &quot;/system/lib/hw&quot;
#define HAL_LIBRARY_PATH2 &quot;/vendor/lib/hw&quot;
#endif

static int hw_module_exists(char *path, size_t path_len, const char *name,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const char *subname)
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;snprintf(path, path_len, &quot;%s/%s.%s.so&quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HAL_LIBRARY_PATH2, name, subname);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (access(path, R_OK) == 0)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;snprintf(path, path_len, &quot;%s/%s.%s.so&quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HAL_LIBRARY_PATH1, name, subname);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (access(path, R_OK) == 0)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return -ENOENT;
}

// hardware/libhardware/hardware.c
static int load(const char *id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const char *path,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const struct hw_module_t **pHmi) 
{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;handle = dlopen(path, RTLD_NOW);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;hmi = (struct hw_module_t *)dlsym(handle, sym);

...

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;hmi-&amp;gt;dso = handle;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* success */
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;status = 0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
...

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*pHmi = hmi;&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;결국 동적으로 붙은 module을 전달하며, 그 모듈을 mHal로 저장해둡니다 (Hardware Abstract Layer)&amp;nbsp;&lt;br&gt;inputDriver는 결국 lib단에서 inputFlinger host 단으로 호출될 수 있도록 kCallback을 넘겨줍니다&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;여느 service들과 마찬가지로 InputManagerService 또한 SystemServer 내에서 시작됩니다&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// SystemServer.java
&amp;nbsp;&amp;nbsp; private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp; ...

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;InputManagerService inputManager = null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;t.traceBegin(&quot;StartInputManagerService&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;inputManager = new InputManagerService(context);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;t.traceEnd();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;...

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;t.traceBegin(&quot;StartInputManager&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;inputManager.start();&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1469&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LDeRI/btsFKQDWGZM/tU50PkbAmzzkhlVJx6AqGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LDeRI/btsFKQDWGZM/tU50PkbAmzzkhlVJx6AqGK/img.png&quot; data-alt=&quot;InputManagerService의 시작 process&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LDeRI/btsFKQDWGZM/tU50PkbAmzzkhlVJx6AqGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLDeRI%2FbtsFKQDWGZM%2FtU50PkbAmzzkhlVJx6AqGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1469&quot; height=&quot;501&quot; data-origin-width=&quot;1469&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;InputManagerService의 시작 process&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>안드로이드/안드로이드 프레임워크</category>
      <author>냥냥냥냥냥냥</author>
      <guid isPermaLink="true">https://nyaang.tistory.com/38</guid>
      <comments>https://nyaang.tistory.com/38#entry38comment</comments>
      <pubDate>Thu, 7 Nov 2024 23:27:08 +0900</pubDate>
    </item>
    <item>
      <title>안드로이드 앱 프레임워크 학습(ActivityManager) 3</title>
      <link>https://nyaang.tistory.com/37</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/36&quot;&gt;안드로이드 앱 프레임워크 학습(ActivityManager) 2 (tistory.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1710045896686&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;안드로이드 앱 프레임워크 학습(ActivityManager) 2&quot; data-og-description=&quot;안드로이드 앱 프레임워크 학습(ActivityManager) 1 (tistory.com) 안드로이드 앱 프레임워크 학습(ActivityManager) 1 이전에 StartActivity 호출 순서를 블로그로 써본적이 있었는데 (자세한 건 아래 참조 부탁드&quot; data-og-host=&quot;nyaang.tistory.com&quot; data-og-source-url=&quot;https://nyaang.tistory.com/36&quot; data-og-url=&quot;https://nyaang.tistory.com/36&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bqWKIb/hyVxvbMhIl/tdxcQDSNE5AcSDUG50pYP1/img.png?width=611&amp;amp;height=491&amp;amp;face=0_0_611_491,https://scrap.kakaocdn.net/dn/bfC4vd/hyVxyGkMy3/JL6owDs8CejG1g1csrBIKk/img.png?width=611&amp;amp;height=491&amp;amp;face=0_0_611_491,https://scrap.kakaocdn.net/dn/nwoO4/hyVxvpkauV/OW8DTlySy3k5QLYmC0b9c1/img.png?width=1321&amp;amp;height=581&amp;amp;face=0_0_1321_581&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/36&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nyaang.tistory.com/36&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bqWKIb/hyVxvbMhIl/tdxcQDSNE5AcSDUG50pYP1/img.png?width=611&amp;amp;height=491&amp;amp;face=0_0_611_491,https://scrap.kakaocdn.net/dn/bfC4vd/hyVxyGkMy3/JL6owDs8CejG1g1csrBIKk/img.png?width=611&amp;amp;height=491&amp;amp;face=0_0_611_491,https://scrap.kakaocdn.net/dn/nwoO4/hyVxvpkauV/OW8DTlySy3k5QLYmC0b9c1/img.png?width=1321&amp;amp;height=581&amp;amp;face=0_0_1321_581');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드 앱 프레임워크 학습(ActivityManager) 2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드 앱 프레임워크 학습(ActivityManager) 1 (tistory.com) 안드로이드 앱 프레임워크 학습(ActivityManager) 1 이전에 StartActivity 호출 순서를 블로그로 써본적이 있었는데 (자세한 건 아래 참조 부탁드&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nyaang.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 시간엔 안드로이드 프레임워크에서 process 관리 부분에 대해 간단히 알아 봤습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 안드로이드에서 lmkd (Low Memory Killer Demon)이 뭔지 어떤 동작을 하는지 알아보려고 합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;android docs를 보면 lmkd의 설명을 아래와 같이 해놓았습니다&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124; text-align: start;&quot;&gt;Android 로우 메모리 킬러 데몬(&lt;/span&gt;lmkd&lt;span style=&quot;background-color: #ffffff; color: #202124; text-align: start;&quot;&gt;) 프로세스는 실행 중인 Android 시스템의 메모리 상태를 모니터링하고, 시스템 성능을 만족할 만한 수준으로 유지하기 위해 가장 필요성이 적은 프로세스를 종료하여 높은 메모리 압력에 반응합니다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요성을 나타내는 값이 OOM_SCORE이고 MIN, MAX값은 아래와 같습니다&lt;/p&gt;
&lt;pre id=&quot;code_1710068182408&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// lmkd.cpp
/* OOM score values used by both kernel and framework */
#define OOM_SCORE_ADJ_MIN       (-1000)
#define OOM_SCORE_ADJ_MAX       1000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;java 단에서는 lmkdConnection 내에서 LocalSocket을 이용하여 lmkd의 fd을 만들어서 연결하고&lt;/p&gt;
&lt;pre id=&quot;code_1710067111651&quot; class=&quot;haxe&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// ProcessList.java
    void init(ActivityManagerService service, ActiveUids activeUids,
            PlatformCompat platformCompat) {
            
            ...
            
        if (sKillHandler == null) {
            sKillThread = new ServiceThread(TAG + &quot;:kill&quot;,
                    THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
            sKillThread.start();
            sKillHandler = new KillHandler(sKillThread.getLooper());
            sLmkdConnection = new LmkdConnection(sKillThread.getLooper().getQueue(),
                    new LmkdConnection.LmkdConnectionListener() {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;native 단에서는 lmkd.rc를 통해서 부팅 후 lmkd의 demon이 시작되며 init에서 lmkd의 fd socket을 연결합니다&lt;/p&gt;
&lt;pre id=&quot;code_1710067076325&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// lmkd::init
    ctrl_sock.sock = android_get_control_socket(&quot;lmkd&quot;);
    if (ctrl_sock.sock &amp;lt; 0) {
        ALOGE(&quot;get lmkd control socket failed&quot;);
        return -1;
    }

    ret = listen(ctrl_sock.sock, MAX_DATA_CONN);
    if (ret &amp;lt; 0) {
        ALOGE(&quot;lmkd control socket listen failed (errno=%d)&quot;, errno);
        return -1;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 그럼 java단 부터 sequence를 한 번 보겠습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;lmkdConnection.png&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;541&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biJXNQ/btsFD4oQVGb/BD9w9xHkCUVSYtK86edI50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biJXNQ/btsFD4oQVGb/BD9w9xHkCUVSYtK86edI50/img.png&quot; data-alt=&quot;lmkdConnection&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biJXNQ/btsFD4oQVGb/BD9w9xHkCUVSYtK86edI50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiJXNQ%2FbtsFD4oQVGb%2FBD9w9xHkCUVSYtK86edI50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;866&quot; height=&quot;541&quot; data-filename=&quot;lmkdConnection.png&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;541&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;lmkdConnection&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;native 단의 sequence는 아래와 같습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;lmkdnative.png&quot; data-origin-width=&quot;446&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ob0wM/btsFF0syiod/6ASiguoZqoqQxm3tDeMK2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ob0wM/btsFF0syiod/6ASiguoZqoqQxm3tDeMK2K/img.png&quot; data-alt=&quot;lmkd sequence&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ob0wM/btsFF0syiod/6ASiguoZqoqQxm3tDeMK2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fob0wM%2FbtsFF0syiod%2F6ASiguoZqoqQxm3tDeMK2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;446&quot; height=&quot;351&quot; data-filename=&quot;lmkdnative.png&quot; data-origin-width=&quot;446&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;lmkd sequence&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 lmkd 에서의 init에서 봐야할 사항이 조금 더 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;use_inkernel_interface 값에 따라 분기가 되기 때문입니다&lt;/p&gt;
&lt;pre id=&quot;code_1710068855074&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// lmkd::init
    has_inkernel_module = !access(INKERNEL_MINFREE_PATH, W_OK);
    use_inkernel_interface = has_inkernel_module;

    if (use_inkernel_interface) {
        ALOGI(&quot;Using in-kernel low memory killer interface&quot;);
        if (init_poll_kernel()) {
            epev.events = EPOLLIN;
            epev.data.ptr = (void*)&amp;amp;kernel_poll_hinfo;
            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kpoll_fd, &amp;amp;epev) != 0) {
                ALOGE(&quot;epoll_ctl for lmk events failed (errno=%d)&quot;, errno);
                close(kpoll_fd);
                kpoll_fd = -1;
            } else {
                maxevents++;
                /* let the others know it does support reporting kills */
                property_set(&quot;sys.lmk.reportkills&quot;, &quot;1&quot;);
            }
        }
    } else {
        if (!init_monitors()) {
            return -1;
        }
        /* let the others know it does support reporting kills */
        property_set(&quot;sys.lmk.reportkills&quot;, &quot;1&quot;);
    }
    
    
// lmkd::init_monitor
static bool init_monitors() {
    /* Try to use psi monitor first if kernel has it */
    use_psi_monitors = GET_LMK_PROPERTY(bool, &quot;use_psi&quot;, true) &amp;amp;&amp;amp;
        init_psi_monitors();
    /* Fall back to vmpressure */
    if (!use_psi_monitors &amp;amp;&amp;amp;
        (!init_mp_common(VMPRESS_LEVEL_LOW) ||
        !init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
        !init_mp_common(VMPRESS_LEVEL_CRITICAL))) {
        ALOGE(&quot;Kernel does not support memory pressure events or in-kernel low memory killer&quot;);
        return false;
    }
    if (use_psi_monitors) {
        ALOGI(&quot;Using psi monitors for memory pressure detection&quot;);
    } else {
        ALOGI(&quot;Using vmpressure for memory pressure detection&quot;);
    }
    return true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 psi monitor에서도 use_new_strategy 유무에 따라 달라집니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;init_psi_monitors는 init_monitors가 불릴 때 불려지는 함수이고, use_new_strategy boolean 값에 따라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;init_mp_psi 내부에서 handler가 mp_event_psi , mp_event_common으로 나누어집니다&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1710069635497&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// lmkd::init_psi_monitors
static bool init_psi_monitors() {
    /*
     * When PSI is used on low-ram devices or on high-end devices without memfree levels
     * use new kill strategy based on zone watermarks, free swap and thrashing stats.
     * Also use the new strategy if memcg has not been mounted in the v1 cgroups hiearchy since
     * the old strategy relies on memcg attributes that are available only in the v1 cgroups
     * hiearchy.
     */
    bool use_new_strategy =
        GET_LMK_PROPERTY(bool, &quot;use_new_strategy&quot;, low_ram_device || !use_minfree_levels);
        
        ...
        
    if (!init_mp_psi(VMPRESS_LEVEL_LOW, use_new_strategy)) {
        return false;
    }
    if (!init_mp_psi(VMPRESS_LEVEL_MEDIUM, use_new_strategy)) {
        destroy_mp_psi(VMPRESS_LEVEL_LOW);
        return false;
    }
    if (!init_mp_psi(VMPRESS_LEVEL_CRITICAL, use_new_strategy)) {
        destroy_mp_psi(VMPRESS_LEVEL_MEDIUM);
        destroy_mp_psi(VMPRESS_LEVEL_LOW);
        return false;
    }
    
 // lmkd::init_mp_psi   
    vmpressure_hinfo[level].handler = use_new_strategy ? mp_event_psi : mp_event_common;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안에서 좀 로직이 좀 다르긴 한데 handler가 mp_event_psi , mp_event_common 든 상관없이 결국은 find_and_kill_process를 타게 됩니다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;find_and_kill_process로직을 보면 OOM_SCORE_ADJ_MAX 부터 내려오면서 체크하고, kill_one_process를 호출합니다&lt;/p&gt;
&lt;pre id=&quot;code_1710070009576&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// lmkd::find_and_kill_process
static int find_and_kill_process(int min_score_adj, struct kill_info *ki, union meminfo *mi,
                                 struct wakeup_info *wi, struct timespec *tm,
                                 struct psi_data *pd) {
    int i;
    int killed_size = 0;
    bool lmk_state_change_start = false;
    bool choose_heaviest_task = kill_heaviest_task;

    for (i = OOM_SCORE_ADJ_MAX; i &amp;gt;= min_score_adj; i--) { // MAX 부터 
        struct proc *procp;

        if (!choose_heaviest_task &amp;amp;&amp;amp; i &amp;lt;= PERCEPTIBLE_APP_ADJ) {
            /*
             * If we have to choose a perceptible process, choose the heaviest one to
             * hopefully minimize the number of victims.
             */
            choose_heaviest_task = true;
        }

        while (true) {
            procp = choose_heaviest_task ?
                proc_get_heaviest(i) : proc_adj_tail(i);

            if (!procp)
                break;

            killed_size = kill_one_process(procp, min_score_adj, ki, mi, wi, tm, pd); // kill

// lmkd::kill_one_process
static int kill_one_process(struct proc* procp, int min_oom_score, struct kill_info *ki,
                            union meminfo *mi, struct wakeup_info *wi, struct timespec *tm,
                            struct psi_data *pd) {     
                            
                            ...
                            
    kill_result = reaper.kill({ pidfd, pid, uid }, false);

    trace_kill_end();

    if (kill_result) {
        stop_wait_for_proc_kill(false);
        ALOGE(&quot;kill(%d): errno=%d&quot;, pid, errno);
        /* Delete process record even when we fail to kill so that we don't get stuck on it */
        goto out;
    }
    
    ...
    
out:
    /*
     * WARNING: After pid_remove() procp is freed and can't be used!
     * Therefore placed at the end of the function.
     */
    pid_remove(pid);
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kill_one_process를 보면 reaper.kill 을 호출해주고 이 부분이 실질적인 process kill 부분으로 보입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1710070115195&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// reaper.cpp
int Reaper::kill(const struct target_proc&amp;amp; target, bool synchronous) {
    /* CAP_KILL required */
    if (target.pidfd &amp;lt; 0) {
        return ::kill(target.pid, SIGKILL);
    }

    if (!synchronous &amp;amp;&amp;amp; async_kill(target)) {
        // we assume the kill will be successful and if it fails we will be notified
        return 0;
    }

    int result = pidfd_send_signal(target.pidfd, SIGKILL, NULL, 0);
    if (result) {
        return result;
    }

    return is_reaping_supported() ? process_mrelease(target.pidfd, 0) : 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다&lt;/p&gt;</description>
      <category>안드로이드/안드로이드 프레임워크</category>
      <author>냥냥냥냥냥냥</author>
      <guid isPermaLink="true">https://nyaang.tistory.com/37</guid>
      <comments>https://nyaang.tistory.com/37#entry37comment</comments>
      <pubDate>Sun, 10 Mar 2024 19:22:48 +0900</pubDate>
    </item>
    <item>
      <title>안드로이드 앱 프레임워크 학습(ActivityManager) 2</title>
      <link>https://nyaang.tistory.com/36</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/35&quot;&gt;안드로이드 앱 프레임워크 학습(ActivityManager) 1 (tistory.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709402684160&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;안드로이드 앱 프레임워크 학습(ActivityManager) 1&quot; data-og-description=&quot;이전에 StartActivity 호출 순서를 블로그로 써본적이 있었는데 (자세한 건 아래 참조 부탁드립니다) Android framework StartActivity 호출 순서 (tistory.com) Android framework StartActivity 호출 순서 안녕하세요, 개&quot; data-og-host=&quot;nyaang.tistory.com&quot; data-og-source-url=&quot;https://nyaang.tistory.com/35&quot; data-og-url=&quot;https://nyaang.tistory.com/35&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cfTwQ7/hyVurAfODg/72vWZNKllX5avkOjQrKM00/img.png?width=800&amp;amp;height=519&amp;amp;face=0_0_800_519,https://scrap.kakaocdn.net/dn/lJsZ9/hyVuquz8uT/28kVCbDZKKHyHNN4FcPsI0/img.png?width=800&amp;amp;height=519&amp;amp;face=0_0_800_519,https://scrap.kakaocdn.net/dn/Cr1dp/hyVqhF5Q1B/6z9uevA62C5JSs9kjEdo3k/img.png?width=1801&amp;amp;height=991&amp;amp;face=0_0_1801_991&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/35&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nyaang.tistory.com/35&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cfTwQ7/hyVurAfODg/72vWZNKllX5avkOjQrKM00/img.png?width=800&amp;amp;height=519&amp;amp;face=0_0_800_519,https://scrap.kakaocdn.net/dn/lJsZ9/hyVuquz8uT/28kVCbDZKKHyHNN4FcPsI0/img.png?width=800&amp;amp;height=519&amp;amp;face=0_0_800_519,https://scrap.kakaocdn.net/dn/Cr1dp/hyVqhF5Q1B/6z9uevA62C5JSs9kjEdo3k/img.png?width=1801&amp;amp;height=991&amp;amp;face=0_0_1801_991');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드 앱 프레임워크 학습(ActivityManager) 1&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이전에 StartActivity 호출 순서를 블로그로 써본적이 있었는데 (자세한 건 아래 참조 부탁드립니다) Android framework StartActivity 호출 순서 (tistory.com) Android framework StartActivity 호출 순서 안녕하세요, 개&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nyaang.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 앱이 실행 될 때, RemoteAnimation, Leash 부분에 대해서 좀 알아봤는데&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 animation 쪽이 아닌 activity, process관리 부분을 한 번 알아보려고 합니다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;startProcessLocked sequence&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ProcessList.png&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;491&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9vG2R/btsFuLOTuza/V6OgSn3W5zGpzFoROMHyt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9vG2R/btsFuLOTuza/V6OgSn3W5zGpzFoROMHyt1/img.png&quot; data-alt=&quot;ProcessList 관련 sequence&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9vG2R/btsFuLOTuza/V6OgSn3W5zGpzFoROMHyt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9vG2R%2FbtsFuLOTuza%2FV6OgSn3W5zGpzFoROMHyt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;611&quot; height=&quot;491&quot; data-filename=&quot;ProcessList.png&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;491&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ProcessList 관련 sequence&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1709448184052&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ProcessList::startProcessLocked
            // Start the process.  It will either succeed and return a result containing
            // the PID of the new process, or else throw a RuntimeException.
            final String entryPoint = &quot;android.app.ActivityThread&quot;;

            return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,
                    runtimeFlags, zygotePolicyFlags, mountExternal, seInfo, requiredAbi,
                    instructionSet, invokeWith, startTime);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ProcessList에서 메서드를 불러주기 전에 entryPoint를 ActivityThread로 설정해서 넘겨줍니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기에 Zygote의 socket 통신을 통해 넘겨서 실행시켜주는 포인트는 ActivityThread의 main을 실행시켜주게 합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 ZygoteProcess에서의 Socket 관련 정보들입니다&lt;/p&gt;
&lt;pre id=&quot;code_1709447420128&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ZygoteProcess.java
    public ZygoteProcess() {
        mZygoteSocketAddress =
                new LocalSocketAddress(Zygote.PRIMARY_SOCKET_NAME,
                                       LocalSocketAddress.Namespace.RESERVED);
        mZygoteSecondarySocketAddress =
                new LocalSocketAddress(Zygote.SECONDARY_SOCKET_NAME,
                                       LocalSocketAddress.Namespace.RESERVED);

        mUsapPoolSocketAddress =
                new LocalSocketAddress(Zygote.USAP_POOL_PRIMARY_SOCKET_NAME,
                                       LocalSocketAddress.Namespace.RESERVED);
        mUsapPoolSecondarySocketAddress =
                new LocalSocketAddress(Zygote.USAP_POOL_SECONDARY_SOCKET_NAME,
                                       LocalSocketAddress.Namespace.RESERVED);

        // This constructor is used to create the primary and secondary Zygotes, which can support
        // Unspecialized App Process Pools.
        mUsapPoolSupported = true;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ProcessList2.png&quot; data-origin-width=&quot;1321&quot; data-origin-height=&quot;581&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5mDt6/btsFvXVUGkm/afejcbDhlaw0k2pOhKEi41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5mDt6/btsFvXVUGkm/afejcbDhlaw0k2pOhKEi41/img.png&quot; data-alt=&quot;application이 onCreate 될 때 까지 sequence&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5mDt6/btsFvXVUGkm/afejcbDhlaw0k2pOhKEi41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5mDt6%2FbtsFvXVUGkm%2FafejcbDhlaw0k2pOhKEi41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1321&quot; height=&quot;581&quot; data-filename=&quot;ProcessList2.png&quot; data-origin-width=&quot;1321&quot; data-origin-height=&quot;581&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;application이 onCreate 될 때 까지 sequence&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ActivityManagerService에서 attachApplicationLocked를 호출해 줄 때, ProcessList에 process관련된 정보를 넘기고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ActivityManagerService내의 mPidSelfLocked에도 pid와 app 관련 해서 저장을 해둡니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는, ActivityThread 관련해서 seq를 저장해두는 부분 코드입니다&lt;/p&gt;
&lt;pre id=&quot;code_1709449980173&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ActivityThread::main
        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format &quot;seq=114&quot;
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i &amp;gt;= 0; --i) {
                if (args[i] != null &amp;amp;&amp;amp; args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국, 안드로이드에서는 Process를 실행할 떄 Zygote를 통한다는 것을 코드 부분을 통해 봤고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행할 process 관련된 정보는 ActivityThread를 통해서 attach, bind를 통해서 그 정보들을 넘겨주고 주고 받은 후&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 과정이 끝나면 Application의 onCreate를 호출시켜주는 것을 보았습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다!&lt;/p&gt;</description>
      <category>안드로이드/안드로이드 프레임워크</category>
      <author>냥냥냥냥냥냥</author>
      <guid isPermaLink="true">https://nyaang.tistory.com/36</guid>
      <comments>https://nyaang.tistory.com/36#entry36comment</comments>
      <pubDate>Sun, 3 Mar 2024 00:01:06 +0900</pubDate>
    </item>
    <item>
      <title>안드로이드 앱 프레임워크 학습(ActivityManager) 1</title>
      <link>https://nyaang.tistory.com/35</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 StartActivity 호출 순서를 블로그로 써본적이 있었는데 (자세한 건 아래 참조 부탁드립니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/19&quot;&gt;Android framework StartActivity 호출 순서 (tistory.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709302690925&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Android framework StartActivity 호출 순서&quot; data-og-description=&quot;안녕하세요, 개발자 도도 입니다 먼저, 저는 현재 안드로이드 프레임워크 개발자로 재직 중 입니다 사실 안드로이드 앱 개발자 입장으로선 프레임워크 코드들은 당연히 되어야 하는 부분이기 &quot; data-og-host=&quot;nyaang.tistory.com&quot; data-og-source-url=&quot;https://nyaang.tistory.com/19&quot; data-og-url=&quot;https://nyaang.tistory.com/19&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iTc9w/hyVqgtrdRu/qPk6Y64LElyBAwc5y57AaK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/gI8t5/hyVussef78/5ns4gv5I0VhUOwdmlwNjEK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cwI3O9/hyVuhKZAnP/Ttc6VU7fJFEnm8MpznbrJk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/19&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nyaang.tistory.com/19&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iTc9w/hyVqgtrdRu/qPk6Y64LElyBAwc5y57AaK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/gI8t5/hyVussef78/5ns4gv5I0VhUOwdmlwNjEK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cwI3O9/hyVuhKZAnP/Ttc6VU7fJFEnm8MpznbrJk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Android framework StartActivity 호출 순서&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요, 개발자 도도 입니다 먼저, 저는 현재 안드로이드 프레임워크 개발자로 재직 중 입니다 사실 안드로이드 앱 개발자 입장으로선 프레임워크 코드들은 당연히 되어야 하는 부분이기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nyaang.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드에서 Activity는 4대 컴포넌트 중 하나입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면에 띄우는 방식이 반드시 Activity를 사용해야만 화면에 띄울 수 있는 것은 아니지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Activity를 사용하면 Framework 단에서 LifeCycle (Resume, Pause, Stop 등과 같은) 관리가 용이하다는 점이 있습니다&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 그 Activity의 LifeCycle등을 관리 해주는 ActivityManager에 대해서 한 번 알아보려고 합니다&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// SystemServer::startBootstrapServices

// Activity manager runs the show.
t.traceBegin(&quot;StartActivityManager&quot;);
// TODO: Might need to move after migration to WM.
ActivityTaskManagerService atm = mSystemServiceManager.startService(
        ActivityTaskManagerService.Lifecycle.class).getService();
mActivityManagerService = ActivityManagerService.Lifecycle.startService(
        mSystemServiceManager, atm);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WindowManagerService 처럼 ActivityManagerService도 SystemServer에서 시작이 됩니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ActivityManager.png&quot; data-origin-width=&quot;879&quot; data-origin-height=&quot;571&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coMxUr/btsFnY9L3yn/vaTqlEE1bF77bK5TMDwO80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coMxUr/btsFnY9L3yn/vaTqlEE1bF77bK5TMDwO80/img.png&quot; data-alt=&quot;Activity관련 자료구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coMxUr/btsFnY9L3yn/vaTqlEE1bF77bK5TMDwO80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoMxUr%2FbtsFnY9L3yn%2FvaTqlEE1bF77bK5TMDwO80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;879&quot; height=&quot;571&quot; data-filename=&quot;ActivityManager.png&quot; data-origin-width=&quot;879&quot; data-origin-height=&quot;571&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Activity관련 자료구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글들에서 WindowManager는 WindowState단위로 관리를 한다고 말씀을 드렸는데 ActivityManager는 ActivityRecord 기준으로 관리를 하게 됩니다 그리고 그 ActivityRecord는 WindowState안에 멤버변수로가지고 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WindowManager 분석에서는 SurfaceFlinger 부분과의 연결되는 관점 위주로 봤기에 ActivityManager 관련 된 얘기를 안했지만, 사실 Activity가 전환 시 wm 측의 transition 과정이 필요하기 때문에 AM과 WM은 어울려서 동작합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 새 activity가 실행 될 때도, multiwindow 상황이 아니라면 하나의 activity 화면을 보여주기 위해, 보이던 앱의 state를 pause -&amp;gt; stop 으로 내려야할 것이고 실행되는 액티비티는 create -&amp;gt; start -&amp;gt; resume으로 바뀌어야할 것입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LifeCycle의 변환을 알기 전에 근본적으로 Activity의 전환(transition) 간에 일어나는 상황을 보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ActivityManager2.png&quot; data-origin-width=&quot;1801&quot; data-origin-height=&quot;991&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bddi0g/btsFn8xP9JS/tBSSMkjx6tv9vOocPgwaiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bddi0g/btsFn8xP9JS/tBSSMkjx6tv9vOocPgwaiK/img.png&quot; data-alt=&quot;transition sequence&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bddi0g/btsFn8xP9JS/tBSSMkjx6tv9vOocPgwaiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbddi0g%2FbtsFn8xP9JS%2FtBSSMkjx6tv9vOocPgwaiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1801&quot; height=&quot;991&quot; data-filename=&quot;ActivityManager2.png&quot; data-origin-width=&quot;1801&quot; data-origin-height=&quot;991&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;transition sequence&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(RootWindowContainer의 openSurfaceTransaction 부분은 WindowManager 분석 때 SurfaceFlinger 관련 연결 부분과 한 번 본적있으니 이전 블로그 글 참고 부탁드립니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 좀 봐야할 부분은 AppTransitionController 부분입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기선 RemoteAnimation과 Leash라는 개념이 있는데 Leash는 사전적 의미를 보니 속박 이란 의미로 사용되는 것 같고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RemoteAnimation은 원격 애니메이션이라는 개념일듯 한데, 그걸 생각해보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SurfaceControl의 layer 전환에 따른 animation =&amp;gt; RemoteAnimation&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그 SurfaceControl에 속박된 effectLayer로 사용되는 layer =&amp;gt; Leash 로 추정이 됩니다&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// SurfaceAnimator::startAnimation
        if (mLeash == null) {
            mLeash = createAnimationLeash(mAnimatable, surface, t, type,
                    mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
                    0 /* y */, hidden, mService.mTransactionFactory);
            mAnimatable.onAnimationLeashCreated(t, mLeash);
        }
        mAnimatable.onLeashAnimationStarting(t, mLeash);
    
...

        mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 받는 mAnimation은 animationAdapter로 WindowContainer에서의 adapter (RemoteAnimationConroller.RemoteAnimationRecord) 입니다&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// SurfaceAnimator::startAnimator
    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
            @AnimationType int type,
            @Nullable OnAnimationFinishedCallback animationFinishedCallback,
            @Nullable Runnable animationCancelledCallback,
            @Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) {
        mAnimation = anim;
        

// WindowContainer::applyanimationUnchecked
        final Pair&amp;lt;AnimationAdapter, AnimationAdapter&amp;gt; adapters = getAnimationAdapter(lp,
                transit, enter, isVoiceInteraction);
        AnimationAdapter adapter = adapters.first;
        
        ...
        
             animationRunnerBuilder.build()
                    .startAnimation(getPendingTransaction(), adapter, !isVisible(),
                            ANIMATION_TYPE_APP_TRANSITION, thumbnailAdapter);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후 AppTransitionController에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// AppTransitionController::handleAppTransitionReady

        try {
            applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit,
                    animLp, voiceInteraction);
            handleClosingApps();
            handleOpeningApps();
            handleChangingApps(transit);

            appTransition.setLastAppTransition(transit, topOpeningApp,
                    topClosingApp, topChangingApp);

            final int flags = appTransition.getTransitFlags();
            layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
            handleNonAppWindowsInTransition(transit, flags);
            appTransition.postAnimationCallback();
            appTransition.clear();
        } finally {
            mService.mSurfaceAnimationRunner.continueStartingAnimations();
        }

        mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;handleClosingApps 에서는 일단 떠 있는 앱의 Visibility를 내리고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;handleOpeningApps에서는 실행시킬 앱의 Visibility를 올리고,&amp;nbsp; 실제로 보여줘야할 Window에 style, animationAttr이 설정되어 있는 경우 WindowStateAnimator를 실행시킵니다&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// AppTransitionController::handleOpeningApps
            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                    &quot;&amp;gt;&amp;gt;&amp;gt; OPEN TRANSACTION handleAppTransitionReady()&quot;);
            mService.openSurfaceTransaction();
            try {
                app.showAllWindowsLocked();
            } finally {
                mService.closeSurfaceTransaction(&quot;handleAppTransitionReady&quot;);
                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                        &quot;&amp;lt;&amp;lt;&amp;lt; CLOSE TRANSACTION handleAppTransitionReady()&quot;);
            }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후 appTransition.goodToGo를 실행시키고, 마무리로 displayContent에 pendingLayoutChanges를 업데이트 시킵니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 봐야하는 내용이 Leash 말고도 InsetsController를 통해 (앱 마다 상,하단바 설정에 따라 보여주고 말고를 결정할 수 있어서) 그 부분의 animation도 봐야하는데 내용이 너무 길어져서 일단 여기서 한 번 자르도록 하겠습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다!&lt;/p&gt;</description>
      <category>안드로이드/안드로이드 프레임워크</category>
      <author>냥냥냥냥냥냥</author>
      <guid isPermaLink="true">https://nyaang.tistory.com/35</guid>
      <comments>https://nyaang.tistory.com/35#entry35comment</comments>
      <pubDate>Fri, 1 Mar 2024 23:45:30 +0900</pubDate>
    </item>
    <item>
      <title>안드로이드 앱 프레임워크 학습(WindowManager) 3</title>
      <link>https://nyaang.tistory.com/34</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/33&quot;&gt;안드로이드 앱 프레임워크 학습(WindowManager) 2 (tistory.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709218170254&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;안드로이드 앱 프레임워크 학습(WindowManager) 2&quot; data-og-description=&quot;안드로이드 앱 프레임워크 학습(WindowManager) 1 (tistory.com) 안드로이드 앱 프레임워크 학습(WindowManager) 1 안드로이드 그래픽스 프레임워크 학습 2 (tistory.com) 안드로이드 그래픽스 프레임워크 학습 2 &quot; data-og-host=&quot;nyaang.tistory.com&quot; data-og-source-url=&quot;https://nyaang.tistory.com/33&quot; data-og-url=&quot;https://nyaang.tistory.com/33&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nurmf/hyVqidAcWl/6ke6JcNpAc4fB5uykFEh8k/img.png?width=800&amp;amp;height=365&amp;amp;face=0_0_800_365&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/33&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nyaang.tistory.com/33&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nurmf/hyVqidAcWl/6ke6JcNpAc4fB5uykFEh8k/img.png?width=800&amp;amp;height=365&amp;amp;face=0_0_800_365');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드 앱 프레임워크 학습(WindowManager) 2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드 앱 프레임워크 학습(WindowManager) 1 (tistory.com) 안드로이드 앱 프레임워크 학습(WindowManager) 1 안드로이드 그래픽스 프레임워크 학습 2 (tistory.com) 안드로이드 그래픽스 프레임워크 학습 2&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nyaang.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 번에 이어 이번엔 relayoutWindow 과정에 대해 알아보도록 하겠습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;relayout.png&quot; data-origin-width=&quot;2087&quot; data-origin-height=&quot;801&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxqeqC/btsFmiUT0jQ/hDWhYG1e8AOjD49KOLQ3xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxqeqC/btsFmiUT0jQ/hDWhYG1e8AOjD49KOLQ3xk/img.png&quot; data-alt=&quot;relayout&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxqeqC/btsFmiUT0jQ/hDWhYG1e8AOjD49KOLQ3xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxqeqC%2FbtsFmiUT0jQ%2FhDWhYG1e8AOjD49KOLQ3xk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2087&quot; height=&quot;801&quot; data-filename=&quot;relayout.png&quot; data-origin-width=&quot;2087&quot; data-origin-height=&quot;801&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;relayout&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 WindowSurfacePlacer 이후 부분의 코드는 이미 한 번 봤던 코드라..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중점으로 봐야할 것은 아래 코드 부분입니다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SurfaceControl&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// ViewRootImpl::relayoutWindow
int relayoutResult = mWindowSession.relayout(mWindow, params,
        (int) (mView.getMeasuredWidth() * appScale + 0.5f),
        (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
        insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
        mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, // mSurfaceControl
        mTempControls, mSurfaceSize);
        
        
...

        if (mSurfaceControl.isValid()) {
            if (!useBLAST()) {
                mSurface.copyFrom(mSurfaceControl);
            } else {
                final Surface blastSurface = getOrCreateBLASTSurface();
                // If blastSurface == null that means it hasn't changed since the last time we
                // called. In this situation, avoid calling transferFrom as we would then
                // inc the generation ID and cause EGL resources to be recreated.
                if (blastSurface != null) {
                    mSurface.transferFrom(blastSurface);
                }
            }
            if (mAttachInfo.mThreadedRenderer != null) {
                if (HardwareRenderer.isWebViewOverlaysEnabled()) {
                    addPrepareSurfaceControlForWebviewCallback();
                    addASurfaceTransactionCallback();
                }
                mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
            }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mSurfaceControl을 넘겨줘서 결국 WindowSurfaceController의 mSurfaceControl을 복사 받습니다&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// WindowSurfaceController
void getSurfaceControl(SurfaceControl outSurfaceControl) {
    outSurfaceControl.copyFrom(mSurfaceControl, &quot;WindowSurfaceController.getSurfaceControl&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후 valid한지 체크 한 후 useBlast라면 (현재 true로 설정 아래 참고)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HardwareRenderer에 surfaceControl을 set 해줍니다&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// ViewRootImpl
boolean useBLAST() {
    return mUseBLASTAdapter &amp;amp;&amp;amp; !mForceDisableBLAST;
}

// ViewRootImpl::setView
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
...
                if ((res &amp;amp; WindowManagerGlobal.ADD_FLAG_USE_BLAST) != 0) {
                    mUseBLASTAdapter = true;
                }



// WindowManagerService
        mUseBLAST = Settings.Global.getInt(resolver,
            Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_VR, 1) == 1;

// WindowManagerService::addWindow
    public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
            int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls) {
            
        ...
        
            if (mUseBLAST) {
                res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST;
            }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WindowManagerService에서 addWindow 시점에 ADD_FLAG_USE_BLAST set 해주고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ViewRootImpl 의 setView 시점에 해당 flag 있으면 true&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;// WindowManager&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) WindowManager에 addView를 하면 WindowManagerGlobal에서 addView할 때 넘겨준 View와, 그 View의 ViewRootImpl을 생성해서 저장한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) relayout 시점에 ViewRootImpl이 가지고 있던 surfaceControl에 WindowSurfaceController가 가지고 있는 surfaceControl의 값을 복사해준다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 그 이후 LayoutPolicy 적용 (statusbar, navigationbar 등의 inset 처리 등등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) 적용한 사항을 WindowContainer의 getPendingTransaction을 통해서 merge 시킨후 apply(closeSurfaceTransaction)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;----------------------------------- WindowManager &amp;lt;-&amp;gt; SurfaceFlinger 경계점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;// SurfaceFlinger&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5) 그러면 SurfaceComposerClient::Transaction::apply에서 surfaceFlinger로 setTransactionState&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6) SurfaceFlinger의 queueTransaction 호출 후 mTransactionQueue에 저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7) SurfaceFlinger의 flushTransactionQueues 호출 시점에 applyTransactionState&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8) output present 호출 시점에 관련된 transaction 정보들 composer로 전달 하고 compose&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 WindowManager는 window 관점 뿐만 아니라 activity의 state 변환에 따른 처리가 매우 많은데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 다음부터는 ActivityManager 관련 된 부분을 정리를 진행해보려고 합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다!&lt;/p&gt;</description>
      <category>안드로이드/안드로이드 프레임워크</category>
      <author>냥냥냥냥냥냥</author>
      <guid isPermaLink="true">https://nyaang.tistory.com/34</guid>
      <comments>https://nyaang.tistory.com/34#entry34comment</comments>
      <pubDate>Fri, 1 Mar 2024 01:25:43 +0900</pubDate>
    </item>
    <item>
      <title>안드로이드 앱 프레임워크 학습(WindowManager) 2</title>
      <link>https://nyaang.tistory.com/33</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/32&quot;&gt;안드로이드 앱 프레임워크 학습(WindowManager) 1 (tistory.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709204833722&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;안드로이드 앱 프레임워크 학습(WindowManager) 1&quot; data-og-description=&quot;안드로이드 그래픽스 프레임워크 학습 2 (tistory.com) 안드로이드 그래픽스 프레임워크 학습 2 안드로이드 그래픽스 프레임워크 학습 1 (tistory.com) 안드로이드 그래픽스 프레임워크 학습 1 이번에 &quot; data-og-host=&quot;nyaang.tistory.com&quot; data-og-source-url=&quot;https://nyaang.tistory.com/32&quot; data-og-url=&quot;https://nyaang.tistory.com/32&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bhjL1m/hyVuj9HDm2/Ni0b3U7dYwSXlAvYMsEkz1/img.png?width=800&amp;amp;height=251&amp;amp;face=0_0_800_251,https://scrap.kakaocdn.net/dn/cwI0tV/hyVqoY8e5n/zZC5zCIVqdmX1rokS2SdtK/img.png?width=800&amp;amp;height=251&amp;amp;face=0_0_800_251,https://scrap.kakaocdn.net/dn/es6XeZ/hyVujaM2I9/qsOp264ZHBZKNpofW2Q32K/img.png?width=1788&amp;amp;height=721&amp;amp;face=0_0_1788_721&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/32&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nyaang.tistory.com/32&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bhjL1m/hyVuj9HDm2/Ni0b3U7dYwSXlAvYMsEkz1/img.png?width=800&amp;amp;height=251&amp;amp;face=0_0_800_251,https://scrap.kakaocdn.net/dn/cwI0tV/hyVqoY8e5n/zZC5zCIVqdmX1rokS2SdtK/img.png?width=800&amp;amp;height=251&amp;amp;face=0_0_800_251,https://scrap.kakaocdn.net/dn/es6XeZ/hyVujaM2I9/qsOp264ZHBZKNpofW2Q32K/img.png?width=1788&amp;amp;height=721&amp;amp;face=0_0_1788_721');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드 앱 프레임워크 학습(WindowManager) 1&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드 그래픽스 프레임워크 학습 2 (tistory.com) 안드로이드 그래픽스 프레임워크 학습 2 안드로이드 그래픽스 프레임워크 학습 1 (tistory.com) 안드로이드 그래픽스 프레임워크 학습 1 이번에&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nyaang.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 블로그에서는 CreateLayer를 어떻게 불러주는 지에 대해서 확인을 했었습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 WindowManager에 관해서 좀 제대로 알아보려고 합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;WindowManagerService 생성&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1709206449010&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SystemServer::startOtherServices
            wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                    new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
                    
// WindowManagerService::main
    public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
            ActivityTaskManagerService atm, DisplayWindowSettingsProvider
            displayWindowSettingsProvider, Supplier&amp;lt;SurfaceControl.Transaction&amp;gt; transactionFactory,
            Supplier&amp;lt;Surface&amp;gt; surfaceFactory,
            Function&amp;lt;SurfaceSession, SurfaceControl.Builder&amp;gt; surfaceControlFactory) {
        DisplayThread.getHandler().runWithScissors(() -&amp;gt;
                sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
                        atm, displayWindowSettingsProvider, transactionFactory, surfaceFactory,
                        surfaceControlFactory), 0);
        return sInstance;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 WindowManager가 시작되는 위치는 SystemServer의 startOtherServies가 불릴 때 이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 때 ServiceManager에도 등록이 되어 아래 처럼 받아 쓸 수 있습니다&lt;/p&gt;
&lt;pre class=&quot;coq&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;&lt;code&gt;context.getSystemService(Context.WINDOW_SERVICE) as WindowManager&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단 WindowManager의 실제 구현은 WindowManagerImpl에 코드가 있으며, WindowManagerService를 wrapping 해놓은 식입니다&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// WindowManager
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {
...

// WindowManagerImpl
public final class WindowManagerImpl implements WindowManager {
    @UnsupportedAppUsage
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    
    
    ...
    
    @Override
    public void setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow) {
        try {
            WindowManagerGlobal.getWindowManagerService()   // windowManagerService
                    .setShouldShowWithInsecureKeyguard(displayId, shouldShow);
        } catch (RemoteException e) {
        }
    }    


// WindowManagerGlobal::getWindowManagerService
    @UnsupportedAppUsage
    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService(&quot;window&quot;));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                        sUseBLASTAdapter = sWindowManagerService.useBLAST();
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Window 관련 ClassDiagram&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Window.png&quot; data-origin-width=&quot;1521&quot; data-origin-height=&quot;695&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIyaTO/btsFqYgyeJC/mCxdOTW3XRKc0Q7ODqA4K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIyaTO/btsFqYgyeJC/mCxdOTW3XRKc0Q7ODqA4K0/img.png&quot; data-alt=&quot;classDiargram&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIyaTO/btsFqYgyeJC/mCxdOTW3XRKc0Q7ODqA4K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIyaTO%2FbtsFqYgyeJC%2FmCxdOTW3XRKc0Q7ODqA4K0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1521&quot; height=&quot;695&quot; data-filename=&quot;Window.png&quot; data-origin-width=&quot;1521&quot; data-origin-height=&quot;695&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;classDiargram&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 이것보다 훨씬 복잡하지만 일단 이렇게만 먼저 보겠습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 번 안드로이드 앱 프레임워크 학습(WindowManager) 1에서 WindowManager는 window 단위로 관리를 한다고 했지만 사실 엄밀히 말하면 WindowState단위로 관리가 됩니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PhoneWindow의 경우 여러 경우가 있지만, 간단히 생각하면 Activity에서 getWindow 할 때 불리는 Window이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Activity에서 setContentView를 호출해줄 때 사용되는 View가 PhoneWindow입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 다음에 다시 한 번 정리를 하겠습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 Activity를 사용하지 않고, 화면에 띄우기 위해 WindowManager에 addView를 호출해주는 상황의 sequence을 보면&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// WindowManagerGlobal::addView
root = new ViewRootImpl(view.getContext(), display);

// ViewRootImpl 생성자
    public ViewRootImpl(Context context, Display display) {
        this(context, display, WindowManagerGlobal.getWindowSession(),
                false /* useSfChoreographer */);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WindowManagerGlobal에 받은 View를 저장하고, ViewRootImpl을 만들어 받은 View와 Session을 set해줍니다&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// ViewRootImpl::setView
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
        getHostVisibility(), mDisplay.getDisplayId(), userId,
        mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
        mTempControls);
        
// Session::addToDisplayAsUser
    @Override
    public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls) {
        return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
                requestedVisibilities, outInputChannel, outInsetsState, outActiveControls);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ViewRootImpl에서는 받은 session에 addToDisplayAsUser를 호출해주고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 이것은 WindowManagerService의 addWindow를 호출을 해줍니다&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// WindowManagerService::addWindow
            final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
            
			...

            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
                    
            // token이 없으면 만들어줍니다
            
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], attrs, viewVisibility, session.mUid, userId,
                    session.mCanAddInternalSystemWindow);
                    
                    
  ...
  
            win.attach();
            mWindowMap.put(client.asBinder(), win);
            
            
          ...
          
            final WindowStateAnimator winAnimator = win.mWinAnimator;
            winAnimator.mEnterAnimationPending = true;
            winAnimator.mEnteringAnimation = true;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 sequence diagram으로 표현한 것이 아래입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AddView.png&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/skfTq/btsFkXDF25l/27JzXFJ2v6IL1Zadhc50VK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/skfTq/btsFkXDF25l/27JzXFJ2v6IL1Zadhc50VK/img.png&quot; data-alt=&quot;addView sequence&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/skfTq/btsFkXDF25l/27JzXFJ2v6IL1Zadhc50VK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FskfTq%2FbtsFkXDF25l%2F27JzXFJ2v6IL1Zadhc50VK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1070&quot; height=&quot;461&quot; data-filename=&quot;AddView.png&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;461&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;addView sequence&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 위의 diagram에서 ViewRootImpl내의 requestLayout시점에 performTraversal을 통해&lt;/p&gt;
&lt;pre id=&quot;code_1709213758009&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ViewRootImpl::performTraversal
    private void performTraversals() {
    ...
    
                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
                
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;relayoutWindow를 호출해주고 타고 타고 가면, windowManager의 relayoutWindow를 호출 해줍니다&lt;/p&gt;
&lt;pre class=&quot;java&quot; style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// WindowMangerService::relayoutWindow
// Create surfaceControl before surface placement otherwise layout will be skipped
// (because WS.isGoneForLayout() is true when there is no surface.
if (shouldRelayout) {
    try {
        result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
    } catch (Exception e) {
        displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);

        ProtoLog.w(WM_ERROR,
                &quot;Exception thrown when creating surface for client %s (%s). %s&quot;,
                client, win.mAttrs.getTitle(), e);
        Binder.restoreCallingIdentity(origId);
        return 0;
    }
}

// We may be deferring layout passes at the moment, but since the client is interested
// in the new out values right now we need to force a layout.
mWindowPlacerLocked.performSurfacePlacement(true /* force */);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 과정도 꽤 복잡하고, 이 과정들로 인해서 SurfaceFlinger에서 사용되는 layer를 SurfaceControl transaction을 통해 업데이트를 해주는 과정이 일어나게 되는데, 이건 relayout 과정으로 3편에서 따로 분리하여 좀 더 보도록 하겠습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다!&lt;/p&gt;</description>
      <category>안드로이드/안드로이드 프레임워크</category>
      <author>냥냥냥냥냥냥</author>
      <guid isPermaLink="true">https://nyaang.tistory.com/33</guid>
      <comments>https://nyaang.tistory.com/33#entry33comment</comments>
      <pubDate>Thu, 29 Feb 2024 22:24:03 +0900</pubDate>
    </item>
    <item>
      <title>안드로이드 앱 프레임워크 학습(WindowManager) 1</title>
      <link>https://nyaang.tistory.com/32</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/30&quot;&gt;안드로이드 그래픽스 프레임워크 학습 2 (tistory.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709121683496&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;안드로이드 그래픽스 프레임워크 학습 2&quot; data-og-description=&quot;안드로이드 그래픽스 프레임워크 학습 1 (tistory.com) 안드로이드 그래픽스 프레임워크 학습 1 이번에 이 글을 작성하게 되는 이유는 위의 Android 공식 홈페이지의 기술되어 있는 부분들에 대해 코&quot; data-og-host=&quot;nyaang.tistory.com&quot; data-og-source-url=&quot;https://nyaang.tistory.com/30&quot; data-og-url=&quot;https://nyaang.tistory.com/30&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dvo3q8/hyVuqng9mw/d0q4ZVo3ZXPuHMtFkoaM50/img.png?width=591&amp;amp;height=465&amp;amp;face=0_0_591_465,https://scrap.kakaocdn.net/dn/3lvIX/hyVukgh0E3/J7fUBWjFGdrwpPrL0TrxSk/img.png?width=591&amp;amp;height=465&amp;amp;face=0_0_591_465,https://scrap.kakaocdn.net/dn/7ykqG/hyVqrIaZUx/M69PfWHCxcsXe5hUkg9cPK/img.png?width=1308&amp;amp;height=351&amp;amp;face=0_0_1308_351&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/30&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nyaang.tistory.com/30&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dvo3q8/hyVuqng9mw/d0q4ZVo3ZXPuHMtFkoaM50/img.png?width=591&amp;amp;height=465&amp;amp;face=0_0_591_465,https://scrap.kakaocdn.net/dn/3lvIX/hyVukgh0E3/J7fUBWjFGdrwpPrL0TrxSk/img.png?width=591&amp;amp;height=465&amp;amp;face=0_0_591_465,https://scrap.kakaocdn.net/dn/7ykqG/hyVqrIaZUx/M69PfWHCxcsXe5hUkg9cPK/img.png?width=1308&amp;amp;height=351&amp;amp;face=0_0_1308_351');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드 그래픽스 프레임워크 학습 2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드 그래픽스 프레임워크 학습 1 (tistory.com) 안드로이드 그래픽스 프레임워크 학습 1 이번에 이 글을 작성하게 되는 이유는 위의 Android 공식 홈페이지의 기술되어 있는 부분들에 대해 코&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nyaang.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 분석에서 SurfaceFlinger의 createLayer를 통해 SurfaceFlinger에 layer가 추가 되는 과정을 알아 봤었습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 그럼, createLayer를 해주는 부분이 어디인지를 한 번 알아보는 것이 핵심입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, createLayer 시작점을 보기 전에 WindowManager라는 개념을 먼저 알아야 합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;native 단의(여기서 native는 c, c++) SurfaceFlinger는 layer 단위로 관리를 하고 있고&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;java 단의 WindowManager는 window 단위로 관리를 합니다&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;둘 사이는 SurfaceControl.Transaction 이라는 것을 통해 통신을 하고,&amp;nbsp; 그 Transaction을 가지고 있는 것은 DisplayContent입니다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드에서는 display별로 DisplayContent를 가지게 되고, 그 displayContent는 RootWindowContainer의 child들로 관리가 됩니다&lt;/p&gt;
&lt;pre id=&quot;code_1709123710548&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. WindowManagerService의 RootWindowContainer
    // The root of the device window hierarchy.
    RootWindowContainer mRoot;
    
 // 2. RootWindowContainer 상속 관계
 class RootWindowContainer extends WindowContainer&amp;lt;DisplayContent&amp;gt;
 
 // 3. RootWindowContainer::SetWindowManager
     void setWindowManager(WindowManagerService wm) {
        mWindowManager = wm;
        ...


        final Display[] displays = mDisplayManager.getDisplays();
        for (int displayNdx = 0; displayNdx &amp;lt; displays.length; ++displayNdx) {
            final Display display = displays[displayNdx];
            final DisplayContent displayContent = new DisplayContent(display, this);
            addChild(displayContent, POSITION_BOTTOM);  // addChild
            if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
                mDefaultDisplay = displayContent;
            }
        }
        
 // 4. WindowContainer::addChild
     /** Adds the input window container has a child of this container at the input index. */
    @CallSuper
    void addChild(E child, int index) {
        if (!child.mReparenting &amp;amp;&amp;amp; child.getParent() != null) {
            throw new IllegalArgumentException(&quot;addChild: container=&quot; + child.getName()
                    + &quot; is already a child of container=&quot; + child.getParent().getName()
                    + &quot; can't add to container=&quot; + getName()
                    + &quot;\n callers=&quot; + Debug.getCallers(15, &quot;\n&quot;));
        }

        if ((index &amp;lt; 0 &amp;amp;&amp;amp; index != POSITION_BOTTOM)
                || (index &amp;gt; mChildren.size() &amp;amp;&amp;amp; index != POSITION_TOP)) {
            throw new IllegalArgumentException(&quot;addChild: invalid position=&quot; + index
                    + &quot;, children number=&quot; + mChildren.size());
        }

        if (index == POSITION_TOP) {
            index = mChildren.size();
        } else if (index == POSITION_BOTTOM) {
            index = 0;
        }

        mChildren.add(index, child);

        // Set the parent after we've actually added a child in case a subclass depends on this.
        child.setParent(this);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2번을 보시면 RootWindowContainer는 WindowContainer를 상속받는데 제네릭으로 DisplayContent 형식을 받는 것을 볼 수 있습니다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3번을 보시면 setWindowManager 호출 시에, addChild를 호출 해주는 것을 볼 수있고&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4번을 보시면 addChild의 구현부분 입니다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음 궁금했던, createLayer가 불리기까지의 sequence는 아래와 같습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;createLayerSequence.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;411&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciTVnY/btsFkJZoaMq/D0MDQDKKyLLA5tVbubUhrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciTVnY/btsFkJZoaMq/D0MDQDKKyLLA5tVbubUhrK/img.png&quot; data-alt=&quot;createLayer 불리기 까지 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciTVnY/btsFkJZoaMq/D0MDQDKKyLLA5tVbubUhrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciTVnY%2FbtsFkJZoaMq%2FD0MDQDKKyLLA5tVbubUhrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;411&quot; data-filename=&quot;createLayerSequence.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;411&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;createLayer 불리기 까지 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서, SurfaceFlinger &amp;lt;-&amp;gt; WindowManager 간의 통신은 Transaction을 통해서 이루어 진다고 얘기를 했는데, 그 부분에 대해서 좀 알아보려고 합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SurfaceFlinger2.png&quot; data-origin-width=&quot;1788&quot; data-origin-height=&quot;721&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zuSpK/btsFp84tJg2/mkWXQlMyr1ThaggFjgqiwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zuSpK/btsFp84tJg2/mkWXQlMyr1ThaggFjgqiwK/img.png&quot; data-alt=&quot;SetTransactionState까지의 sequence&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zuSpK/btsFp84tJg2/mkWXQlMyr1ThaggFjgqiwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzuSpK%2FbtsFp84tJg2%2FmkWXQlMyr1ThaggFjgqiwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1788&quot; height=&quot;721&quot; data-filename=&quot;SurfaceFlinger2.png&quot; data-origin-width=&quot;1788&quot; data-origin-height=&quot;721&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SetTransactionState까지의 sequence&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709125918954&quot; class=&quot;arduino&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// 1. RootWindowContainer::performSurfacePlacementNoTrace
    void performSurfacePlacementNoTrace() {
    ...
        mWmService.openSurfaceTransaction();
        try {
            applySurfaceChangesTransaction();
        } catch (RuntimeException e) {
            Slog.wtf(TAG, &quot;Unhandled exception in Window Manager&quot;, e);
        } finally {
            mWmService.closeSurfaceTransaction(&quot;performLayoutAndPlaceSurfaces&quot;);
            ...
        }
        
// 2. WindowManagerService::opensurfaceTranaction, closeSurfaceTransaction
    void openSurfaceTransaction() {
        try {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, &quot;openSurfaceTransaction&quot;);
            SurfaceControl.openTransaction();
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

    /**
     * Closes a surface transaction.
     * @param where debug string indicating where the transaction originated
     */
    void closeSurfaceTransaction(String where) {
        try {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, &quot;closeSurfaceTransaction&quot;);
            SurfaceControl.closeTransaction();
            mWindowTracing.logState(where);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }


  // 3. SurfaceComposerClient::Transaction::merge
SurfaceComposerClient::Transaction&amp;amp; SurfaceComposerClient::Transaction::merge(Transaction&amp;amp;&amp;amp; other) {
    for (auto const&amp;amp; [handle, composerState] : other.mComposerStates) {
        if (mComposerStates.count(handle) == 0) {
            mComposerStates[handle] = composerState;
        } else {
            mComposerStates[handle].state.merge(composerState.state);
        }
    }

    for (auto const&amp;amp; state : other.mDisplayStates) {
        ssize_t index = mDisplayStates.indexOf(state);
        if (index &amp;lt; 0) {
            mDisplayStates.add(state);
        } else {
            mDisplayStates.editItemAt(static_cast&amp;lt;size_t&amp;gt;(index)).merge(state);
        }
    }  
  
  // 4. SurfaceComposerClient::Transaction::apply
  status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
  ...
  
    Vector&amp;lt;ComposerState&amp;gt; composerStates;
    Vector&amp;lt;DisplayState&amp;gt; displayStates;

   ...

    for (auto const&amp;amp; kv : mComposerStates){
        composerStates.add(kv.second);
    }
    
    ...
    
    sf-&amp;gt;setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
                            mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
                            {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
                            hasListenerCallbacks, listenerCallbacks, mId);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번을 보시면 performSurfacePlacement 함수 내부의 performSurfacePlacementNoTrace 부분에 대한 설명입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;performSurfacePlacement는 activity 전환 등 많은 경우에 불릴 수 있습니다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;2번은 openSurfaceTransaction, closeSurfaceTransaction 부분의 코드 입니다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;3번과 4번은 각각 openSurfaceTransaction, closeSurfaceTransaction시의 주요 사항 부분에 대한 코드 입니다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openSurfaceTransaction 일 때, SurfaceComposerClient::Transaction::merge가 불려서 mComposerStates와 mDisplayStates를 업데이트 시켜주고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;closeSurfaceTransaction이 불릴 때 SurfaceComposerClient::Transaction::apply가 불려서 SurfaceFlinger의 setTransactionState가 불리어 TransactionState를 넘겨줍니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후 SurfaceFlinger에서 &lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;queueTransaction를 통해 transaction을 저장했던 것을 &lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;flushTransactionQueues 시점에 업데이트 해줍니다&amp;nbsp; 자세한 사항은 지난 번에 썼던 아래 부분과 연계가 되니 궁금하신 분은 확인 부탁드리겠습니다&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/30&quot;&gt;안드로이드 그래픽스 프레임워크 학습 2 (tistory.com)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709125601386&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;안드로이드 그래픽스 프레임워크 학습 2&quot; data-og-description=&quot;안드로이드 그래픽스 프레임워크 학습 1 (tistory.com) 안드로이드 그래픽스 프레임워크 학습 1 이번에 이 글을 작성하게 되는 이유는 위의 Android 공식 홈페이지의 기술되어 있는 부분들에 대해 코&quot; data-og-host=&quot;nyaang.tistory.com&quot; data-og-source-url=&quot;https://nyaang.tistory.com/30&quot; data-og-url=&quot;https://nyaang.tistory.com/30&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dvo3q8/hyVuqng9mw/d0q4ZVo3ZXPuHMtFkoaM50/img.png?width=591&amp;amp;height=465&amp;amp;face=0_0_591_465,https://scrap.kakaocdn.net/dn/3lvIX/hyVukgh0E3/J7fUBWjFGdrwpPrL0TrxSk/img.png?width=591&amp;amp;height=465&amp;amp;face=0_0_591_465,https://scrap.kakaocdn.net/dn/7ykqG/hyVqrIaZUx/M69PfWHCxcsXe5hUkg9cPK/img.png?width=1308&amp;amp;height=351&amp;amp;face=0_0_1308_351&quot;&gt;&lt;a href=&quot;https://nyaang.tistory.com/30&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nyaang.tistory.com/30&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dvo3q8/hyVuqng9mw/d0q4ZVo3ZXPuHMtFkoaM50/img.png?width=591&amp;amp;height=465&amp;amp;face=0_0_591_465,https://scrap.kakaocdn.net/dn/3lvIX/hyVukgh0E3/J7fUBWjFGdrwpPrL0TrxSk/img.png?width=591&amp;amp;height=465&amp;amp;face=0_0_591_465,https://scrap.kakaocdn.net/dn/7ykqG/hyVqrIaZUx/M69PfWHCxcsXe5hUkg9cPK/img.png?width=1308&amp;amp;height=351&amp;amp;face=0_0_1308_351');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드 그래픽스 프레임워크 학습 2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드 그래픽스 프레임워크 학습 1 (tistory.com) 안드로이드 그래픽스 프레임워크 학습 1 이번에 이 글을 작성하게 되는 이유는 위의 Android 공식 홈페이지의 기술되어 있는 부분들에 대해 코&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nyaang.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 SurfaceFlinger &amp;lt;-&amp;gt; WindowManger간의 통신은 어떻게 이루어 지는지, SurfaceFlinger의 createLayer는 누가 해주는 지를 분석해보았습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 번엔 WindowManager에 대해서 좀 더 자세하게 분석 해보도록 하겠습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다&lt;/p&gt;</description>
      <category>안드로이드/안드로이드 프레임워크</category>
      <category>createLayer</category>
      <category>WindowManager와 SurfaceFlinger</category>
      <category>안드로이드프레임워크</category>
      <author>냥냥냥냥냥냥</author>
      <guid isPermaLink="true">https://nyaang.tistory.com/32</guid>
      <comments>https://nyaang.tistory.com/32#entry32comment</comments>
      <pubDate>Wed, 28 Feb 2024 21:00:29 +0900</pubDate>
    </item>
  </channel>
</rss>