ทำ Android dev บน MIUI/HyperOS แล้ว adb ทำงานไม่ได้ — กับดักบน Windows
adb ขึ้น INSTALL_FAILED, ไฟล์ screenshot เป็น 0 ไบต์, path /sdcard เพี้ยนไปโผล่ในโฟลเดอร์ Git — สามอาการนี้ไม่ได้แปลว่าเครื่องพัง แต่คือ Windows, Git Bash, PowerShell และ MIUI ต่างคนต่างแทรกมือใส่ adb กันคนละจุด
พอดีช่วงนี้ผมทำแอป Android ที่เขียนด้วย Jetpack Compose มี backend เป็น Laravel แล้วยิงลงเครื่องทดสอบเป็น Xiaomi ที่รัน MIUI/HyperOS โดยมี dev box เป็น Windows — พอเริ่มจริงก็เจอว่า adb "ทำงานไม่ได้" อยู่หลายจุด ทั้งที่ตัว adb เองปกติดี ปัญหาคือ Windows, Git Bash, PowerShell และ MIUI ต่างคนต่างเข้ามาแทรกกลางระหว่างคำสั่งกับเครื่องคนละท่า กว่าจะไล่ครบผมเสียเวลาไปเยอะ เลยรวมกับดักที่เจอจริงมาเล่าให้ฟัง พร้อมคำสั่งที่ใช้แล้วผ่าน
ชื่อ package (com.example.pos), serial เครื่อง และ path ในบทความเป็นตัวอย่าง ส่วนคำสั่งกับอาการคือของจริงทั้งหมด
กับดักที่ 1: Git Bash แปลง /sdcard/ ให้เพี้ยน
อาการแรกคือสั่ง adb push ลง /sdcard/Download/ แล้วไฟล์หายไปเฉยๆ ไม่ได้อยู่บนเครื่อง พอไปไล่ดูถึงเจอว่ามันไปโผล่บนดิสก์ที่ C:/Program Files/Git/sdcard/Download/ แทน สาเหตุคือ Git Bash (MSYS) แปลง path แบบ POSIX ให้อัตโนมัติ พอเจอ /sdcard/... มันเข้าใจว่าเป็น absolute path บน Windows เลยเติม prefix ของ Git ให้
ทางที่ผมใช้คือย้ายไปยิง adb push จาก PowerShell แทน — path ฝั่งอุปกรณ์ไม่โดนแปลงเลย:
$device = "abc123def456"
$apk = "app\build\outputs\apk\debug\app-debug.apk"
adb -s $device push $apk /sdcard/Download/app-beta.apk
ถ้าจำเป็นต้องอยู่ใน Git Bash จริงๆ ให้ปิดการแปลง path ด้วยตัวแปร MSYS_NO_PATHCONV เฉพาะคำสั่งนั้น:
MSYS_NO_PATHCONV=1 adb -s "$device" push "$apk" /sdcard/Download/app-beta.apk
ส่วนตัวผมเลือกทำงาน adb ทั้งหมดใน PowerShell ไปเลย จำกฎน้อยกว่า ไม่ต้องมานั่งจำว่าคำสั่งไหนมี path ฝั่งเครื่องบ้าง
กับดักที่ 2: PowerShell ทำไฟล์ screenshot เป็น 0 ไบต์
พอจะเก็บภาพหน้าจอเครื่องมาแนบรายงานเทสต์ ผมสั่งตามที่เคยชินบน Linux:
adb exec-out screencap -p > .tmp\screen.png
ได้ไฟล์ออกมาแต่เปิดไม่ขึ้น บางทีก็ 0 ไบต์ ต้นเหตุคือเครื่องหมาย > ใน PowerShell มันคือ Out-File ที่ยัด text encoding ให้ทุกอย่างที่ไหลผ่าน — พอเป็นสตรีมไบนารีอย่าง PNG ไบต์เลยถูกแปลงพัง ไม่ใช่ไฟล์ภาพเดิมอีกต่อไป
ไพป์ผ่าน PowerShell ตรงๆ
ให้ไบต์ของ PNG ไหลผ่าน > ของ PowerShell — โดน text encoding เหยียบทุกไบต์ ได้ไฟล์ 0 ไบต์หรือเปิดไม่ขึ้น
ให้เครื่องเซฟเองแล้วค่อยดึง
screencap เขียนลง /sdcard บนเครื่องก่อน แล้ว adb pull กลับมา — ไบต์ไม่ผ่านมือ PowerShell เลย ภาพครบทุกครั้ง
วิธีที่ผมใช้แล้วได้ภาพครบทุกครั้งคือให้เครื่องเซฟลง /sdcard ก่อน แล้วค่อย adb pull กลับมา ไบต์ไม่ต้องผ่านมือ PowerShell เลย:
adb -s $device shell screencap -p /sdcard/screen.png
adb -s $device pull /sdcard/screen.png .tmp\screen.png
adb -s $device shell rm /sdcard/screen.png
ถ้าอยากไพป์ตรงจริงๆ ให้ใช้ redirect ของ cmd.exe แทน เพราะ cmd ไม่ยุ่งกับ encoding ของสตรีม:
adb exec-out screencap -p > screen.png
อีกจุดที่พลาดกันบ่อยคืออย่าเซฟลง C:\temp เพราะโฟลเดอร์นั้นอาจไม่มีอยู่จริง แล้ว PowerShell สร้างให้ไม่เหมือน Git Bash — เซฟลงโฟลเดอร์ในโปรเจกต์ (เช่น .tmp\) ที่มีอยู่แน่ๆ จะชัวร์กว่า
กับดักที่ 3: MIUI บล็อกการติดตั้งครั้งแรก
พอ build เสร็จจะติดตั้งลงเครื่องรอบแรก MIUI/HyperOS เด้งกลับมาแบบนี้:
adb: failed to install fresh.apk: Failure [INSTALL_FAILED_USER_RESTRICTED: Install canceled by user]
ทั้งที่ป๊อปอัปยืนยันบนเครื่องยังไม่ทันโผล่ด้วยซ้ำ สาเหตุคือ toggle "Install via USB" ใน Developer options ปิดอยู่ พอปิดไว้ MIUI จะบล็อกการติดตั้งครั้งแรกผ่าน adb ทุกกรณี ตัว toggle นี้ต้องล็อกอิน Mi Account และมีซิมในเครื่องถึงจะเปิดได้ ซึ่งเครื่องเทสต์บางเครื่องไม่มีซิมเสียบ ทางที่ผมใช้เลยเป็นการหลบด่านแทน — push APK ลง /sdcard แล้วแตะติดตั้งด้วยมือผ่านแอป Files บนเครื่อง เพราะการติดตั้งที่ผู้ใช้กดเองไม่ติดด่านนี้
- สั่ง assembleDebugAndroidTest ให้ได้ทั้ง APK แอปและ APK เทสต์ออกมา
- ติดตั้งครั้งแรกด้วยมือ — push APK ลง /sdcard แล้วแตะติดตั้งผ่านแอป Files บนเครื่อง (ข้ามด่าน MIUI)
- ครั้งต่อไปใช้ adb install -r ทับได้เงียบๆ ไม่โดนบล็อกอีก
- ยิงเทสต์ตรงด้วย am instrument ไม่ต้องพึ่ง gradle task
พอติดตั้งผ่านรอบแรกไปแล้ว การอัปเดตครั้งถัดไปใช้ธง -r (reinstall ทับ) ได้เงียบๆ MIUI ไม่ถามอีก:
.\gradlew.bat assembleDebugAndroidTest
# รอบแรก: push ให้ผู้ใช้ติดตั้งด้วยมือผ่าน Files
adb -s $device push app\build\outputs\apk\androidTest\debug\app-debug-androidTest.apk /sdcard/Download/
# รอบถัดไป: ทับได้เลยด้วย -r
adb -s $device install -r app\build\outputs\apk\androidTest\debug\app-debug-androidTest.apk
ที่สำคัญคืออย่าใช้ gradle task connectedAndroidTest บน MIUI — ให้ยิง am instrument ตรงแทน:
# รันทั้งชุด
adb -s $device shell am instrument -w -r -e debug false com.example.pos.test/androidx.test.runner.AndroidJUnitRunner
# รันเฉพาะคลาสเดียว
adb -s $device shell am instrument -w -r -e class com.example.pos.OwnerHomeTest com.example.pos.test/androidx.test.runner.AndroidJUnitRunner
-w คือรอจนจบ, -r คือ raw output ที่ parse ต่อได้
task นี้จะถอนแอปหลักออกก่อนแล้วค่อยติดตั้งใหม่ พอการติดตั้งใหม่โดนด่าน MIUI ปัดตก เครื่องจะเหลือทั้งแอปหลักและแอปเทสต์ หายไปทั้งคู่ — ต้องมานั่งติดตั้งด้วยมือใหม่หมด ผมโดนมาแล้ว ให้ยิง am instrument ตรงแทนตั้งแต่แรก
อีกจุดที่เจอตอนรันชุดเทสต์ยาวเกินห้านาที คือ MIUI พักหน้าจอแล้ว activity หลุด focus ทำให้เทสต์ที่แตะหรือ scroll ล้มเป็นชุด กันด้วยการปลุกจอค้างไว้ก่อนเริ่ม:
adb -s $device shell input keyevent KEYCODE_WAKEUP
adb -s $device shell svc power stayon true
กับดักที่ 4: here-string ของ PowerShell กลืน backslash เวลาปั้นโค้ด PHP
กับดักตัวสุดท้ายอยู่ฝั่ง backend เวลาจะปั้น seed / test fixture ของ Laravel แล้วส่งขึ้นเซิร์ฟเวอร์ ผมเคยลองยัดโค้ด PHP หลายบรรทัดผ่าน here-string ของ PowerShell แล้ว pipe ขึ้น ssh — ปรากฏว่า backslash ในโค้ดโดนกลืนหายตั้งแต่ต้นทาง พอขึ้นไปถึงเซิร์ฟเวอร์ PHP จึงเป็น parse error แบบงงๆ ที่หาต้นเหตุยากมาก เพราะไฟล์ในเครื่องกับที่ส่งขึ้นไปหน้าตาไม่เหมือนกัน
backslash และตัว escape ในโค้ดจะโดนกลืน พอส่งขึ้นเซิร์ฟเวอร์ PHP จะ parse error แบบไล่ต้นเหตุยาก เพราะโค้ดต้นทางกับปลายทางไม่ตรงกัน — เขียนลงไฟล์ในเครื่องก่อนแล้วค่อย scp ขึ้นไปรัน ปลอดภัยกว่ามาก
วิธีที่ผมใช้คือเขียนโค้ดลงเป็นไฟล์จริงในเครื่องก่อน (ผ่าน editor ไม่ผ่าน here-string) แล้วค่อย scp ขึ้นไปรัน เสร็จแล้วลบไฟล์ชั่วคราวทิ้งทุกครั้ง:
scp .\scripts\seed.php host:/home/deploy/seed.php
ssh host "php /home/deploy/seed.php && rm -f /home/deploy/seed.php"
จุดที่ต้องระวังในไฟล์นั้นคืออย่าใส่ path แบบ Windows (D:\... หรือ C:\...) ลงในสคริปต์ที่จะไปรันบน Linux เด็ดขาด เพราะ backslash จะกลายเป็นชื่อไฟล์ประหลาดใน /tmp แล้วบางเซิร์ฟเวอร์มี monitor คอยจับชื่อไฟล์แปลกๆ อยู่ ใช้ path แบบ Linux ให้หมด แล้วเก็บกวาดไฟล์ชั่วคราวทุกครั้งหลังรันเสร็จ
สรุปลำดับที่ยิงแล้วไม่พลาด
พอไล่ครบทั้งสี่จุดแล้ว pipeline ที่ผมใช้ประจำจะเป็นแบบนี้ — เลือกเครื่องมือให้ถูกตัวในแต่ละสเต็ป ไม่สลับไปมา:
หัวใจของทุกกับดักคือเรื่องเดียวกันหมด — คำสั่ง adb ไม่ได้วิ่งตรงถึงเครื่องอย่างที่คิด แต่มีชั้นของ Windows, Git Bash และ PowerShell แทรกอยู่ตรงกลาง แล้ว MIUI ก็มีด่านของมันอีกชั้น พอรู้ว่าใครแทรกตรงไหนก็เลี่ยงได้หมด ใครจะปรับ flow ตามเครื่องและ ROM ของตัวเองก็ได้ตามสะดวกครับ ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ
เคยเจองานแบบนี้ไหม? ติดตรงไหน?
มาแลกวิธีกันต่อในบอร์ด LinuxThai



ความคิดเห็น 0
ยังไม่มีความคิดเห็น — เป็นคนแรกเลย