

<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Android筆記 &#8211; Max的程式語言筆記</title>
	<atom:link href="https://stackoverflow.max-everyday.com/category/android/feed/" rel="self" type="application/rss+xml" />
	<link>https://stackoverflow.max-everyday.com</link>
	<description>我要當一個豬頭，快樂過每一天</description>
	<lastBuildDate>Fri, 09 Jan 2026 13:43:11 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>

<image>
	<url>https://stackoverflow.max-everyday.com/wp-content/uploads/2017/02/max-stackoverflow-256.png</url>
	<title>Android筆記 &#8211; Max的程式語言筆記</title>
	<link>https://stackoverflow.max-everyday.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Android 15 FrameLayout 蓋住了系統欄，手動處理 視窗內邊距</title>
		<link>https://stackoverflow.max-everyday.com/2026/01/android-15-framelayout-fitssystemwindows/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/01/android-15-framelayout-fitssystemwindows/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Fri, 09 Jan 2026 13:42:41 +0000</pubDate>
				<category><![CDATA[Android筆記]]></category>
		<category><![CDATA[Android]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=7647</guid>

					<description><![CDATA[Android 15 強制開啟 Edge-to-...]]></description>
										<content:encoded><![CDATA[
<p>Android 15 強制開啟 Edge-to-Edge 模式。應用程式會預設填滿整個螢幕，包含狀態列與導覽列下方。如果你的 FrameLayout 蓋住了系統欄，需要手動處理 Insets（視窗內邊距）。<sup>1</sup></p>



<h2 class="wp-block-heading">使用 fitsSystemWindows</h2>



<p>這是最快的方法。在 XML 佈局中，對最外層的 FrameLayout 加入屬性：</p>



<p>XML</p>



<pre class="wp-block-code"><code>&lt;FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"&gt;
    &lt;/FrameLayout&gt;
</code></pre>



<p>這會讓系統自動幫 FrameLayout 加上內邊距，避開狀態列與導覽列。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">程式碼精確控制</h2>



<p>如果你只需要頂部避開狀態列，或是想保留背景填滿全螢幕，建議在 Activity 中使用 ViewCompat 監聽器。</p>



<p>Kotlin</p>



<pre class="wp-block-code"><code>ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.your_frame_layout)) { view, insets -&gt;
    val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
    // 根據需求設定 padding
    view.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
    insets
}
</code></pre>



<p>這段程式碼會抓取系統欄的高度。將它設為 FrameLayout 的 padding 後，內容就不會被遮擋。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">暫時停用強制規範</h2>



<p>如果你還沒準備好適應新規則，可以在 Android 15 (API 35) 的主題中加入這個屬性。但注意這只是過渡方案，未來版本可能失效。<sup>2</sup></p>



<p>在 res/values-v35/themes.xml 加入：</p>



<p>XML</p>



<pre class="wp-block-code"><code>&lt;item name="android:windowOptOutEdgeToEdgeEnforcement"&gt;true&lt;/item&gt;
</code></pre>



<p>這會讓 App 回到舊版的顯示邏輯，不再自動延伸到系統欄下方。</p>



<p><a target="_blank" rel="noreferrer noopener" href="https://www.youtube.com/watch?v=gIUe2OOzohw">Handling Edge-to-Edge in Android 15</a></p>



<p>這段影片詳細解說了 Android 15 強制全螢幕的原理，並示範如何在 Compose 和傳統 XML 中正確處理系統內邊距。</p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/01/android-15-framelayout-fitssystemwindows/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>電腦上透過指令安裝 APK 到 Android 裝置</title>
		<link>https://stackoverflow.max-everyday.com/2025/12/pc-install-apk-to-device/</link>
					<comments>https://stackoverflow.max-everyday.com/2025/12/pc-install-apk-to-device/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Wed, 31 Dec 2025 12:24:08 +0000</pubDate>
				<category><![CDATA[Android筆記]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=7603</guid>

					<description><![CDATA[要在電腦上透過指令安裝 APK 到 Androi...]]></description>
										<content:encoded><![CDATA[
<p>要在電腦上透過指令安裝 APK 到 Android 裝置，最常用的工具是 ADB (Android Debug Bridge)。這是 Android SDK 裡的一個工具，能讓電腦跟手機溝通。</p>



<h3 class="wp-block-heading">前置準備</h3>



<p>首先電腦要安裝 ADB 工具。如果你有下載 Android Studio，它通常已經在裡面。如果是單獨下載 SDK Platform-Tools，記得要把路徑加入系統環境變數。手機端要開啟開發者選項，並把 USB 偵錯打開。</p>



<h3 class="wp-block-heading">連接裝置</h3>



<p>安裝前先確認電腦有沒有抓到手機。</p>



<p>adb devices</p>



<p>如果畫面出現一串序號加上 device 字樣，代表連接成功。如果顯示 unauthorized，記得看手機螢幕點選允許偵錯。</p>



<h3 class="wp-block-heading">安裝指令</h3>



<p>最基本的安裝指令很簡單。</p>



<p>adb install 路徑/檔名.apk</p>



<p>如果電腦上有好幾個檔案，可以直接把 APK 檔案拖進終端機視窗，它會自動填入路徑。</p>



<h3 class="wp-block-heading">常用的安裝參數</h3>



<p>有時候檔案已經存在，或者需要特殊權限，可以加參數。</p>



<ul class="wp-block-list">
<li>覆蓋安裝：adb install -r 檔名.apk (保留原本的應用程式資料)</li>



<li>降版安裝：adb install -d 檔名.apk (允許安裝比手機現有版本還舊的 APK)</li>



<li>授予所有權限：adb install -g 檔名.apk (安裝完自動開啟所有權限)</li>
</ul>



<h3 class="wp-block-heading">指定特定裝置安裝</h3>



<p>如果你同時接了兩台以上的手機，要指定其中一台。</p>



<p>adb -s 裝置序號 install 檔名.apk</p>



<p>裝置序號可以用剛才的 adb devices 指令查到。</p>



<h3 class="wp-block-heading">常見問題</h3>



<p>如果安裝失敗，通常會回傳錯誤代碼。</p>



<ul class="wp-block-list">
<li>INSTALL_FAILED_ALREADY_EXISTS：手機裡已經有同樣的 App，請改用 -r 參數。</li>



<li>INSTALL_FAILED_INSUFFICIENT_STORAGE：手機空間不夠。</li>



<li>INSTALL_FAILED_UPDATE_INCOMPATIBLE：簽名不一致，通常要先手動移除手機舊版 App 才能裝。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>如果你已經安裝了 Android Studio，ADB 執行檔通常會藏在 SDK 的資料夾裡面。路徑會根據你的作業系統而有所不同。</p>



<h3 class="wp-block-heading">Windows 系統的路徑</h3>



<p>在 Windows 上，預設的路徑通常在你的使用者資料夾下。</p>



<p>C:\Users\使用者名稱\AppData\Local\Android\Sdk\platform-tools</p>



<p>注意 AppData 是一個隱藏資料夾，你可能要在檔案總管開啟顯示隱藏項目才看得到。在這個資料夾裡，你會找到 adb.exe。</p>



<h3 class="wp-block-heading">macOS 系統的路徑</h3>



<p>在 Mac 上，路徑通常在資源庫裡面。</p>



<p>/Users/使用者名稱/Library/Android/sdk/platform-tools</p>



<p>你可以直接在終端機輸入這個路徑來執行 adb，或者把這個路徑加到你的 .zshrc 檔案中，之後輸入 adb 就不需要完整路徑。</p>



<h3 class="wp-block-heading">在 Android Studio 介面查看</h3>



<p>如果你不確定路徑，可以從 IDE 裡面直接看。</p>



<ol start="1" class="wp-block-list">
<li>打開 Android Studio。</li>



<li>點選 Settings (或是 macOS 的 Settings/Preferences)。</li>



<li>尋找 Appearance &amp; Behavior。</li>



<li>點開 System Settings 找到 Android SDK。</li>



<li>視窗右上方會直接顯示 Android SDK Location，那個路徑底下的 platform-tools 就是目的地。</li>
</ol>



<h3 class="wp-block-heading">快速執行的方法</h3>



<p>如果你懶得找路徑，可以直接用 Android Studio 內建的 Terminal 視窗。</p>



<p>它會自動幫你設定好環境變數。只要在 Android Studio 下方點開 Terminal，直接輸入 adb 就能使用，不需要自己輸入長長的路徑。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>寫一段把這個路徑加入系統環境變數的指令</p>



<p>既然你想要把路徑加入系統環境變數，這樣以後在任何資料夾輸入 adb 都能直接執行。我幫你整理了 Windows 和 Mac 的快速設定方式。</p>



<h3 class="wp-block-heading">Windows 設定方式</h3>



<p>你可以透過 PowerShell 指令直接把路徑加進去。請記得把下面的路徑換成你電腦上實際的路徑。</p>



<p>PowerShell</p>



<pre class="wp-block-code"><code>$adbPath = "C:\Users\使用者名稱\AppData\Local\Android\Sdk\platform-tools"
&#91;Environment]::SetEnvironmentVariable("Path", $env:Path + ";" + $adbPath, "User")
</code></pre>



<p>執行完後，請重新啟動你的 PowerShell 或 CMD 視窗，輸入 adb version 確認是否成功。</p>



<h3 class="wp-block-heading">macOS 設定方式</h3>



<p>Mac 通常使用 Zsh，你需要修改設定檔。</p>



<ol start="1" class="wp-block-list">
<li>在終端機輸入 <code>nano ~/.zshrc</code>。</li>



<li>在檔案最下方加入這行：<code>export PATH=$PATH:/Users/使用者名稱/Library/Android/sdk/platform-tools</code></li>



<li>按下 Ctrl + O 存檔，Ctrl + X 離開。</li>



<li>輸入 <code>source ~/.zshrc</code> 讓設定生效。</li>
</ol>



<h3 class="wp-block-heading">驗證設定</h3>



<p>設定完成後，不管你在哪個資料夾，直接輸入這行。</p>



<p>adb devices</p>



<p>如果能正常列出裝置清單，就代表你成功了。之後要安裝 APK，只要切換到檔案所在的資料夾，輸入 <code>adb install 檔名.apk</code> 就可以了。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>Error message:</p>



<pre class="wp-block-code"><code>adb.exe: failed to install app.apk: Failure &#91;INSTALL_FAILED_UPDATE_INCOMPATIBLE: Existing package com.your.packagename signatures do not match newer version; ignoring!]</code></pre>



<p>這個錯誤訊息代表手機裡已經裝了同一個 App，但是舊版跟新版的「簽名（Signatures）」不一樣。這通常發生在舊版是從 Google Play 下載，而新版是你自己開發或從網路下載的 APK，系統為了安全會禁止覆蓋。</p>



<h3 class="wp-block-heading">解決方案</h3>



<p>因為簽名不一致，<code>-r</code> 參數（強制覆蓋）在這種情況下會失效。唯一的辦法是先移除手機上的舊版本，再重新安裝。</p>



<h3 class="wp-block-heading">步驟 1：解除安裝舊版</h3>



<p>你不需要在手機上點選，可以直接用指令移除。請使用這個 App 的套件名稱（Package Name）。</p>



<p>adb uninstall com.your.packagename</p>



<h3 class="wp-block-heading">步驟 2：重新安裝新版</h3>



<p>移除成功後，再執行你原本的安裝指令。</p>



<p>adb install app.apk</p>



<h3 class="wp-block-heading">注意事項</h3>



<p>解除安裝會導致該 App 的所有資料（如帳號紀錄、暫存檔）被清除。如果你開發時常遇到這個問題，建議確認你的開發環境（例如 Android Studio）是否使用了固定的 Debug Key。</p>



<p>如果執行 <code>adb uninstall</code> 還是失敗，需要我幫你查其他的移除參數嗎？</p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2025/12/pc-install-apk-to-device/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>android 如何使用 command 產生 .apk 檔案</title>
		<link>https://stackoverflow.max-everyday.com/2025/12/android-command-build-apk/</link>
					<comments>https://stackoverflow.max-everyday.com/2025/12/android-command-build-apk/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Wed, 31 Dec 2025 10:09:13 +0000</pubDate>
				<category><![CDATA[Android筆記]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=7601</guid>

					<description><![CDATA[在 Android 開發中，現在大多使用 Gra...]]></description>
										<content:encoded><![CDATA[
<p>在 Android 開發中，現在大多使用 Gradle 來處理編譯。如果你已經有專案原始碼，可以直接在專案根目錄使用 ./gradlew 指令。這是最常見也最推薦的做法。</p>



<h2 class="wp-block-heading">使用 Gradle Wrapper 編譯</h2>



<p>Gradle Wrapper 是一個腳本。它能確保專案在不同機器上使用相同的 Gradle 版本。打開終端機並切換到專案路徑，執行以下指令：</p>



<pre class="wp-block-code"><code>./gradlew assembleDebug
</code></pre>



<p>這個指令會編譯出一個用於測試的 Debug 版本 APK。如果你需要正式上架的版本，則改用：</p>



<pre class="wp-block-code"><code>./gradlew assembleRelease
</code></pre>



<p>執行成功後，產出的檔案通常會放在 app/build/outputs/apk/ 目錄下。你會在裡面看到 debug 或 release 資料夾，副檔名就是 .apk。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">常用指令與路徑</h2>



<p>如果你的環境是 Windows，指令要改成 gradlew.bat assembleDebug。在執行編譯前，建議先清理舊的暫存檔：</p>



<pre class="wp-block-code"><code>./gradlew clean assembleDebug
</code></pre>



<p>這樣可以避免舊的檔案影響新的編譯結果。如果你的專案有多個不同的 Product Flavors，你可以用 ./gradlew tasks 查看所有可用的編譯指令。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">關於 AAB 格式</h2>



<p>現在 Google Play 要求上架必須使用 .aab 格式。如果你是為了上架準備，指令會稍微不同：</p>



<pre class="wp-block-code"><code>./gradlew bundleRelease
</code></pre>



<p>這會產生 Android App Bundle 檔案。位置在 app/build/outputs/bundle/release/ 之下。這比傳統的 APK 能提供更小的下載體積。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>如果修改了 keystore 的存放路徑，最直接的調整位置是在專案中的 build.gradle 檔案。通常這個檔案位於 app 目錄下。你需要找到 signingConfigs 區塊。</p>



<h2 class="wp-block-heading">修改 build.gradle 設定</h2>



<p>在 signingConfigs 裡面，有一個屬性叫做 storeFile。你需要把後面的路徑指向新的位置。如果路徑是相對路徑，它是以專案根目錄為基準。</p>



<pre class="wp-block-code"><code>android {
    signingConfigs {
        release {
            storeFile file('/新的路徑/my-release-key.jks')
            storePassword '你的密碼'
            keyAlias '你的別名'
            keyPassword '你的密碼'
        }
    }
}
</code></pre>



<p>建議把路徑寫成相對路徑。例如 file(&#8216;../keystores/release.jks&#8217;)。這樣換一台電腦開發時，只要相對位置正確，就不會出錯。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">使用 gradle.properties 管理</h2>



<p>為了安全與方便，很多人會把路徑寫在 gradle.properties 檔案裡。這樣就不會把私密路徑寫死在程式碼。你可以打開 gradle.properties 調整變數值。</p>



<pre class="wp-block-code"><code>MYAPP_RELEASE_STORE_FILE=/新的路徑/my-release-key.jks
</code></pre>



<p>然後在 build.gradle 引用這個變數。改完後記得執行一次 ./gradlew clean。這能確保 Gradle 抓到最新的路徑設定。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">檢查環境變數</h2>



<p>有些團隊會把路徑放在系統環境變數。如果你的設定是透過 System.getenv 取得。那你就要去修改作業系統的環境設定。修改完後可能需要重啟終端機或 IDE 才會生效。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>在 <code>build.gradle</code> 引用 <code>gradle.properties</code> 的變數很簡單。Gradle 會自動將 properties 檔案中的屬性載入。</p>



<h2 class="wp-block-heading">引用變數的方式</h2>



<p>假設你在 <code>gradle.properties</code> 設定了變數名為 <code>MYAPP_RELEASE_STORE_FILE</code>。在 <code>app/build.gradle</code> 裡，你可以直接像這樣寫：</p>



<pre class="wp-block-code"><code>android {
    signingConfigs {
        release {
            // 直接使用變數名稱即可
            storeFile file(MYAPP_RELEASE_STORE_FILE)
            storePassword MYAPP_RELEASE_STORE_PASSWORD
            keyAlias MYAPP_RELEASE_KEY_ALIAS
            keyPassword MYAPP_RELEASE_KEY_PASSWORD
        }
    }
}
</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">使用專案屬性檢查</h2>



<p>有時候為了保險，會建議使用 <code>project.hasProperty</code> 來檢查變數是否存在。這樣即使變數沒設定，編譯也不會直接噴錯。這在多人協作時很好用。</p>



<p>Gradle</p>



<pre class="wp-block-code"><code>storeFile file(project.hasProperty('MYAPP_RELEASE_STORE_FILE') ? MYAPP_RELEASE_STORE_FILE : "default_path.jks")
</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">關於路徑的提醒</h2>



<p>如果你在變數中寫的是相對路徑，它是相對於專案根目錄。如果你希望路徑更靈活，可以搭配 <code>rootDir</code> 變數來組合：</p>



<p>Gradle</p>



<pre class="wp-block-code"><code>storeFile file("${rootDir}/keystores/${MYAPP_RELEASE_STORE_FILE}")
</code></pre>



<p>這樣無論在哪個層級的 gradle 檔引用，路徑都不會跑掉。改完設定後，建議執行 <code>./gradlew assembleRelease</code> 測試看看能不能成功讀取。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>看到 unsigned 代表這個 APK 還沒有經過數位簽署。Android 系統出於安全考慮，不會允許安裝任何未簽署的 APK。這通常是因為你的編譯指令或設定沒有正確關聯到簽署設定。</p>



<h2 class="wp-block-heading">檢查 build.gradle 的類型設定</h2>



<p>在 <code>build.gradle</code> 中，你雖然定義了 <code>signingConfigs</code>，但還必須在 <code>buildTypes</code> 裡面告訴 Gradle，release 版本要使用哪一組簽署設定。</p>



<pre class="wp-block-code"><code>android {
    buildTypes {
        release {
            // 這一行是關鍵，必須手動指定
            signingConfig signingConfigs.release 
            
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
</code></pre>



<p>如果漏掉了 <code>signingConfig signingConfigs.release</code> 這行，Gradle 就會預設產生不帶簽署的 APK，檔名就會出現 unsigned。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">檢查簽署內容是否完整</h2>



<p>如果設定已經寫了，但產出還是 unsigned，請檢查 <code>gradle.properties</code> 或 <code>signingConfigs</code> 區塊中的資訊是否齊全。簽署需要這四個要素：</p>



<ul class="wp-block-list">
<li><code>storeFile</code>: keystore 檔案的路徑。</li>



<li><code>storePassword</code>: 儲存庫的密碼。</li>



<li><code>keyAlias</code>: 密鑰的別名。</li>



<li><code>keyPassword</code>: 該密鑰的獨立密碼。</li>
</ul>



<p>只要缺一個，Gradle 有時候會因為找不到資訊而跳過簽署步驟，直接產出 unsigned 版本。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">清除快取重試</h2>



<p>有時候是之前的編譯產物殘留，導致你看到的是舊檔案。調整完設定後，請務必先執行清理指令再編譯：</p>



<pre class="wp-block-code"><code>./gradlew clean assembleRelease
</code></pre>



<p>如果設定正確，生成的檔名應該會變成 <code>app-release.apk</code>。</p>



<p>你需要我幫你檢查 <code>signingConfigs</code> 和 <code>buildTypes</code> 的對應程式碼嗎？</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>error message:</p>



<pre class="wp-block-code"><code>gradlew clean assembleRelease

* What went wrong:
A problem occurred evaluating project ':app'.
> Could not get unknown property 'release' for SigningConfig container of type org.gradle.api.internal.FactoryNamedDomainObjectContainer.</code></pre>



<p>這個錯誤訊息代表你在 <code>buildTypes</code> 裡面引用了一個還沒定義的 <code>signingConfigs</code> 名稱。Gradle 的執行順序是由上而下，如果你先在 <code>buildTypes</code> 寫了 <code>signingConfig signingConfigs.release</code>，但當時 <code>release</code> 這個設定還沒被建立，就會報錯。</p>



<h2 class="wp-block-heading">調整 build.gradle 的順序</h2>



<p>在 Gradle 腳本中，定義的順序非常重要。你必須確保 <code>signingConfigs</code> 區塊寫在 <code>buildTypes</code> 之前。請檢查你的 <code>app/build.gradle</code> 結構是否如下：</p>



<pre class="wp-block-code"><code>android {
    // 1. 先定義簽署設定
    signingConfigs {
        release {
            storeFile file(MYAPP_RELEASE_STORE_FILE)
            storePassword MYAPP_RELEASE_STORE_PASSWORD
            keyAlias MYAPP_RELEASE_KEY_ALIAS
            keyPassword MYAPP_RELEASE_KEY_PASSWORD
        }
    }

    buildTypes {
        release {
            // 2. 這裡才能引用上面定義好的 release
            signingConfig signingConfigs.release
            
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-bundle.txt'), 'proguard-rules.pro'
        }
    }
}
</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">常見的命名陷阱</h2>



<p>錯誤訊息提到的 <code>Could not get unknown property 'release'</code>，有時候是因為你在 <code>signingConfigs</code> 裡面把名稱取錯了。例如：</p>



<ul class="wp-block-list">
<li>如果你在 <code>signingConfigs</code> 裡寫的是 <code>myConfig { ... }</code>。</li>



<li>那在 <code>buildTypes</code> 就必須寫 <code>signingConfig signingConfigs.myConfig</code>。</li>
</ul>



<p>兩邊的名稱必須完全對應。請確認你的 <code>signingConfigs</code> 括號內確實有名為 <code>release</code> 的區塊。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">暫時註解法排查</h2>



<p>如果你想確認是不是路徑變數導致的問題，可以先試著在 <code>buildTypes</code> 裡把 <code>signingConfig signingConfigs.release</code> 這一行註解掉。如果編譯能過（雖然產出又是 unsigned），那就代表問題百分之百出在簽署區塊的定義。</p>



<p>修正順序後，再執行一次編譯試試看。</p>



<p>你需要我幫你看一下你目前 <code>build.gradle</code> 第 20 行附近的程式碼長什麼樣子嗎？</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2025/12/android-command-build-apk/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Kotlin 2.1.0 和 2.2.0 的主要差異</title>
		<link>https://stackoverflow.max-everyday.com/2025/12/kotlin-2-1-0-vs-2-2-0/</link>
					<comments>https://stackoverflow.max-everyday.com/2025/12/kotlin-2-1-0-vs-2-2-0/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Wed, 31 Dec 2025 09:24:08 +0000</pubDate>
				<category><![CDATA[Android筆記]]></category>
		<category><![CDATA[kotlin]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=7598</guid>

					<description><![CDATA[Kotlin 2.1.0 和 2.2.0 的主要...]]></description>
										<content:encoded><![CDATA[
<p>Kotlin 2.1.0 和 2.2.0 的主要差異在於功能的「成熟度」與「編譯器規範」。</p>



<p>2.1.0 是許多新特性的試用期，而 2.2.0 則是將這些功能正式轉為穩定版，並加強了代碼規範的檢查。</p>



<h3 class="wp-block-heading">核心功能差異</h3>



<ul class="wp-block-list">
<li><strong>語法穩定化</strong>：在 2.1.0 還是預覽的功能，如 <code>when</code> 表達式中的 <code>guard</code> 條件（<code>when (x) { is String if x.isNotEmpty() -> ... }</code>）、非局部 <code>break</code> 與 <code>continue</code>、以及多個 <code>$</code> 符號的字串模板，在 2.2.0 都變成了正式穩定版。</li>



<li><strong>預設介面方法 (JVM)</strong>：2.2.0 改變了 JVM 上的編譯方式。現在介面中的預設方法會直接編譯成 Java 的 <code>default</code> 方法，不再產生額外的 <code>DefaultImpls</code> 類別，這讓 Kotlin 與 Java 的交互更自然，字節碼也更簡潔。</li>



<li><strong>標準庫加強</strong>：2.2.0 正式引入了穩定的 <code>Base64</code> 和 <code>HexFormat</code> API，以後處理編碼就不需要再找第三方函式庫或依賴特定平台。</li>



<li><strong>上下文參數 (Context Parameters)</strong>：2.2.0 引入了這個新功能的預覽版（取代舊的 context receivers），這對簡化依賴注入和提升代碼可讀性很有幫助。</li>
</ul>



<h3 class="wp-block-heading">Android 開發者的影響</h3>



<ul class="wp-block-list">
<li><strong>更嚴格的檢查</strong>：2.2.0 把很多 2.1.0 裡的警告提升到了錯誤級別。例如，如果你在 type aliases 裡用了不該用的 <code>reified</code> 修飾符，2.1.0 只會提醒你，2.2.0 會直接報錯不讓你編譯。</li>



<li><strong>記憶體優化</strong>：Kotlin/Native 在 2.2.0 針對 Apple 平台（iOS）有更細緻的記憶體分配管理，這對 KMP（Kotlin Multiplatform）專案很有感。</li>



<li><strong>編譯器警告管理</strong>：2.2.0 提供了統一的 <code>-Xwarning-level</code> 選項，讓你能更方便地控制專案中警告的顯示方式。</li>
</ul>



<h3 class="wp-block-heading">總結與建議</h3>



<p>如果你是新專案，直接上 <strong>2.2.0</strong> 比較好，因為它的標準庫更完整，語法也更穩定。</p>



<p>但如果是舊專案升級，從 2.1.0 升到 2.2.0 可能會遇到一些編譯報錯（因為 2.2.0 把一些舊的不良語法警告變成了錯誤），需要花點時間修正代碼。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>想了解具體的 <code>when</code> guard 語法範例，或是如何使用新的 Base64 API 嗎？</p>



<h3 class="wp-block-heading">when guard 語法範例</h3>



<p>這是 Kotlin 2.2.0 中最實用的新功能之一。它讓你在匹配類型的同時，可以直接加上額外的判斷條件，不需要在裡面多寫一層 if。</p>



<p>Kotlin</p>



<pre class="wp-block-code"><code>// 2.2.0 的寫法
when (response) {
    is Success if response.data.isEmpty() -&gt; println("成功但沒資料")
    is Success -&gt; println("拿到資料了")
    is Error if response.code == 404 -&gt; println("找不到頁面")
    else -&gt; println("其他錯誤")
}
</code></pre>



<p>在 2.1.0 以前，你必須寫成 <code>is Success -&gt; if (response.data.isEmpty()) ...</code>，程式碼層級會變得比較深。現在這樣寫更直覺，閱讀起來很像在讀英文句子。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Base64 API 範例</h3>



<p>以前在 Android 裡處理 Base64，我們通常會用到 <code>android.util.Base64</code>。但這會導致你的程式碼綁死在 Android 平台上。2.2.0 把這個功能放進了 Kotlin 標準庫。</p>



<pre class="wp-block-code"><code>import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi

@OptIn(ExperimentalEncodingApi::class)
fun testBase64() {
    val text = "Hello Kotlin 2.2"
    
    // 編碼
    val encoded = Base64.encode(text.encodeToByteArray())
    println(encoded)
    
    // 解碼
    val decodedBytes = Base64.decode(encoded)
    println(String(decodedBytes))
}
</code></pre>



<p>這對寫 KMP 跨平台專案的人來說是救星。你可以直接在 commonMain 資料夾裡寫編碼邏輯，不用再幫 iOS 和 Android 各自寫一套實作。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">開發建議</h3>



<p>如果你還在用 2.1.0，建議可以先在 build.gradle 試著改版號。如果出現很多紅色底線，通常是 2.2.0 對於語法規範變得更嚴格。</p>



<p>像是有些隱含的類型轉換或是不規範的泛型用法，2.2.0 都會要求你寫清楚。雖然修起來有點煩，但這能讓你的專案體質更健康，減少執行時期的閃退。</p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2025/12/kotlin-2-1-0-vs-2-2-0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>AsyncTask migrate to Kotlin Coroutines</title>
		<link>https://stackoverflow.max-everyday.com/2025/12/asynctask-migrate-to-kotlin-coroutines/</link>
					<comments>https://stackoverflow.max-everyday.com/2025/12/asynctask-migrate-to-kotlin-coroutines/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 30 Dec 2025 03:00:37 +0000</pubDate>
				<category><![CDATA[Android筆記]]></category>
		<category><![CDATA[kotlin]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=7549</guid>

					<description><![CDATA[AsyncTask 已經在 Android AP...]]></description>
										<content:encoded><![CDATA[
<p>AsyncTask 已經在 Android API 30 正式廢棄。這不是隨意的決定，是因為它太容易造成內存洩漏，且生命週期管理混亂。</p>



<p>現在主流的替代方案是 Kotlin Coroutines 和 RxJava。</p>



<h3 class="wp-block-heading">核心比較與評估</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><td><strong>特性</strong></td><td><strong>Kotlin Coroutines</strong></td><td><strong>RxJava (RxAndroid)</strong></td></tr></thead><tbody><tr><td><strong>官方態度</strong></td><td>Google 官方首選，Jetpack 庫深度整合</td><td>成熟的第三方庫，處於維護期</td></tr><tr><td><strong>學習曲線</strong></td><td>較低，像在寫同步代碼</td><td>極高，需要理解各種操作符與流的概念</td></tr><tr><td><strong>代碼風格</strong></td><td>順序執行，簡潔明瞭</td><td>鏈式調用，容易變成「操作符地獄」</td></tr><tr><td><strong>內存佔用</strong></td><td>極輕量，支持暫停與恢復</td><td>較重，每個訂閱都會創建不少對象</td></tr><tr><td><strong>適用場景</strong></td><td>大多數 Android 開發、UI 交互、簡單請求</td><td>複雜的數據流、多個數據源頻繁組合、背壓處理</td></tr></tbody></table></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">Kotlin Coroutines：現代開發的首選</h3>



<p>如果你使用的是 Kotlin，這幾乎是不二之選。</p>



<p>它的優勢在於結構化並發（Structured Concurrency）。當你在 <code>ViewModel</code> 中使用 <code>viewModelScope.launch</code> 時，如果 <code>ViewModel</code> 銷毀，協程會自動取消。這直接解決了 AsyncTask 最頭痛的內存洩漏問題。</p>



<p>此外，它讓異步代碼看起來像同步代碼。你不需要寫回調，只需要在函數前加一個 <code>suspend</code> 關鍵字，就能在後台執行任務後直接更新 UI，邏輯非常直觀。</p>



<h3 class="wp-block-heading">RxJava：強大的流式處理工具</h3>



<p>RxJava 是一把沉重的重型武器。</p>



<p>如果你的應用需要處理非常複雜的數據流，例如：同時監聽傳感器、網絡狀態與用戶輸入，並將這三者實時組合處理，RxJava 的操作符（如 <code>combineLatest</code> 或 <code>zip</code>）會非常強大。</p>



<p>但在一般的開發場景中，RxJava 的維護成本較高。新人接手時，面對幾十行連在一起的操作符通常會感到崩潰。而且 RxJava 的錯誤處理比較瑣碎，一旦漏掉 <code>onError</code>，程序可能直接崩潰。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">遷移建議</h3>



<p><strong>如果你是從 AsyncTask 遷移：</strong></p>



<ul class="wp-block-list">
<li><strong>無腦選擇 Coroutines：</strong> 這是目前 Android 開發的標準。它能完美替代 <code>doInBackground</code>（對應 <code>withContext(Dispatchers.IO)</code>）和 <code>onPostExecute</code>（協程自動切回主線程）。</li>



<li><strong>考慮團隊成本：</strong> 協程的上手時間通常只要幾天，而 RxJava 可能需要幾週甚至幾個月才能熟練應用。</li>



<li><strong>庫的整合：</strong> 現在的 Retrofit、Room 等主流庫都已經原生支持 Coroutines，遷移阻力極小。</li>
</ul>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2025/12/asynctask-migrate-to-kotlin-coroutines/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>android AsyncTask 除了遷移至 Kotlin Coroutines, 可以改用  Runnable 或 Handler改寫?</title>
		<link>https://stackoverflow.max-everyday.com/2025/12/android-asynctask-runnable-handler/</link>
					<comments>https://stackoverflow.max-everyday.com/2025/12/android-asynctask-runnable-handler/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 30 Dec 2025 02:58:40 +0000</pubDate>
				<category><![CDATA[Android筆記]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[kotlin]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=7550</guid>

					<description><![CDATA[替代方案的可行性 可以用 Runnable 搭配...]]></description>
										<content:encoded><![CDATA[
<h3 class="wp-block-heading">替代方案的可行性</h3>



<p>可以用 Runnable 搭配 Handler 改寫。<br>這在協程普及前是很常見的做法。<br>原理是自己管理執行緒切換。<br>Runnable 負責定義要做的任務。<br>Handler 負責跨執行緒溝通。</p>



<h3 class="wp-block-heading">具體的執行方式</h3>



<p>你需要先準備一個 ExecutorService 執行緒池。<br>把耗時工作放進 Runnable 裡面。<br>透過 executor.execute 丟到背景執行。<br>當資料處理完畢。<br>再利用 Handler 的 post 方法切換回主執行緒。<br>這樣就能安全地更新介面元件。</p>



<pre class="wp-block-code"><code>ExecutorService executor = Executors.newSingleThreadExecutor();
Handler handler = new Handler(Looper.getMainLooper());

executor.execute(new Runnable() {
    @Override
    public void run() {
        // 這裡跑背景任務
        final String data = "載入完成";

        handler.post(new Runnable() {
            @Override
            public void run() {
                // 這裡更新 UI
                textView.setText(data);
            }
        });
    }
});</code></pre>



<h3 class="wp-block-heading">優缺點評估</h3>



<p>這種做法控制權最高。<br>適合不支援 Kotlin 的老舊專案。<br>但是程式碼會變得很碎。<br>也要特別注意記憶體洩漏問題。<br>建議將 Handler 宣告為靜態類別。<br>才不會意外持有 Activity 的引用。<br>如果是簡單的任務。<br>這比導入整個協程庫還要輕量。</p>



<p>可以再利用 WeakReference 來優化這段程式碼以防止記憶體洩漏.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2025/12/android-asynctask-runnable-handler/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>kotlin 空安全處理是什麼?</title>
		<link>https://stackoverflow.max-everyday.com/2025/12/kotlin-null-safety/</link>
					<comments>https://stackoverflow.max-everyday.com/2025/12/kotlin-null-safety/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 30 Dec 2025 02:51:18 +0000</pubDate>
				<category><![CDATA[Android筆記]]></category>
		<category><![CDATA[kotlin]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=7546</guid>

					<description><![CDATA[Kotlin 的空安全（Null Safety）...]]></description>
										<content:encoded><![CDATA[
<p>Kotlin 的空安全（Null Safety）是為了根除 Java 最常見的 NullPointerException (NPE)。在 Kotlin 中，類型系統會強制區分「可以為空」和「不可以為空」的變量。</p>



<h3 class="wp-block-heading">可空類型與不可空類型</h3>



<p>在定義變量時，Kotlin 默認所有類型都是不可空的。如果你嘗試給它賦值 null，編譯器會直接報錯。</p>



<p>如果你希望一個變量可以存放 null，必須在類型後面加上問號。例如 <code>String</code> 不能為空，而 <code>String?</code> 則代表可以存放字符串或 null。</p>



<p>這種機制讓潛在的崩潰問題在編譯階段就被抓出來，而不是等到用戶使用時才發生。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">安全調用與強行轉換</h3>



<p>當你有一個可空變量時，你不能直接調用它的方法。你有幾種處理方式：</p>



<ol start="1" class="wp-block-list">
<li><strong>安全調用符 <code>?.</code></strong>：這是最常用的。如果變量是 null，調用會直接返回 null，而不會崩潰。</li>



<li><strong>非空斷言 <code>!!</code></strong>：如果你百分之百確定變量不為空，可以用這個。但如果猜錯了，程序會立刻崩潰。這通常是不建議的做法。</li>
</ol>



<h3 class="wp-block-heading">Elvis 運算符 <code>?:</code></h3>



<p>這個符號用來提供「默認值」。當左邊的表達式結果為 null 時，它會執行右邊的內容。</p>



<p>這在處理 UI 顯示時非常好用，比如 <code>val name = user?.name ?: "匿名用戶"</code>。如果用戶數據不存在，就會直接顯示默認稱呼。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">類型轉換與檢查</h3>



<p>Kotlin 會自動進行類型轉換（Smart Cast）。如果你已經用 <code>if</code> 檢查過變量不為空，在該代碼塊範圍內，你可以直接把它當成不可空類型來用，不需要再寫問號。</p>



<p>這讓代碼保持乾淨，同時又具備極高的安全性。這就像是在寫代碼前加了一層過濾網，過濾掉所有可能讓程序當機的空指針。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2025/12/kotlin-null-safety/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ubuntu 安裝 android studio</title>
		<link>https://stackoverflow.max-everyday.com/2025/12/ubuntu-install-android-studio/</link>
					<comments>https://stackoverflow.max-everyday.com/2025/12/ubuntu-install-android-studio/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 09 Dec 2025 16:44:31 +0000</pubDate>
				<category><![CDATA[Android筆記]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=7364</guid>

					<description><![CDATA[好的，當您在 Ubuntu 環境下取得 Andr...]]></description>
										<content:encoded><![CDATA[
<p>好的，當您在 Ubuntu 環境下取得 <strong>Android Studio</strong> 的壓縮檔 <code>android-studio-2025.2.2.7-linux.tar.gz</code> 之後，詳細的安裝步驟如下。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f6e0.png" alt="🛠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 1. 前置作業：確保 Java 已安裝</h2>



<p>Android Studio 需要 <strong>Java Development Kit (JDK)</strong> 才能運行。由於您之前決定使用 Java 21，請確保它已正確安裝並設定 <code>JAVA_HOME</code>。</p>



<pre class="wp-block-code"><code>java -version
</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>確認：</strong> 輸出顯示 Java 21 或更高版本。</p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4e5.png" alt="📥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 2. 解壓縮 Android Studio</h2>



<p>建議將 Android Studio 安裝到 <code>/opt</code> 目錄下，這是 Linux 系統中用於安裝非標準套件的推薦位置。</p>



<h3 class="wp-block-heading">步驟 A：移動與解壓縮</h3>



<ol start="1" class="wp-block-list">
<li><strong>移動檔案 (如果檔案在 Downloads 資料夾)：</strong>Bash<br><code># 假設檔案在您的 Downloads 資料夾中 </code><br><code>cd ~/Downloads </code><br><code># 將檔案移到一個更合適的位置 </code><br><code>sudo mv android-studio-2025.2.2.7-linux.tar.gz /opt/</code></li>



<li><strong>進入 <code>/opt</code> 目錄：</strong><br><code>cd /opt/</code></li>



<li><strong>解壓縮檔案：</strong><br><code># 使用 sudo 權限解壓縮 </code><br><code>sudo tar -xvf android-studio-2025.2.2.7-linux.tar.gz </code><br>解壓縮後，會生成一個名為 <code>android-studio</code> 或類似名稱的資料夾。</li>
</ol>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f680.png" alt="🚀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 3. 啟動 Android Studio</h2>



<h3 class="wp-block-heading">步驟 A：運行啟動腳本</h3>



<p>進入解壓縮後的 <code>bin</code> 資料夾，並執行啟動腳本。</p>



<pre class="wp-block-code"><code># 進入 bin 目錄 (請根據您的實際資料夾名稱調整)
cd /opt/android-studio/bin 

# 執行 studio.sh 腳本
./studio.sh
</code></pre>



<h3 class="wp-block-heading">步驟 B：設定精靈 (Setup Wizard)</h3>



<p>當您首次啟動時，Android Studio 會引導您完成幾個設定步驟：</p>



<ol start="1" class="wp-block-list">
<li><strong>匯入設定</strong>：選擇是否匯入舊版 Android Studio 的設定。</li>



<li><strong>安裝類型</strong>：選擇 <strong>Standard</strong> 即可。</li>



<li><strong>UI 主題</strong>：選擇您喜歡的主題（例如 Light 或 Darcula）。</li>



<li><strong>下載組件</strong>：設定精靈會列出需要額外下載的組件，包括：
<ul class="wp-block-list">
<li><strong>Android SDK</strong></li>



<li><strong>Android SDK Platform-Tools</strong></li>



<li><strong>Android Virtual Device (AVD)</strong></li>
</ul>
</li>



<li>點擊 <strong>Finish</strong>，等待所有必要的組件下載並安裝完成。</li>
</ol>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="928" height="716" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2025/12/Screenshot-from-2025-12-10-00-42-28.png?v=1765298593" alt="" class="wp-image-7366" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2025/12/Screenshot-from-2025-12-10-00-42-28.png?v=1765298593 928w, https://stackoverflow.max-everyday.com/wp-content/uploads/2025/12/Screenshot-from-2025-12-10-00-42-28-600x463.png?v=1765298593 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2025/12/Screenshot-from-2025-12-10-00-42-28-768x593.png?v=1765298593 768w" sizes="(max-width: 928px) 100vw, 928px" /></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f517.png" alt="🔗" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 4. 建立桌面捷徑 (可選，但強烈建議)</h2>



<p>手動安裝時，您需要手動建立一個桌面捷徑（<code>.desktop</code> 檔案），以便您可以從應用程式選單中啟動 Android Studio。</p>



<ul start="1" class="wp-block-list">
<li><strong>在 Android Studio 介面中建立捷徑：</strong>
<ul class="wp-block-list">
<li>啟動 Android Studio 後，在頂部菜單欄選擇 <strong>Configure</strong> (或 File &gt; Settings &gt; Appearance &amp; Behavior &gt; System Settings &gt; <strong>Menus and Toolbars</strong>，舊版在 <strong>Help</strong>)。</li>



<li>選擇 <strong>Create Desktop Entry&#8230;</strong>。</li>



<li>勾選 <strong>For all users</strong> (如果需要) 並點擊 <strong>OK</strong>。<br></li>
</ul>
</li>
</ul>



<figure class="wp-block-image size-full"><img decoding="async" width="820" height="876" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2025/12/Screenshot-from-2025-12-10-01-40-44.png?v=1765302995" alt="" class="wp-image-7373" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2025/12/Screenshot-from-2025-12-10-01-40-44.png?v=1765302995 820w, https://stackoverflow.max-everyday.com/wp-content/uploads/2025/12/Screenshot-from-2025-12-10-01-40-44-562x600.png?v=1765302995 562w, https://stackoverflow.max-everyday.com/wp-content/uploads/2025/12/Screenshot-from-2025-12-10-01-40-44-768x820.png?v=1765302995 768w" sizes="(max-width: 820px) 100vw, 820px" /></figure>



<ul start="1" class="wp-block-list">
<li><strong>手動建立捷徑 (如果步驟 1 失敗)：</strong></li>
</ul>



<pre class="wp-block-code"><code># 創建一個 .desktop 檔案
nano ~/.local/share/applications/jetbrains-studio.desktop

# 貼入以下內容 (請確保 Exec 和 Icon 路徑正確)
&#91;Desktop Entry]
Version=1.0
Type=Application
Name=Android Studio
Icon=/opt/android-studio/bin/studio.png # 圖標路徑
Exec="/opt/android-studio/bin/studio.sh" %f # 執行路徑
Comment=The official IDE for Android application development
Categories=Development;IDE;
Terminal=false
StartupWMClass=jetbrains-android-studio</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f511.png" alt="🔑" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 5. 安裝 Android NDK (用於編譯 ics-openvpn)</h2>



<p>您最終的目標是編譯 <code>ics-openvpn</code>，這涉及到 C/C++ 的本地程式碼，因此您必須安裝 <strong>Android NDK</strong> (Native Development Kit)。</p>



<ol start="1" class="wp-block-list">
<li>在 Android Studio 啟動後，開啟或創建一個專案。</li>



<li>進入頂部菜單：<strong>Settings</strong> (或 <strong>File &gt; Settings</strong>)。</li>



<li>導航到：<strong>Appearance &amp; Behavior &gt; System Settings &gt; Android SDK</strong>。</li>



<li>切換到 <strong>SDK Tools</strong> 標籤頁。</li>



<li>勾選 <strong>Android NDK (Side by side)</strong>。</li>



<li>點擊 <strong>Apply</strong>，讓 Android Studio 下載並安裝 NDK。</li>
</ol>



<p>完成這些步驟後，您的 Ubuntu 環境就具備了完整的 Android Studio 。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2025/12/ubuntu-install-android-studio/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>在舊的 .java 專案加入 .kt 流程</title>
		<link>https://stackoverflow.max-everyday.com/2025/12/%e5%9c%a8%e8%88%8a%e7%9a%84-java-%e5%b0%88%e6%a1%88%e5%8a%a0%e5%85%a5-kt-%e6%b5%81%e7%a8%8b/</link>
					<comments>https://stackoverflow.max-everyday.com/2025/12/%e5%9c%a8%e8%88%8a%e7%9a%84-java-%e5%b0%88%e6%a1%88%e5%8a%a0%e5%85%a5-kt-%e6%b5%81%e7%a8%8b/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Fri, 05 Dec 2025 12:48:43 +0000</pubDate>
				<category><![CDATA[Android筆記]]></category>
		<category><![CDATA[機器學習筆記]]></category>
		<category><![CDATA[Android]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=7300</guid>

					<description><![CDATA[當您在一個原本純 Java 或沒有啟用 Kotl...]]></description>
										<content:encoded><![CDATA[
<p>當您在一個原本<strong>純 Java</strong> 或<strong>沒有啟用 Kotlin 支援</strong>的 Android 專案中加入 <strong><code>.kt</code> 檔案</strong>時，編譯器可能會因為缺少必要的設定或相依性而報錯。</p>



<p>您遇到的錯誤訊息：</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>&#8220;Starting in Kotlin 2.0, the Compose Compiler Gradle plugin is required when compose is enabled.&#8221;</p>
</blockquote>



<p>這表示編譯器<strong>誤以為</strong>您的專案正在使用 <strong>Jetpack Compose</strong> (Android 的現代 UI 工具包)，但缺少了 <strong>Compose Compiler 插件</strong>。</p>



<p>google 官方文件:<br><a href="https://developer.android.com/develop/ui/compose/compiler?hl=zh-tw">https://developer.android.com/develop/ui/compose/compiler?hl=zh-tw</a></p>



<p>有看沒有懂&#8230;</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f6e0.png" alt="🛠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 解決方案：檢查並新增 Kotlin 支援</h2>



<p>解決這個問題的關鍵是確保您的專案正確地配置了 <strong>Kotlin Android Plugin</strong> 和 <strong>Kotlin Standard Library</strong>。</p>



<h3 class="wp-block-heading">步驟 1: 檢查 Project-level 的 <code>build.gradle</code> (或 <code>settings.gradle.kts</code>)</h3>



<p>確保您的專案級別 (Project-level) 的設定檔案中，已經加入了 <strong>Kotlin Gradle Plugin</strong> 的 Classpath。</p>



<p><strong>在 <code>build.gradle</code> (Project 級別):</strong></p>



<pre class="wp-block-code"><code>buildscript {
    ext.kotlin_version = '1.9.22' // 根據您的專案需求使用最新或穩定版本
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        // ... 其他 dependencies
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // &lt;--- 確保有這行
    }
}</code></pre>



<ul class="wp-block-list">
<li><strong>注意：</strong> 如果您使用的是新的 Gradle 設定方式 (Kotlin DSL)，則應在 <strong><code>settings.gradle.kts</code></strong> 中確保有這個 Plugin。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">步驟 2: 編輯 Module-level 的 <code>build.gradle</code> (通常是 <code>app/build.gradle</code>)</h3>



<p>這是最重要的步驟，您必須在編寫 Kotlin 程式碼的模組 (Module) 中<strong>應用 (apply)</strong> Kotlin 插件並新增必要的相依性。</p>



<p><strong>在 <code>app/build.gradle</code> (Module 級別):</strong></p>



<ol class="wp-block-list">
<li><strong>套用 Plugin (Apply Plugin):</strong> 在檔案頂部，新增 <code>kotlin-android</code> 插件。 <code>// 確保這裡有這兩行 plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' // &lt;--- 確保有這行 // 如果您的專案是純 Java 且您剛剛加入 kotlin 檔案，您可能會需要將： // apply plugin: 'com.android.application' // 改為新的 `plugins` 區塊並加入 'org.jetbrains.kotlin.android' }</code></li>



<li><strong>新增 Kotlin 標準庫 (Standard Library):</strong> 在 <code>dependencies</code> 區塊中，新增 Kotlin 標準庫。 <code>dependencies { // ... 其他 dependencies// 新增 Kotlin 標準庫相依性 implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" // &amp;lt;--- 確保有這行 // 如果您使用 Kotlin 與 Activity 或 Fragment 交互，建議也加入 KTX implementation "androidx.core:core-ktx:1.12.0" // 確保您使用的版本是最新的}</code></li>
</ol>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">步驟 3: 移除或禁用 Compose 相關設定 (如果沒有使用 Compose)</h3>



<p>如果您的專案<strong>確定沒有</strong>使用 Jetpack Compose，但錯誤訊息仍然出現，這可能是因為您在 <code>app/build.gradle</code> 中<strong>意外啟用了</strong> Compose 設定。</p>



<p><strong>請檢查 <code>android</code> 區塊：</strong></p>



<ul class="wp-block-list">
<li>尋找以下設定，如果存在且您沒有使用 Compose，請<strong>將其移除或註解掉</strong>：<br><code>gradle android { // ... buildFeatures { // 這個可能會導致錯誤，如果專案沒有使用 Compose UI compose true // &lt;--- 移除或改成 false } // ... }</code></li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f680.png" alt="🚀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 最終動作</h2>



<p>完成上述步驟後，請在 Android Studio 中點擊 <strong>Sync Now</strong> (或 Gradle 檔案修改後自動出現的同步提示)，讓 Gradle 下載並應用新的設定。這樣應該就能解決編譯器報錯，並允許 Java 程式碼正確地與您的 <code>.kt</code> 檔案（例如 <code>BaseActivity.kt</code>）互動。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2025/12/%e5%9c%a8%e8%88%8a%e7%9a%84-java-%e5%b0%88%e6%a1%88%e5%8a%a0%e5%85%a5-kt-%e6%b5%81%e7%a8%8b/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[Android] 如何通過 Google Play 16 KB page size 要求</title>
		<link>https://stackoverflow.max-everyday.com/2025/12/android-google-play-16-kb-page-size/</link>
					<comments>https://stackoverflow.max-everyday.com/2025/12/android-google-play-16-kb-page-size/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Fri, 05 Dec 2025 09:13:43 +0000</pubDate>
				<category><![CDATA[Android筆記]]></category>
		<category><![CDATA[Android]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=7298</guid>

					<description><![CDATA[遇到的錯誤是因為 OpenVPN 的啟動機制與 ...]]></description>
										<content:encoded><![CDATA[
<p>遇到的錯誤是因為 OpenVPN 的啟動機制與 <code>useLegacyPackaging = false</code> 互相衝突。</p>



<h3 class="wp-block-heading">核心原因分析</h3>



<ol start="1" class="wp-block-list">
<li>useLegacyPackaging = false 的作用：這個設定告訴 Android 建置系統（AGP），不要把 APK 裡面的 .so 檔案解壓縮到手機的 /data/app/&#8230;/lib/ 目錄下。它會讓系統直接從 APK 包裡面讀取這些函式庫（為了節省手機空間）。這就是為什麼你用 adb ls 看到目錄是空的。</li>



<li>OpenVPN (ics-openvpn) 的運作方式：Cake-VPN（以及大多數基於 ics-openvpn 的專案）並不是像一般 App 那樣只用 System.loadLibrary。它實際上是把 libovpnexec.so 當作一個可執行檔 (Executable) 來跑。Java 程式碼會嘗試去執行類似這樣的指令：/data/app/&#8230;/lib/arm64/libovpnexec.so &#8230;。因為你設了 false，檔案不存在於那個路徑上，導致 java.io.IOException，程式崩潰。</li>
</ol>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">解決方案</h3>



<p>為了同時修復崩潰問題並通過 Google Play 的 16 KB Page Size 檢查，你需要採取<strong>兩步走</strong>的策略：</p>



<h4 class="wp-block-heading">第一步：修復崩潰（改回 True）</h4>



<p>你必須允許 Native Library 被解壓縮，因為這個 App 的架構依賴於實體檔案的存在。</p>



<p>修改 <code>build.gradle</code>：</p>



<p>Groovy</p>



<pre class="wp-block-code"><code>android {
    packaging {
        jniLibs {
            // 改回 true (或者直接刪除這段，因為預設通常是 true)
            // 這樣 Android 才會把 .so 檔解壓到 /data/app/.../lib 目錄
            useLegacyPackaging = true
        }
    }
    // ... 其他設定
}
</code></pre>



<p><em>做完這步，你的 App 就能正常啟動了，但可能還過不了 16 KB 檢查。接下來看第二步。</em></p>



<h4 class="wp-block-heading">第二步：真正解決 16 KB 對齊問題 (Rebuild NDK)</h4>



<p>既然我們必須解壓縮 <code>.so</code> 檔（<code>useLegacyPackaging = true</code>），那麼 Google Play 的要求就是：<strong>這些解壓縮出來的 ELF (.so) 檔案，其 Segment 必須是 16 KB 對齊的。</strong></p>



<p>這代表你不能只改 <code>build.gradle</code> 的 packaging 設定，你需要<strong>重新編譯 C/C++ 原始碼</strong>，並加上 Linker flags。</p>



<p>請在你的 <code>build.gradle</code> (Module level) 加入以下設定，強制 NDK 使用 16KB 對齊：</p>



<pre class="wp-block-code"><code>android {
    defaultConfig {
        // ... 其他設定
        
        externalNativeBuild {
            cmake {
                // 這是關鍵：告訴 Linker 使用 16KB (16384 bytes) 的 Page Size
                cppFlags "-Wl,-z,max-page-size=16384"
                arguments "-DANDROID_STL=c++_shared"
            }
            // 如果你是用 ndk-build (Android.mk)，則需要用不同的參數，但大多數現在都用 cmake
        }

        ndk {
            abiFilters "arm64-v8a", "x86_64"
        }
    }
}
</code></pre>



<p>注意：</p>



<p>如果你的專案不是透過 CMake 編譯（例如直接引用 libs/ 資料夾下的預編譯 .so 檔），那麼你無法透過 Gradle 修改對齊。你必須找到這些 .so 檔的原始碼重新編譯，或者尋找已經支援 16KB Alignment 的第三方庫版本。</p>



<h3 class="wp-block-heading">驗證方法</h3>



<p>修改並重新 Build 出 APK 後，請使用 Android SDK 提供的工具 <code>zipinfo</code> 或 Linux 的 <code>readelf</code> 來檢查。</p>



<ol start="1" class="wp-block-list">
<li><strong>解壓 APK</strong>，取出裡面的 <code>.so</code> 檔。</li>



<li>在終端機執行：<br><code>readelf -l libovpnexec.so | grep LOAD</code></li>



<li>檢查輸出中的 <code>Align</code> 欄位。如果是 <code>0x4000</code> (16384)，代表成功；如果是 <code>0x1000</code> (4096)，代表失敗。</li>
</ol>



<h3 class="wp-block-heading">總結</h3>



<ol start="1" class="wp-block-list">
<li>把 <code>useLegacyPackaging</code> 改回 <strong><code>true</code></strong> (讓 OpenVPN 找得到檔案)。</li>



<li>在 <code>externalNativeBuild</code> 加入 <strong><code>-Wl,-z,max-page-size=16384</code></strong> (讓編譯出來的檔案符合 Google 規範)。</li>
</ol>



<p></p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2025/12/android-google-play-16-kb-page-size/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
