{"id":2008,"date":"2023-12-15T23:44:57","date_gmt":"2023-12-15T14:44:57","guid":{"rendered":"http:\/\/batmask.dothome.co.kr\/?p=2008"},"modified":"2023-12-15T23:44:57","modified_gmt":"2023-12-15T14:44:57","slug":"android-external-storage-and-storage-access-frameworksaf","status":"publish","type":"post","link":"http:\/\/batmask.net\/index.php\/2023\/12\/15\/2008\/","title":{"rendered":"Android: External Storage and Storage Access Framework(SAF)"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\">\uac1c\uc694<\/h3>\n\n\n\n<p> SD Card \ub610\ub294 External Storage\uc5d0 \ub300\ud574 \uac00\ubccd\uac8c \uc811\uadfc\ud588\ub2e4\uac00 \ub300\ud63c\ub3c8\uc774\ub77c\ub294\uac78 \ub4a4\ub2a6\uac8c \uc54c\uc558\ub2e4. \ub0b4\ub9d8\uac19\uc544\uc120, \uadf8\ub0e5 \uae30\uc874\ucc98\ub7fc \uc790\uc720\ub86d\uac8c \uc811\uadfc\ud558\uac8c \ub0c5\ub450\uba74 \uc88b\uaca0\uc9c0\ub9cc&#8230; <strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">\ubaa8\ub4e0 \ubcc0\ud654\uc758 \ud575\uc2ec\uc740 Internal Storage \uc218\uc900\uc758 \ubcf4\uc548\uacfc \uad8c\ud55c(Permission)\uc774\ub2e4.<\/mark><\/strong> \uc0ac\uc871\uc744 \ub2ec\uc790\uba74, \ub300\ubd80\ubd84 \uc571\ub4e4\uc740 \uc544\uc774\ud3f0\uc6a9\uc73c\ub85c\ub3c4 \uac1c\ubc1c\ub418\uae30 \ub54c\ubb38\uc5d0 \uc774 \uace8\uce58\uc544\ud508 External Storage\ub97c \ub2e4\ub8e8\uc9c0 \uc54a\uace0 \uc804\ubd80 Internal Storage\ub9cc \uc0ac\uc6a9\ud55c\ub2e4, \uce74\uce74\uc624 \ud1a1\ucc98\ub7fc. \ub0b4\uc6a9\uc744 \ubcfc\uc218\ub85d \uc774\uac83\ub3c4 \uc774\ud574\uac00 \ub418\uae34 \ud558\ub294\ub370, \uc548\ub4dc\ub85c\uc774\ub4dc \uc720\uc800\ub85c\uc120 \uadf8\ub0e5 \uac8c\uc73c\ub974\uace0 \ubb34\ucc45\uc784\ud55c\uac8c \uc544\ub2cc\uac00\ub3c4 \uc0dd\uac01\uc774&#8230; \u314b<\/p>\n\n\n\n<p> Android 10 \uc774\uc804\uae4c\uc9c0\ub294 \uadf8\ub0e5 READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE \uad8c\ud55c\ub9cc \uac00\uc9c0\uace0 \ub2e4\uc74c\uc758 API\ub85c \ub514\ub809\ud1a0\ub9ac\ub97c \uc5bb\uc5b4\uc624\uba74, \uacf5\uc720 \uc601\uc5ed\uc73c\ub85c\uc11c External Storage\uc5d0 \uc811\uadfc\ud574 \uc790\uc720\ub86d\uac8c \uc0ac\uc6a9\ud574\uc654\ub2e4. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"kotlin\" class=\"language-kotlin\">Environment.getExternalStorageDirectory()<\/code><\/pre>\n\n\n\n<p>Android 10\ubd80\ud130, Scoped Storage\ub77c\ub294 \uac1c\ub150\uc774 \ub4e4\uc5b4\uc624\uba74\uc11c \uc774\uc804\ubc29\uc2dd\uc744 \uc0ac\uc6a9\ud558\uae30 \uc704\ud574\uc120, \ub2e4\uc74c\uc744 \uba85\uc2dc\ud574\uc918\uc57c \ud588\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"kotlin\" class=\"language-kotlin\">&lt;application android:requestLegacyExternalStorage=\"true\" ... &gt;\n    ...\n  &lt;\/application&gt;\n<\/code><\/pre>\n\n\n\n<p>\uc774\uc870\ucc28\ub3c4 Android 11\ubd80\ud130\ub294 \uc0ac\uc6a9\ud560 \uc218\uac00 \uc5c6\ub2e4. \ub354\uc774\uc0c1\uc740 External Storage\uc5d0 \uc790\uc720\ub85c\uc6b4 \uc811\uadfc\uc740 \ubd88\uac00\ub2a5\ud558\ub2e4. <\/p>\n\n\n\n<p> Scoped Storage\uac00 \uc801\uc6a9\ub418\uba74\uc11c \ubcc0\ud654\ud588\ub2e4\uace0 \uc598\uae30\ud588\ub294\ub370, \uc774\uac83\uc740 \uac04\ub2e8\ud558\uac8c External Storage\uc5d0\uc11c\ub3c4 Internal Storage\ucc98\ub7fc \ub2e4\ub978\uc571\uc740 \uc811\uadfc \ubabb\ud558\ub294 <a href=\"https:\/\/developer.android.com\/training\/data-storage\/app-specific\">App specific storage<\/a>\ub97c \uc0ac\uc6a9\ud55c\ub2e4\ub294 \uc598\uae30\ub2e4. Internal storage\uc5d0\uc11c context.filesDir \ub85c \uacbd\ub85c\ub97c \uac00\uc838\uc624\ub4ef\uc774, <a href=\"https:\/\/developer.android.com\/reference\/android\/content\/Context.html#getExternalFilesDir(java.lang.String)\">ContextCompat.getExternalFilesDir()<\/a>, <a href=\"https:\/\/developer.android.com\/reference\/androidx\/core\/content\/ContextCompat#getExternalFilesDirs(android.content.Context,%20java.lang.String)\">ContextCompat.getExternalFilesDirs()<\/a> \ub97c \uc774\uc6a9\ud55c\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"kotlin\" class=\"language-kotlin\">ContextCompat.getExternalFilesDir(type)\nContextCompat.getExternalFilesDirs(applicationContext, type)<\/code><\/pre>\n\n\n\n<p>type\uc740 root\ub294 null\uc744 \ub123\uc5b4 \uc5bb\uc5b4\uc62c \uc218 \uc788\uace0, \uc11c\ube0c\ub514\ub809\ud1a0\ub9ac\ub294 <a href=\"https:\/\/developer.android.com\/reference\/android\/os\/Environment#DIRECTORY_MUSIC\">Environment.DIRECTORY_MUSIC<\/a> \uacfc \uac19\uc740 \ud30c\ub77c\ubbf8\ud130\ub97c \ub123\uc5b4\uc8fc\uba74 \ub41c\ub2e4. Scoped Storage\ub294 \uc571\uc5d0 \uadc0\uc18d\ub41c \uc601\uc5ed\uc774\uae30 \ub54c\ubb38\uc5d0, Internal Storage\uac00 \uadf8\ub807\ub4ef \uc571\uc744 \uc0ad\uc81c\ud558\uba74 \uac19\uc774 \uc0ac\ub77c\uc9c4\ub2e4\uace0 \ud55c\ub2e4. <\/p>\n\n\n\n<p>\uadf8\ub807\ub2e4\uba74, External Storage\ub97c Scoped Storage\ub85c \ubc16\uc5d0 \uc0ac\uc6a9\ud558\uc9c0 \ubabb\ud558\ub098 \uc0dd\uac01\uc774 \ub4e0\ub2e4. \uadf8\ub807\uc9c0\ub294 \uc54a\uace0 \uae30\uc874 Internal Storage\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 Media store\uae30\ub2a5\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. \uc774\ub807\uac8c\ud558\uba74, \uacf5\uc6a9\uc601\uc5ed\uc5d0\uc11c <a href=\"https:\/\/developer.android.com\/training\/data-storage\/shared\/media\">Images, Videos, Audio files, Download files\ub4f1\uc758 \uacf5\uac04\uc73c\ub85c\uc11c \uc0ac\uc6a9\uac00\ub2a5<\/a>\ud558\ub2e4. \ub2e4\ub9cc, \uacf5\uc6a9\uc601\uc5ed\uc73c\ub85c \ub2e4\ub978 \uc571\uc774 \uc0dd\uc131\ud55c \ubbf8\ub514\uc5b4 \ud30c\uc77c\uc5d0 \uc811\uadfc\ud558\uae30 \uc704\ud574, Android 13\ubd80\ud130\ub294 \ub2e4\uc74c \uad8c\ud55c\uc744 \ubcc4\ub3c4\ub85c \ud544\uc694\ub85c \ud55c\ub2e4. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml\">&lt;!-- Required only if your app needs to access images or photos\n     that other apps created. --&gt;\n&lt;uses-permission android:name=\"android.permission.READ_MEDIA_IMAGES\" \/&gt;\n\n&lt;!-- Required only if your app needs to access videos\n     that other apps created. --&gt;\n&lt;uses-permission android:name=\"android.permission.READ_MEDIA_VIDEO\" \/&gt;\n\n&lt;!-- Required only if your app needs to access audio files\n     that other apps created. --&gt;\n&lt;uses-permission android:name=\"android.permission.READ_MEDIA_AUDIO\" \/&gt;\n\n&lt;uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"\/&gt;\n\n&lt;uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"\n                 android:maxSdkVersion=\"29\" \/&gt;\n<\/code><\/pre>\n\n\n\n<p>\ubbf8\ub514\uc5b4 \ubcc4\ub85c \uad8c\ud55c\uc774 \ub530\ub85c \uc0dd\uae34\uac70 \uc678\uc5d0, \uc5ec\uc804\ud788 READ_EXTERNAL_STORAGE\uac00 \uc0ac\uc6a9\ub41c\ub2e4\ub294\uac78 \uc8fc\uc758\ud558\uc790. \uad00\ub828\ub41c \ub0b4\uc6a9\uc740 <a href=\"https:\/\/developer.android.com\/training\/data-storage\/shared\/media\">\uacf5\uc2dd\ubb38\uc11c<\/a>\uc5d0\uc11c \ud655\uc778 \uac00\ub2a5\ud558\ub2e4. <\/p>\n\n\n\n<p>\uc544\ub2c8 \uadf8\ub7fc, External Storage\ub97c \uc774\ub807\uac8c Internal Storage\uc815\ub3c4\ub85c\ub9cc \uc0ac\uc6a9\uc774 \uac00\ub2a5\ud55c\uac70\ub0d0 \uc5ec\uc804\ud788 \uc0dd\uac01\uc774 \ub4e0\ub2e4. \uc774\ub97c \uc704\ud574 \uc874\uc7ac\ud558\ub294\uac8c Storage Access Framework(SAF)\uc774\ub2e4. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Storage Access Framework(SAF)<\/h3>\n\n\n\n<p>\ub180\ub78d\uac8c\ub3c4 Storage Access Framework(\uc774\ud558 SAF)\ub294 \uc548\ub4dc\ub85c\uc774\ub4dc 4.4\ub54c\ubd80\ud130 \uc874\uc7ac\ud574 \uc654\ub2e4. \uc774 \uc815\uccb4\uac00 \uadf8\ub0e5 \ucd94\uc0c1\uc801\uc778 \uc800\uc7a5\uc18c\uc758 Document File\ub4e4\uc5d0 \ub300\ud55c Content Provider\uc774\uae30 \ub54c\ubb38\uc774\ub2e4(Documents Provider). \ucd94\uc0c1\uc801\uc778 \uc800\uc7a5\uc18c\ub77c \ud568\uc740 Internal Storage\uc640 cloud storage\uc5d0\ub3c4 \uc0ac\uc6a9\uc774 \uac00\ub2a5\ud558\uae30 \ub54c\ubb38\uc774\ub2e4. External Storage\uc5d0 \uc811\uadfc\uc774 \uc81c\ud55c\ub418\ub294 Android 10, 11\ubd80\ud130\ub294 \uc774\ub97c \ud1b5\ud574, External Storage\uc758 \uacf5\uc6a9 \uc800\uc7a5\uacf5\uac04(Shared Storage)\uc5d0 \uc811\uadfc\ud560 \uc218 \uc788\ub2e4.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"578\" height=\"355\" src=\"http:\/\/batmask.dothome.co.kr\/wordpress\/wp-content\/uploads\/2023\/11\/storage_dataflow.png\" alt=\"Storage Access Framework Flow\" class=\"wp-image-2163\" srcset=\"http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_dataflow.png 578w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_dataflow-300x184.png 300w\" sizes=\"auto, (max-width: 578px) 100vw, 578px\" \/><figcaption class=\"wp-element-caption\">Storage Access Framework Flow<\/figcaption><\/figure>\n<\/div>\n\n\n<p>SAF\uc5d0\uc11c \uc571\uc740 \uc9c1\uc811 \ud30c\uc77c \uc5d1\uc138\uc2a4\ub97c \ud558\uc9c0 \uc54a\ub294\ub2e4. Documents Provider\uc758 Client\ub85c\uc11c Provider\uc5d0\uac8c \ud30c\uc77c\uc5d0 \ub300\ud55c read, edit, create, delete \uad8c\ud55c\uc744 \uc694\uccad\ud574\uc11c \uc0ac\uc6a9\ud55c\ub2e4. <\/p>\n\n\n\n<p>Documents Provider\ub3c4 Contents Provider\uc774\uae30 \ub54c\ubb38\uc5d0, Intent\ub97c \ub0a0\ub824 \uc694\uccad\uc744 \ud55c\ub2e4. \uc0ac\uc6a9\ub418\ub294 \uc778\ud150\ud2b8\ub294 <a href=\"https:\/\/developer.android.com\/reference\/android\/content\/Intent#ACTION_OPEN_DOCUMENT\">ACTION_OPEN_DOCUMENT<\/a>, <a href=\"https:\/\/developer.android.com\/reference\/android\/content\/Intent#ACTION_CREATE_DOCUMENT\">ACTION_CREATE_DOCUMENT<\/a>, <a href=\"https:\/\/developer.android.com\/reference\/android\/content\/Intent#ACTION_OPEN_DOCUMENT_TREE\">ACTION_OPEN_DOCUMENT_TREE<\/a> \uc138\uac00\uc9c0\uc774\ub2e4. \uc774 \uc778\ud150\ud2b8\uc5d0\ub294 MIME type\uc744 \uc774\uc6a9\ud558\uc5ec \uc6d0\ud558\ub294 \ud30c\uc77c \ud0c0\uc785\uc744 \ud544\ud130\ub9c1\ud560 \uc218 \uc788\ub2e4. \uc774 \uc778\ud150\ud2b8\ub4e4\uc744 \ub0a0\ub9ac\uac8c \ub418\uba74, \ud30c\uc77c\uc774\ub098 \ub514\ub809\ud1a0\ub9ac\ub97c \uc120\ud0dd\ud558\ub294 \uc2dc\uc2a4\ud15c\uc774 \uc81c\uacf5\ud558\ub294 Picker UI\uac00 \ub728\uac8c\ub41c\ub2e4. \ub2e4\uc74c \uc774\ubbf8\uc9c0\ub294 photo \ud30c\uc77c\ub85c MIME type\uc744 \uc124\uc815\ud55c ACTION_OPEN_DOCUMENT \uc778\ud150\ud2b8\ub97c \ubcf4\ub0b4\uc11c \ud30c\uc77c\uc744 \uc5ec\ub294 \uc608\uc774\ub2e4.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"560\" height=\"1024\" src=\"http:\/\/batmask.dothome.co.kr\/wordpress\/wp-content\/uploads\/2023\/11\/storage_picker-560x1024.png\" alt=\"\" class=\"wp-image-2171\" style=\"width:300px\" srcset=\"http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_picker-560x1024.png 560w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_picker-164x300.png 164w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_picker-768x1403.png 768w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_picker-841x1536.png 841w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_picker-1121x2048.png 1121w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_picker.png 1152w\" sizes=\"auto, (max-width: 560px) 100vw, 560px\" \/><\/figure>\n<\/div>\n\n\n<p>UI\ubaa8\uc591\uc740 \uc2dc\uc2a4\ud15c\uc5d0 \ub530\ub77c \ub2e4\ub97c \uc218 \uc788\ub294\ub370, \uba3c\uc800 \ub514\ub809\ud1a0\ub9ac\ub97c \uc120\ud0dd\ud558\ub294 UI\uac00 \ub72c\ub2e4. \uc704 \uc774\ubbf8\uc9c0\ub97c \ubcf4\uba74, SDCARD\ub3c4 \ubcf4\uc774\uace0 \uad6c\uae00 \ub4dc\ub77c\uc774\ube0c\ub3c4 \uc120\ud0dd\ud560 \uc218 \uc788\ub294\uac8c \ubcf4\uc778\ub2e4. \uc5ec\uae30\uc11c Downlaods\ub97c \uc120\ud0dd\ud588\ub2e4\uace0 \uac00\uc815\ud558\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \ud30c\uc77c Picker\ub97c \ubcfc \uc218 \uc788\ub2e4.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"560\" height=\"1024\" src=\"http:\/\/batmask.dothome.co.kr\/wordpress\/wp-content\/uploads\/2023\/11\/storage_photos-560x1024.png\" alt=\"\" class=\"wp-image-2172\" style=\"width:300px\" srcset=\"http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_photos-560x1024.png 560w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_photos-164x300.png 164w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_photos-768x1403.png 768w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_photos-841x1536.png 841w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_photos-1121x2048.png 1121w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2023\/11\/storage_photos.png 1152w\" sizes=\"auto, (max-width: 560px) 100vw, 560px\" \/><\/figure>\n<\/div>\n\n\n<p>\uc5ec\uae30\uc11c \ud30c\uc77c\uc744 \uc120\ud0dd\ud558\uba74 \uc571\uc5d0 \ud574\ub2f9 \ud30c\uc77c\uc5d0 \ub300\ud55c Uri\uac00 \ub9ac\ud134\ub418\ub294 \ubc29\uc2dd\uc774\ub2e4. ACTION_CREATE_DOCUMENT\uc758 \uacbd\uc6b0\uc5d0\ub3c4 \ud30c\uc77c\uc744 \uc5b4\ub514\uc5d0 \uc0dd\uc131\ud560\uc9c0 \ub514\ub809\ud1a0\ub9ac \uc120\ud0dd\ucc3d\uc774 \ub728\uac8c\ub418\uace0, ACTION_OPEN_DOCUMENT_TREE\uc758 \uacbd\uc6b0\uc5d0\ub3c4 \ub514\ub809\ud1a0\ub9ac \uc120\ud0dd\ucc3d\uc774 \ubcf4\uc5ec\uc9c0\uace0 \ub514\ub809\ud1a0\ub9ac\ub97c \uc120\ud0dd\ud558\uba74, \ud574\ub2f9 \ub514\ub809\ud1a0\ub9ac \ub0b4\uc758 \ub0b4\uc6a9\uc774 Uri\ub85c \ub9ac\ud134\ub41c\ub2e4. \uc2e4\uc0ac\uc6a9 \uc608\uc81c\ub294 \ub4a4\uc5d0\uc11c \ub2e4\ub8f0 \uac83\uc774\ub2e4.<\/p>\n\n\n\n<p>\uc774\uc640\uac19\uc774 \uc2dc\uc2a4\ud15c Picker UI\ub294 \ud574\ub2f9 \ud30c\uc77c(\ub610\ub294 \ub514\ub809\ud1a0\ub9ac)\uc5d0 \ub300\ud55c Uri\uac00 \uc571\uc5d0 \ub9ac\ud134\ub41c\ub2e4. \uc138\uc2a4\ud15c UI\uc5d0\uc11c \uc0ac\uc6a9\uc790\uc5d0\uac8c \ubb3c\uc5b4\ubcf4\ub294 \ubc29\uc2dd\uc73c\ub85c \uad8c\ud55c\uacfc \uad00\ub828\ub41c \ucc98\ub9ac\uac00 \ub418\uae30 \ub54c\ubb38\uc5d0, \uc571\uc5d0\uc11c \ubcc4\ub3c4\ub85c \uc124\uc815\ud560 \uad8c\ud55c\uc774 \ud544\uc694\uc5c6\uc774 \ub9ac\ud134\ubc1b\uc740 Uri\ub97c \uc774\uc6a9\ud558\uc5ec \ud30c\uc77c \uc791\uc5c5\uc774 \uac00\ub2a5\ud574\uc9c4\ub2e4. \uc774\ub807\uac8c \ubd80\uc5ec\ubc1b\uc740 Uri\uc5d0 \ub300\ud55c \uad8c\ud55c\uc740 \ub514\ubc14\uc774\uc2a4\uac00 \uc7ac\ubd80\ud305 \ub420 \ub54c\uae4c\uc9c0 \uc720\ud6a8\ud558\ub2e4. \ud558\uc9c0\ub9cc, \uc7ac\ubd80\ud305 \uc2dc\uc5d0\ub3c4 \uad8c\ud55c\uc774 \ud544\uc694\ud55c \uacbd\uc6b0, \uc778\ud150\ud2b8\uc758 \ud50c\ub798\uadf8\ub97c \uc774\uc6a9\ud558\uc5ec \ub2e4\uc74c \ucf54\ub4dc\uc640 \uac19\uc774 \uc601\uad6c\uc801\uc778 \uad8c\ud55c\uc694\uccad\ub3c4 \uac00\ub2a5\ud558\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"kotlin\" class=\"language-kotlin\">val contentResolver = applicationContext.contentResolver\n\nval takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or\n        Intent.FLAG_GRANT_WRITE_URI_PERMISSION\n\/\/ Check for the freshest data.\ncontentResolver.takePersistableUriPermission(uri, takeFlags)\n<\/code><\/pre>\n\n\n\n<p>\ub2f9\uc5f0\ud558\uaca0\uc9c0\ub9cc, \ud574\ub2f9 \ud30c\uc77c\uc774 \uc774\ub3d9\ud558\uac70\ub098, \uc0ad\uc81c\ub418\uba74 \uc774\ub807\uac8c \ud68d\ub4dd\ud55c \uad8c\ud55c\uc740 \uc720\uc9c0\ub418\uc9c0 \uc54a\ub294\ub2e4.<\/p>\n\n\n\n<p>SAF\uac00 \uc5b4\ub5bb\uac8c \uc791\ub3d9\ud558\ub294\uc9c0\ub294 \ucda9\ubd84\ud788 \uc124\uba85\ud55c\uac70 \uac19\uace0, \uc774\uc81c \ub2e4\ud050\uba3c\ud2b8 \ud504\ub85c\ubc14\uc774\ub354\uc758 \ud074\ub77c\uc774\uc5b8\ud2b8\ub85c\uc11c \uc138\uac00\uc9c0 \uc778\ud150\ud2b8 <a href=\"https:\/\/developer.android.com\/reference\/android\/content\/Intent#ACTION_OPEN_DOCUMENT\">ACTION_OPEN_DOCUMENT<\/a>, <a href=\"https:\/\/developer.android.com\/reference\/android\/content\/Intent#ACTION_CREATE_DOCUMENT\">ACTION_CREATE_DOCUMENT<\/a>, <a href=\"https:\/\/developer.android.com\/reference\/android\/content\/Intent#ACTION_OPEN_DOCUMENT_TREE\">ACTION_OPEN_DOCUMENT_TREE<\/a> \ub4e4\uc744 \uc5b4\ub5bb\uac8c \uc0ac\uc6a9\ud558\ub294\uc9c0 \uc54c\uc544\ubcf4\uc790.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">SAF Intents<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">ACTION_OPEN_DOCUMENT_TREE<\/h4>\n\n\n\n<p> \uc791\uc5c5 \ub514\ub809\ud1a0\ub9ac\ub97c \uc120\ud0dd\ud558\ub294 \uc778\ud150\ud2b8\uc774\ub2e4. \uc0ac\uc6a9\ubc29\ubc95\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"kotlin\" class=\"language-kotlin\">class MainActivity : AppCompatActivity() {\n    ...\n    private val openDocTree = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -&gt;\n        binding.currentTreeUri.text = uri.toString()\n        if(uri != null) requestPersistantPermission(uri)\n    }\n    \n    override fun onCreate(savedInstanceState: Bundle?) {\n    ...\n       binding.openDocTree.setOnClickListener {\n            val uri = null  \/\/ root directory\n            openDocTree.launch(uri)\n        }\n    }\n    ...\n<\/code><\/pre>\n\n\n\n<p>\uc778\ud150\ud2b8\uc758 \uc0ac\uc6a9\uc5d0 registerForActivityResult()\ub97c \uc0ac\uc6a9\ud588\ub2e4. \uc774 API\uc758 \uc0ac\uc6a9\uc0c1 \uc8fc\uc758\uc810\uc740 \ub4f1\ub85d\uc744 onStart \uc774\uc804\uc5d0 \ud574\uc57c\ud55c\ub2e4\ub294 \uac83\uc774\ub2e4. \ub9cc\uc57d\uc5d0 start \uc774\ud6c4\uc5d0 \uc0ac\uc6a9\ud558\uba74, \ub2e4\uc74c\uacfc \uac19\uc740 \ub7f0\ud0c0\uc784 \uc5d0\ub7ec\ub97c \ub9cc\ub098\uac8c \ub41c\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.<\/code><\/pre>\n\n\n\n<p>registerForActivityResult()\ub97c \uc0ac\uc6a9\uc2dc, ACTION_OPEN_DOCUMENT_TREE\uc5d0 \ub300\ud55c contract\uac00 ActivityResultContracts.OpenDocumentTree()\ub85c \ubbf8\ub9ac \uc815\uc758\ub418\uc5b4 \uc788\uc73c\ubbc0\ub85c \uadf8\ub300\ub85c \uc0ac\uc6a9\ud55c\ub2e4. \uc774\ub807\uac8c \ub4f1\ub85d\uc744 \ud574\ub193\uace0, \ubc84\ud2bc \ud074\ub9ad\uc2dc, \ubd88\ub9ac\ub3c4\ub85d \uad6c\ud604\ud574\ub1a8\ub2e4. openDocTree.launch(uri)\uac00 \uadf8 \ubd80\ubd84\uc774\ub2e4. \uc778\uc790\ub85c\ub294 uri\uac00 \ub118\uc5b4\uac00\ub294\ub370, EXTRA_INITIAL_URI \uac12\uc744 \ubc1b\ub294\ub2e4. \ud574\ub2f9 \ubd80\ubd84 \ucf54\ub4dc\ub97c \ucc3e\uc544\ubcf4\uba74 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"kotlin\" class=\"language-kotlin\">open class OpenDocumentTree : ActivityResultContract&lt;Uri?, Uri?&gt;() {\n        @CallSuper\n        override fun createIntent(context: Context, input: Uri?): Intent {\n            val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)\n            if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.O &amp;&amp; input != null) {\n                intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, input)\n            }\n            return intent\n        }\n...<\/code><\/pre>\n\n\n\n<p> createIntent()\uac00 launch()\ub54c\ub9c8\ub2e4 \uc778\ud150\ud2b8\ub97c \uc0dd\uc131\ud558\ub294\ub370 \ud638\ucd9c\ub41c\ub2e4. \ucf54\ub4dc\ub97c \ubcf4\uba74, android O( API 8.0\/26) \uc774\uc0c1\uc77c \ub54c, putExtra()\ub85c EXTRA_INITIAL_URI\uac12\uc744 \ub123\uc5b4\uc8fc\uace0 \uc788\ub2e4. \ud558\uc9c0\ub9cc, \uc5c6\ub294 \uae30\ub2a5\uc73c\ub85c \uc0dd\uac01\ud558\ub294\uac8c \ub9d8 \ud3b8\ud560\uac70\ub2e4. \uc774 EXTRA_INITIAL_URI\uc5d0 \ub300\ud574\uc120 \ub4a4\uc5d0 \ucd94\uac00\ub85c \ub2e4\ub8e8\uaca0\ub2e4. <\/p>\n\n\n\n<p>\uc774\ub807\uac8c \ud574\uc11c \uc2e4\ud589\ud558\uac8c\ub418\uba74, \ub2e4\uc74c\uacfc \uac19\uc740 \uc2dc\uc2a4\ud15c UI\uac00 \ub5a0\uc11c \ub514\ub809\ud1a0\ub9ac \uc120\ud0dd\uc774 \uac00\ub2a5\ud558\ub2e4. UI\ub294 \uc548\ub4dc\ub85c\uc774\ub4dc \ubc84\uc804\uc5d0 \ub530\ub77c \ub2e4\ub974\ub2e4.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"576\" height=\"1024\" src=\"http:\/\/batmask.dothome.co.kr\/wordpress\/wp-content\/uploads\/2023\/12\/ScopedStorage-02-576x1024.png\" alt=\"\" class=\"wp-image-2185\" style=\"width:279px;height:auto\"\/><\/figure>\n<\/div>\n\n\n<p>\uc0ac\uc6a9\ud560 \uc218 \uc5c6\ub294 \ub514\ub809\ud1a0\ub9ac\uc758 \uacbd\uc6b0, \uc704\ucabd\uc5d0 Create new Folder\uac00 \ub728\uace0 \uc5ec\uae38 \ud074\ub9ad\ud574\uc11c \uc0c8 \ud3f4\ub354\ub97c \uc0dd\uc131\ud574 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. \ubc14\ub85c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4\uba74, \ud558\ub2e8\uc5d0 &#8220;Use this folder&#8221; \ubc84\ud2bc\uc774 \ud65c\uc131\ud654 \ub418\uace0 \ubc84\ud2bc\uc744 \ud074\ub9ad\ud574\uc11c \uc6d0\ub798 \uc571\uc73c\ub85c \ubcf5\uadc0\ud558\uba70 \ud574\ub2f9 \ud3f4\ub354\uc758 uri\ub97c \ubc18\ud658\ud55c\ub2e4. <\/p>\n\n\n\n<p>\uc548\ub4dc\ub85c\uc774\ub4dc \ubc84\uc804\uc5d0 \ub530\ub77c \ub2e4\ub974\uac8c \ub3d9\uc791\ud560\uc9c0 \ubaa8\ub974\uaca0\uc73c\ub098, \uc774\ub807\uac8c \ud55c \ubc88 \uc120\ud0dd\ud55c \ud3f4\ub354\uac00 \ub2e4\uc74c\ubc88 \uc2e4\ud589\ud574\ub3c4 \uc774 \uc704\uce58\ub85c \uacc4\uc18d \uc720\uc9c0\ub41c\ub2e4. EXTRA_INITIAL_URI\ub97c \uc9c1\uc811 \ub123\uc5b4\uc918\ub3c4 \ubb34\uc2dc\ub418\ub294 \uac83\uc744 \ud655\uc778\ud588\ub2e4. \ub4a4\uc758 \ub2e4\ub978 \uc778\ud150\ud2b8\uc5d0 \ub300\ud574\uc11c\ub3c4 \ub9c8\ucc2c\uac00\uc9c0\ub2e4.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">ACTION_CREATE_DOCUMENT<\/h4>\n\n\n\n<p> \uc0c8\ub85c\uc6b4 \ud30c\uc77c\uc744 \ub9cc\ub4dc\ub294 \uc778\ud150\ud2b8\uc774\ub2e4. \uc0ac\uc6a9\ubc95\uc740 ACTION_OPEN_DOCUMENT_TREE\uc640 \ud06c\uac8c \ub2e4\ub974\uc9c0 \uc54a\ub2e4. \ub0b4\uac00 \uc6d0\ud558\ub294 \uc704\uce58\uc5d0 \uc800\uc7a5\ud560 \uc218 \uc788\uc73c\uba74 \uc88b\uaca0\uc9c0\ub9cc, \uc2dc\uc2a4\ud15c UI\uc5d0\uc11c \uc0ac\uc6a9\uc790\uc5d0 \uc758\ud574 \uc800\uc7a5 \uc704\uce58\uac00 \uc815\ud574\uc9c4\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"kotlin\" class=\"language-kotlin\">private val createDoc = registerForActivityResult(MyCreateDocument(\"text\/plain\")) { uri ->\n   binding.currentDocUri.text = uri.toString()\n   if(uri != null) addText(uri)\n}\n\n...\n\nbinding.createDocument.setOnClickListener {\n    val filename = \"test.txt\"\n    createDoc.launch(filename)\n\n}<\/code><\/pre>\n\n\n\n<p>Contract\uc704\uce58\uc5d0 MyCreateDocument()\uac00 \ub4e4\uc5b4\uac00 \uc788\ub294\ub370, \uc774\ub294 \ub2e4\uc74c\uacfc \uac19\uc774 \uad6c\ud604\ud588\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">class MyCreateDocument(mimetype: String, private var initialUri: Uri? = null): ActivityResultContracts.CreateDocument(mimetype) {\n\n    override fun createIntent(context: Context, input: String): Intent {\n        val intent = super.createIntent(context, input)\n        return if(initialUri != null)\n            intent.putExtra(EXTRA_INITIAL_URI, initialUri)\n        else\n            intent\n    }\n\n    fun putInitialUri(uri: Uri) {\n        initialUri = uri\n    }\n}<\/code><\/pre>\n\n\n\n<p>ActivityResultContracts\uc5d0 \ubbf8\ub9ac \uad6c\ud604\ub418\uc5b4 \uc788\ub294 CreateDocument(mimeType)\uc744 \uc0ac\uc6a9\ud558\uba74 \ub418\ub294\ub370, EXTRA_INITIAL_URI\ub97c \ucd94\uac00\ud558\uace0 \uc2f6\uc5b4\uc11c \uc0c1\uc18d\ubc1b\uc544 createIntent()\ub97c \uad6c\ud604\ud588\ub2e4. EXTRA_INITIAL_URI\uac00 \uc758\ub3c4\ub300\ub85c \ub3d9\uc791\ud558\uc9c0 \uc54a\uc544\uc11c \ud544\uc694 \uc5c6\uc744\uc218 \uc788\uc9c0\ub9cc, \uc774\ub7f0\uc2dd\uc758 \uc0ac\uc6a9\uc774 \uac00\ub2a5\ud558\ub2e4\ub294\uac78 \ubcf4\uc5ec\uc8fc\uae30\uc704\ud574 \uadf8\ub300\ub85c \uc0ac\uc6a9\ud574\ubd24\ub2e4.<\/p>\n\n\n\n<p>\uc774\ubbf8 \ubd24\uc9c0\ub9cc, CreateDocument() Contract\ub294 \uc0dd\uc131\uc790\uc5d0 mimeType\uc744 \uc778\uc790\ub85c \ubc1b\ub294\ub2e4. \uc774\ub294 \uc800\uc7a5\ud560 \ud30c\uc77c\uc758 type\uc744 \uc9c0\uc815\ud574\uc900\ub2e4. \uc2e4\uc81c\ub85c \uc2e4\ud589\ud558\ub294 launch()\uc5d0\uc11c\ub294 \uc778\uc790\ub85c \ud30c\uc77c\uc774\ub984\uc744 \ubc1b\ub294\ub2e4. \uc774\ub294 \uc2dc\uc2a4\ud15c UI\uc5d0\uc11c \uc800\uc7a5 \ud30c\uc77c \uc774\ub984\uc73c\ub85c \uc0ac\uc6a9\ub41c\ub2e4. <\/p>\n\n\n\n<p> \uc2dc\uc2a4\ud15c UI\uc5d0\uc11c &#8220;\uc800\uc7a5&#8221;\uc744 \uc120\ud0dd\ud558\uba74 \ud574\ub2f9 \uc774\ub984\uc758 \ud30c\uc77c\uc5d0 \ub300\ud55c uri\ub97c \ub3cc\ub824\ubc1b\ub294\ub2e4. \uc774 uri\ub85c \ubd80\ud130 \ud30c\uc77c\uc744 \uc5bb\uc5b4\uc640 \uc368\uc8fc\uba74 \ub41c\ub2e4. \ucf54\ub4dc\uc5d0\uc11c addText(uri)\ub97c \ubd88\ub7ec\uc8fc\uace0 \uc788\ub294\ub370, \uc774 \ub0b4\uc6a9\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">private fun addText(uri: Uri) {\n    try{\n        val os = contentResolver.openOutputStream(uri, \"w\")\n        if(os != null) {\n            os.write(\"This is test text file\".toByteArray())\n            os.close()\n        }\n    }catch(e: Exception){\n        Log.e(\"SAFSample\", \"Can't open file\")\n    }\n}<\/code><\/pre>\n\n\n\n<p>uri\ub85c\ubd80\ud130 \ud30c\uc77c\uc744 \uc2a4\ud2b8\ub9bc\uc73c\ub85c \uc5bb\uc5b4\uc640 \uc368\uc8fc\uace0 \uc788\ub2e4. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">ACTION_OPEN_DOCUMENT<\/h4>\n\n\n\n<p> \uc774\ubbf8 \uc874\uc7ac\ud558\ub294 \ud30c\uc77c\uc744 \uc5f4 \ub54c \uc0ac\uc6a9\ud558\ub294 \uc778\ud150\ud2b8\uc774\ub2e4. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"kotlin\" class=\"language-kotlin\">...\n    private val openDoc = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->\n        binding.currentDocUri.text = uri.toString()\n    }\n...\n    binding.openDocument.setOnClickListener {\n        openDoc.launch(arrayOf(\"*\/*\"))\n    }<\/code><\/pre>\n\n\n\n<p>launch()\uc2dc \uc778\uc790\ub294 MimeType\uc758 \ub9ac\uc2a4\ud2b8\ub97c \ub118\uaca8\uc900\ub2e4. \uc774\ub294 \ubcf4\uc5ec\uc904 \ud30c\uc77c \ud0c0\uc785\ub4e4\uc758 \ud544\ud130\ub9c1\uc5d0 \uc0ac\uc6a9\ub41c\ub2e4. Mime type\uc5d0 \ub300\ud574\uc11c\ub294 <a href=\"https:\/\/developer.mozilla.org\/ko\/docs\/Web\/HTTP\/Basics_of_HTTP\/MIME_types\">\ubaa8\uc9c8\ub77c \ubb38\uc11c<\/a>\ub97c \ucc38\uace0\ud558\uc790.<\/p>\n\n\n\n<p>\uc2dc\uc2a4\ud15c UI\uc5d0\uc11c \ud30c\uc77c\uc744 \uc120\ud0dd\ud558\uba74, \uadf8 \ud30c\uc77c\uc5d0 \ub300\ud55c uri\ub97c \ub3cc\ub824\ubc1b\uac8c \ub41c\ub2e4. \uc774\uac78 ACTION_CREATE_DOCUMENT\uc5d0\uc11c \ud588\ub4ef\uc774, \ud30c\uc77c \uc785\ucd9c\ub825\uc744 \uc774\uc6a9\ud574 \uc6d0\ud558\ub294 \uc791\uc5c5\uc744 \uc218\ud589\ud558\uba74 \ub41c\ub2e4. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\uacb0\ub860 \ubc0f EXTRA_INITIAL_URI \ubb38\uc81c<\/h3>\n\n\n\n<p>\uc5b4&#8230; \uc774\uac78 \uc815\ub9d0 \uc368\uc57c\ud558\ub098 \uace0\ubbfc\uc774 \ub9ce\uc544\uc9c4\ub2e4. \uc77c\ub2e8, \uc2dc\uc2a4\ud15c UI\uac00 \ubb34\uc9c0\ud558\uac8c \ub2a6\uac8c \ub72c\ub2e4. \ub85c\ub529\uc774 \ub728\uc9c0\ub3c4 \uc54a\uace0 \uadf8\ub0e5 \ube48\ud654\uba74\uc774 \ud55c\ucc38 \ubcf4\uc778\ub2e4\uace0. \uadf8\ub9ac\uace0 \uacb0\uc815\uc801\uc73c\ub85c EXTRA_INITIAL_URI \ubb38\uc81c. SD \uce74\ub4dc\uc5d0 \uc800\uc7a5\ud558\uace0 \uc2f6\uc744 \ub54c\ub294 \uc571\uc5d0\uc11c \uc704\uce58\ub97c \uc9c0\uc815\ud574\uc8fc\uace0 \uc2f6\uc744\uac70\ub2e4. \ub09c\uc7a1\ud55c \uc678\uc7a5 \uc2a4\ud1a0\ub9ac\uc9c0\uc758 \ub514\ub809\ud1a0\ub9ac \uad6c\uc870\ub97c \uc720\uc800\uc5d0\uac8c \ub118\uaca8\uc904 \ub9c8\uc74c\uc774 \uc5c6\ub2e4\uace0. \uadf8\ub7f0\ub370, \uc774\uac8c \uc81c\ub300\ub85c \ub3d9\uc791\uc744 \uc548\ud55c\ub2e4. \ub610\ud558\ub098, \ub514\ub809\ud1a0\ub9ac\ub97c \ubcc0\uacbd\ud558\uba74 \ub9e4\ubc88 \uc0ac\uc6a9\uc5ec\ubd80\ub97c \ubb3c\uc5b4\ubcf4\uace0 \uad8c\ud55c\uc744 \uc900\ub2e4. \ud55c \ubc88 \uc0ac\uc6a9\ud588\ub2e4\uace0 \ud574\ub3c4, \ub2e4\ub978 \ub514\ub809\ud1a0\ub9ac\ub85c \uac14\ub2e4\uac00 \ub610 \ub2e4\uc2dc \ubcc0\uacbd\ud558\ub824\uba74 \uc0c8\ub85c \uc5bb\uc5b4\uc57c \ud55c\ub2e4. <\/p>\n\n\n\n<p>\uc815, \uc678\uc7a5 \uc2a4\ud1a0\ub9ac\uc9c0\ub97c \uc0ac\uc6a9\ud558\uace0 \uc2f6\ub2e4\uba74,  scoped storage\ub85c \uadf8\ub0e5 \uc571\uc5d0 \uc81c\ud55c\ub41c \uc601\uc5ed\uc744 \uc0ac\uc6a9\ud558\uc790. \uc678\ubd80\ub85c \ube7c\uc57c\ud560 \uacbd\uc6b0\uc5d4 \ud30c\uc77c export\uac19\uc740 \uae30\ub2a5\ub9cc \ucd94\uac00\ud558\uc5ec SAF\ub97c \uc774\uc6a9, \uc5b4\ub290 \uc704\uce58\uc5d0 \ud30c\uc77c\uc744 \uc800\uc7a5\ud560\uc9c0\ub294 \uc2dc\uc2a4\ud15c UI\ub97c \ud1b5\ud574 \uc720\uc800\uc5d0\uac8c \ub9e1\uaca8\ubc84\ub9ac\uc790. \ub354 \uc695\uc2ec\ub0b4\uc9c0 \ub9d0\uc790 \u314b\u314b\u314b \uc544, \uc678\uc7a5 \uc2a4\ud1a0\ub9ac\uc9c0\ub97c \ub9d8\ub300\ub85c \uc0ac\uc6a9\ud558\ub358 Android 10\uae4c\uc9c0\uc640 \uadf8\uac8c \ubd88\uac00\ub2a5\ud55c Android 11\uc774\uc0c1\uc744 \uad6c\ubd84\ud574\uc11c \ub2e4\ub8e8\ub294 \uac83\ub3c4 \ub3c4\uc6c0\uc774 \ub420\uac70\ub2e4. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>\uac1c\uc694 SD Card \ub610\ub294 External Storage\uc5d0 \ub300\ud574 \uac00\ubccd\uac8c \uc811\uadfc\ud588\ub2e4\uac00 \ub300\ud63c\ub3c8\uc774\ub77c\ub294\uac78 \ub4a4\ub2a6\uac8c \uc54c\uc558\ub2e4. \ub0b4\ub9d8\uac19\uc544\uc120, \uadf8\ub0e5 \uae30\uc874\ucc98\ub7fc \uc790\uc720\ub86d\uac8c \uc811\uadfc\ud558\uac8c \ub0c5\ub450\uba74 \uc88b\uaca0\uc9c0\ub9cc&#8230; \ubaa8\ub4e0 \ubcc0\ud654\uc758 \ud575\uc2ec\uc740 Internal Storage \uc218\uc900\uc758 \ubcf4\uc548\uacfc \uad8c\ud55c(Permission)\uc774\ub2e4. \uc0ac\uc871\uc744 \ub2ec\uc790\uba74, \ub300\ubd80\ubd84 \uc571\ub4e4\uc740 \uc544\uc774\ud3f0\uc6a9\uc73c\ub85c\ub3c4 \uac1c\ubc1c\ub418\uae30 \ub54c\ubb38\uc5d0 \uc774 \uace8\uce58\uc544\ud508 External Storage\ub97c \ub2e4\ub8e8\uc9c0 \uc54a\uace0 \uc804\ubd80 Internal Storage\ub9cc \uc0ac\uc6a9\ud55c\ub2e4, \uce74\uce74\uc624 \ud1a1\ucc98\ub7fc. \ub0b4\uc6a9\uc744 \ubcfc\uc218\ub85d \uc774\uac83\ub3c4 \uc774\ud574\uac00 <a href=\"http:\/\/batmask.net\/index.php\/2023\/12\/15\/2008\/\" class=\"btn btn-link continue-link\">\ub354 \uc77d\uae30<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,34],"tags":[186,366,364,367,365,20],"class_list":["post-2008","post","type-post","status-publish","format-standard","hentry","category-android","category-kotlin","tag-android-2","tag-external-storage","tag-saf","tag-sd-card","tag-storage-access-framework","tag-android"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/posts\/2008","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/comments?post=2008"}],"version-history":[{"count":36,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/posts\/2008\/revisions"}],"predecessor-version":[{"id":2193,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/posts\/2008\/revisions\/2193"}],"wp:attachment":[{"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/media?parent=2008"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/categories?post=2008"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/tags?post=2008"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}