Skip to main content

สคริปต์ที่กำหนดเอง

สคริปต์ในตัวครอบคลุมงานทั่วไปอยู่แล้ว แต่เมื่อคุณต้องการสิ่งที่มันไม่มี — ขั้นตอนที่เรียงต่างออกไป หน้าจอที่มันไม่เคยแตะ หรือแอปที่ไม่ใช่ TikTok และ Instagram — คุณเขียนเองได้ด้วยภาษาใดก็ได้ แล้วให้ TikMatrix ส่งมอบเครื่องให้คุณ

ข้อกำหนด

ข้อกำหนดด้านสิทธิ์การใช้งาน

สคริปต์ที่กำหนดเองต้องใช้แพ็กเกจ Pro, Team หรือ Business แพ็กเกจ Starter ไม่มีสิทธิ์เข้าถึง

จำนวนอุปกรณ์ในแพ็กเกจของคุณคือขีดจำกัดการทำงานพร้อมกันด้วย: แพ็กเกจ Pro (20 เครื่อง) ขับเคลื่อนโทรศัพท์ได้ 20 เครื่องพร้อมกัน ไม่ว่าจะผ่านงานในตัว สคริปต์ที่กำหนดเอง หรือทั้งสองอย่างผสมกัน

สองวิธีในการรันสคริปต์

แบบอิสระ

คุณรันโปรแกรมเอง TikMatrix แค่ให้ยืมอุปกรณ์

from tikmatrix import TikMatrix

client = TikMatrix()

for device in client.devices():
if device["busy"]:
continue
with client.device(device["serial"], label="my crawler") as d:
d.press("home")
print(d.info())

เหมาะกับงานครั้งเดียว การเก็บข้อมูล และทุกอย่างที่คุณอยากสั่งจากตัวจัดตารางเวลาของคุณเอง

แบบจัดการโดยระบบ

คุณลงทะเบียนโปรแกรมไว้ใน TikMatrix แล้วมันจะกลายเป็นงานเหมือนงานอื่น ๆ ได้ทั้งคิวงาน การจำกัดการทำงานพร้อมกันตามแพ็กเกจ การลองใหม่อัตโนมัติ บันทึกงาน และเทมเพลตตารางเวลา TikMatrix จะจองเครื่องก่อนเริ่มโปรแกรมของคุณ และส่งรหัสการจองผ่านตัวแปรสภาพแวดล้อม

from tikmatrix import TikMatrix

with TikMatrix.from_env() as d: # เครื่องถูกจองไว้แล้ว
d.click(text="Log in")
print("done") # บรรทัดนี้จะไปอยู่ในบันทึกงาน

เหมาะกับทุกอย่างที่ต้องการรันซ้ำ ๆ ตามเวลา หรือกระจายไปหลายเครื่อง

ควรเลือกแบบไหน

แบบอิสระแบบจัดการโดยระบบ
ใครเป็นคนเริ่มคุณคิวงานของ TikMatrix
การจองอุปกรณ์คุณจองเองถือไว้ให้แล้วตั้งแต่เริ่ม
ลองใหม่ ตารางเวลา บันทึกงานคุณสร้างเองมีมาให้
รันบนหลายเครื่องคุณเขียนลูปเองหนึ่งงานต่อหนึ่งเครื่อง ส่งขนานกัน
เหมาะที่สุดกับการสำรวจ ตัวเก็บข้อมูล งานครั้งเดียวทุกอย่างที่ต้องทำซ้ำ

คุณเริ่มจากแบบอิสระเพื่อปรับขั้นตอนให้ลงตัวก่อน แล้วค่อยลงทะเบียนไฟล์เดียวกันเป็นสคริปต์แบบจัดการก็ได้ — บรรทัดเดียวที่เปลี่ยนคือ TikMatrix.from_env()

เริ่มต้นใช้งาน

1. ติดตั้งไลบรารีไคลเอนต์

pip install requests

จากนั้นคัดลอก tikmatrix.py จากไดเรกทอรี SDK มาไว้ข้าง ๆ สคริปต์ของคุณ ไลบรารีเป็นไฟล์เดียว ไม่มีสิ่งที่ต้องพึ่งพาอื่น

คุณไม่จำเป็นต้องใช้มันก็ได้ — API เป็นแค่ JSON ผ่าน HTTP และเอ็นด์พอยต์ดิบอยู่ในเอกสารด้านล่าง

2. เขียนสคริปต์ของคุณ

from tikmatrix import TikMatrix

client = TikMatrix()
with client.device("192.168.1.5:5555") as d:
d.press("home")
d.adb("shell", "am", "start", "-a", "android.settings.SETTINGS")
d.wait_for(text="Settings", timeout=15)
d.screenshot("settings.png")

รันขณะที่ TikMatrix เปิดอยู่และเชื่อมต่อโทรศัพท์แล้ว ถ้ามันพิมพ์ดิกชันนารีข้อมูลอุปกรณ์ออกมา แสดงว่าทุกอย่างเชื่อมกันเรียบร้อย

3. ลงทะเบียน (เฉพาะแบบจัดการโดยระบบ)

ไปที่ อุปกรณ์ → สคริปต์ที่กำหนดเอง → เพิ่มสคริปต์:

ช่องความหมาย
ชื่อแสดงในรายการสคริปต์และในบันทึกงาน
คำสั่งบรรทัดคำสั่งที่จะรัน เช่น python C:/scripts/my_flow.py
ไดเรกทอรีทำงานไม่บังคับ ตำแหน่งที่โปรแกรมเริ่มทำงาน
แพลตฟอร์มดูโหมดแพลตฟอร์มด้านล่าง
หมดเวลาจำนวนวินาทีก่อนที่สคริปต์จะถูกฆ่าและงานถูกทำเครื่องหมายว่าล้มเหลว ค่าเริ่มต้น 1800
ตัวแปรสภาพแวดล้อมเพิ่มเติมออบเจ็กต์ JSON (ไม่บังคับ) ที่จะผสานเข้ากับสภาพแวดล้อมของโปรแกรม
เปิดใช้งานปิดสคริปต์โดยไม่ต้องลบ สคริปต์ที่ปิดอยู่จะส่งไปทำงานไม่ได้

จากนั้นกด ▶ ที่แถวของสคริปต์แล้วเลือกอุปกรณ์ เหมือนกับสคริปต์ในตัวทุกประการ

ให้ผู้ช่วยเขียนให้

ผู้ช่วย AI ร่างสคริปต์ที่กำหนดเองจากคำอธิบายภาษาธรรมดาและลงทะเบียนให้ได้ในขั้นตอนเดียว โดยจะแสดงไฟล์ทั้งไฟล์ให้ดูก่อนที่จะเขียนอะไรลงดิสก์

การจองอุปกรณ์

โทรศัพท์เครื่องหนึ่งถูกควบคุมได้ครั้งละหนึ่งอย่างเท่านั้น การจองเครื่องเป็นการบอก TikMatrix ว่าเครื่องนี้ไม่ว่าง ดังนั้น:

  • คิวงานจะไม่ส่งงานไปยังหน้าจอเดียวกัน และ
  • การเรียก JSON-RPC ของคุณจะรายงานสถานะเอเจนต์เหมือนที่สคริปต์ในตัวทำ ตัวเฝ้าระวังจึงเห็นเอเจนต์ที่กำลังยุ่ง ไม่ใช่เอเจนต์ที่เงียบหาย

การจองยังกินโควตาอุปกรณ์ของแพ็กเกจไปหนึ่งช่องด้วย

การจองมีวันหมดอายุ — ค่าเริ่มต้น 120 วินาที สูงสุด 600 วินาที ไลบรารี Python จะต่ออายุให้ในเธรดเบื้องหลังและปล่อยคืนเมื่อบล็อก with จบ ดังนั้นสคริปต์ที่ล่มจะปล่อยเครื่องคืนภายในไม่กี่วินาที แทนที่จะยึดไว้จนกว่าคุณจะรีสตาร์ตแอป หากคุณเรียก API โดยตรง คุณต้องส่งสัญญาณชีพเอง

คุณดูการจองที่ยังมีชีวิตทั้งหมด — และบังคับปล่อยคืนได้ — ที่ การตั้งค่า → Developer API → เซสชันอุปกรณ์ที่ใช้งานอยู่

โหมดแพลตฟอร์ม

สคริปต์ที่ลงทะเบียนไว้จะประกาศเป้าหมายของตัวเอง:

Generic — ส่งมอบเครื่องให้ตามสภาพ ไม่เปิดแอปใด ไม่สลับบัญชี ไม่ตรวจวิธีป้อนข้อความ และไม่ปิดอะไรหลังจากจบงาน ใช้โหมดนี้เพื่อทำงานอัตโนมัติกับทุกอย่างที่ไม่ใช่ TikTok หรือ Instagram

TikTok / Instagram — เปิดแอปและสลับบัญชีก่อนโปรแกรมของคุณจะเริ่ม แล้วปิดแอปเมื่อจบ เหมือนสคริปต์ในตัวทุกประการ TIKMATRIX_PACKAGE จะบอกว่าใช้แพ็กเกจใด ใช้โหมดนี้เพื่อเพิ่มขั้นตอนที่สคริปต์ในตัวไม่ครอบคลุม

ตัวแปรสภาพแวดล้อม

สคริปต์แบบจัดการจะได้รับ:

ตัวแปรความหมาย
TIKMATRIX_API_BASEURL ของเซิร์ฟเวอร์ เช่น http://127.0.0.1:50809
TIKMATRIX_SESSION_IDการจองที่ถือไว้ให้คุณแล้ว
TIKMATRIX_SERIALอุปกรณ์ที่งานนี้ถูกส่งไป
TIKMATRIX_PACKAGEแพ็กเกจแอปที่ระบุได้
TIKMATRIX_PLATFORMtiktok, instagram หรือ generic

TikMatrix.from_env() อ่านทั้งหมดนี้ให้คุณ

สคริปต์แบบอิสระจะไม่ได้รับตัวแปรเหล่านี้เลย — ให้จองอุปกรณ์เองอย่างชัดเจนแทน

สิ่งที่คุณใส่ใน ตัวแปรสภาพแวดล้อมเพิ่มเติม จะถูกผสานทับลงไป ซึ่งเป็นวิธีปกติในการส่งค่าตั้งค่าเฉพาะรอบให้สคริปต์ที่ลงทะเบียนไว้ตัวเดียวโดยไม่ต้องแก้ไฟล์

เอกสารอ้างอิงไลบรารี Python

TikMatrix — การเชื่อมต่อ

การเรียกทำอะไร
TikMatrix(base_url=None, timeout=30.0)เชื่อมต่อ ถอยไปใช้ TIKMATRIX_API_BASE แล้วจึง http://127.0.0.1:50809
client.devices()อุปกรณ์ที่ออนไลน์ แต่ละตัวมี serial, real_serial และ busy
client.sessions()การจองที่ยังมีชีวิตทั้งหมด รวมถึงของโพรเซสอื่น
client.device(serial, label=..., ttl_secs=120)จองอุปกรณ์และคืนค่า Device
TikMatrix.from_env()รับช่วงอุปกรณ์ที่สคริปต์แบบจัดการถูกเริ่มมาพร้อมกัน

Device — ตัวเครื่อง

การเรียกทำอะไร
d.info()ข้อมูลอุปกรณ์จาก UIAutomator2
d.window_size()(กว้าง, สูง)
d.screenshot(path=None)ไบต์ PNG เขียนลง path ได้ตามต้องการ
d.hierarchy()ผังหน้าจอปัจจุบันในรูป XML
d.find(text=, resource_id=, description=, class_name=)โหนดที่ตรงกัน แต่ละตัวมี bounds และ center
d.exists(**criteria)มีอะไรตรงกันหรือไม่
d.wait_for(timeout=10.0, interval=1.0, **criteria)รอจนปรากฏแล้วคืนค่า
d.click(timeout=10.0, **criteria)รอองค์ประกอบแล้วแตะที่จุดกึ่งกลาง
d.click_xy(x, y)แตะที่พิกัด
d.swipe(sx, sy, ex, ey, steps=20)ปัด
d.press(key)back, home, recent, enter, …
d.input_text(text)พิมพ์ลงช่องที่โฟกัสอยู่ผ่าน IME ป้อนเร็วที่มาพร้อมกัน
d.jsonrpc(method, params=None, timeout=10)เมธอด UIAutomator2 ใด ๆ
d.adb(*args, timeout_ms=None)รันคำสั่ง ADB
d.release()ปล่อยการจอง with ทำให้อัตโนมัติ

find จับคู่กับผังหน้าจอที่ดัมป์ออกมา ดังนั้นเมื่อตัวเลือกพลาด คุณสั่ง print(d.hierarchy()) เพื่อดูได้เลยว่ามันค้นหาในอะไร ตัวตรวจสอบองค์ประกอบ ในมุมมองอุปกรณ์แสดงผังเดียวกันแบบภาพ ซึ่งมักเป็นวิธีหา resource-id ที่เร็วที่สุด

input_text ต้องใช้ ADB

มันส่งบรอดแคสต์ไปยัง IME ที่มาพร้อมกัน ซึ่งผ่าน adb shell ให้เปิดการเข้าถึง ADBก่อนใช้ ไม่เช่นนั้นจะล้มเหลวด้วยรหัส 403

ข้อผิดพลาด

ไลบรารีโยนข้อยกเว้นสองแบบ ทั้งคู่เป็นคลาสย่อยของ RuntimeError:

ข้อยกเว้นเมื่อใด
DeviceBusyErrorHTTP 409 — อุปกรณ์ถูกจองอยู่แล้ว หรือแพ็กเกจไม่มีช่องอุปกรณ์ว่าง
TikMatrixErrorกรณีอื่นทั้งหมด: แพ็กเกจต่ำเกินไป การจองหมดอายุ ADB ปิดอยู่ ตัวเลือกไม่เคยตรงกัน
from tikmatrix import TikMatrix, TikMatrixError, DeviceBusyError

client = TikMatrix()
try:
with client.device("192.168.1.5:5555") as d:
d.click(text="Log in", timeout=20)
except DeviceBusyError:
print("เครื่องนั้นมีคนอื่นใช้อยู่ — ลองเครื่องอื่น")
except TikMatrixError as exc:
print("ล้มเหลว:", exc)

ในสคริปต์แบบจัดการ การปล่อยให้ข้อยกเว้นหลุดออกไปมักเป็นสิ่งที่ถูกต้อง: รหัสออกที่ไม่ใช่ศูนย์จะทำให้งานถูกทำเครื่องหมายว่าล้มเหลว และร่องรอยข้อผิดพลาดจะไปอยู่ในบันทึกงาน

เอ็นด์พอยต์ HTTP

การดำเนินการกับอุปกรณ์ต้องมีเฮดเดอร์ x-session-id ที่ระบุการจองที่ยังมีชีวิต ไม่มีคีย์ API: เช่นเดียวกับส่วนอื่นของ API ภายในเครื่อง เอ็นด์พอยต์เหล่านี้ไม่มีการยืนยันตัวตน — การเข้าถึงเครื่องนี้ได้ในเครือข่ายคือการควบคุมการเข้าถึง และเอ็นด์พอยต์เหล่านี้ไม่ส่งเฮดเดอร์ CORS จึงควรเรียกจากโปรแกรม (curl, Python หรือโค้ดฝั่งเซิร์ฟเวอร์ใด ๆ) ไม่ใช่จากหน้าเว็บในเบราว์เซอร์

เมธอดเส้นทางจุดประสงค์
GET/api/v1/rpc/devicesแสดงอุปกรณ์ที่ออนไลน์และสถานะว่าง/ไม่ว่าง
POST/api/v1/rpc/sessionจองอุปกรณ์ → session_id
POST/api/v1/rpc/session/{id}/heartbeatต่ออายุการจอง
DELETE/api/v1/rpc/session/{id}ปล่อยการจอง
GET/api/v1/rpc/sessionแสดงการจองที่ยังมีชีวิต
POST/api/v1/rpc/jsonrpcเรียกเมธอด UIAutomator2
POST/api/v1/rpc/adbรันคำสั่ง ADB
GET/api/v1/rpc/hierarchy?serial=ผังหน้าจอปัจจุบันในรูป XML
GET/api/v1/rpc/screenshot?serial=หน้าจอปัจจุบันในรูป PNG

การตอบกลับแบบ JSON ใช้ซองเดียวกับ API ภายในเครื่องส่วนอื่น — {"code": 0, "message": "success", "data": ...} โดย code จะไม่ใช่ศูนย์เมื่อล้มเหลว ส่วน hierarchy และ screenshot จะคืนเนื้อหาดิบแทน

ตัวอย่าง

# จองอุปกรณ์
curl -X POST http://127.0.0.1:50809/api/v1/rpc/session \
-H "Content-Type: application/json" \
-d '{"serial":"192.168.1.5:5555","label":"curl test","ttl_secs":120}'

# {"code":0,"message":"success","data":{"session_id":"ff3ae079-...","serial":"192.168.1.5:5555", ...}}

# สั่งงาน
curl -X POST http://127.0.0.1:50809/api/v1/rpc/jsonrpc \
-H "x-session-id: ff3ae079-..." \
-H "Content-Type: application/json" \
-d '{"serial":"192.168.1.5:5555","method":"deviceInfo","params":[]}'

# ต่ออายุระหว่างทำงาน
curl -X POST http://127.0.0.1:50809/api/v1/rpc/session/ff3ae079-.../heartbeat \
-H "Content-Type: application/json" \
-d '{"ttl_secs":120}'

# คืนเครื่อง
curl -X DELETE http://127.0.0.1:50809/api/v1/rpc/session/ff3ae079-...

ข้อผิดพลาด

สถานะความหมาย
403แพ็กเกจต่ำกว่า Pro, ไม่มีการจอง, การจองหมดอายุ หรือปิดการเข้าถึง ADB
409อุปกรณ์ถูกจองอยู่แล้ว หรือแพ็กเกจไม่มีช่องอุปกรณ์ว่าง

เขียนด้วยภาษาอื่น

ไม่มีอะไรที่ผูกกับ Python เลย รันไทม์ใดก็ตามที่ส่งคำขอ HTTP ได้ก็ใช้ได้ — ข้อตกลงของโหมดจัดการมีแค่ "อ่านตัวแปรสภาพแวดล้อมสามตัว และออกด้วยรหัส 0 เมื่อสำเร็จ"

// my_flow.js — ลงทะเบียนด้วย: node C:/scripts/my_flow.js
const base = process.env.TIKMATRIX_API_BASE || "http://127.0.0.1:50809";
const serial = process.env.TIKMATRIX_SERIAL;
const session = process.env.TIKMATRIX_SESSION_ID;

async function jsonrpc(method, params = []) {
const res = await fetch(`${base}/api/v1/rpc/jsonrpc`, {
method: "POST",
headers: { "content-type": "application/json", "x-session-id": session },
body: JSON.stringify({ serial, method, params }),
});
const body = await res.json();
if (!res.ok || body.code !== 0) throw new Error(body.message || res.statusText);
return body.data;
}

console.log(await jsonrpc("deviceInfo"));

ถ้าตัวแปลภาษาไม่อยู่ใน PATH ให้ใส่เส้นทางเต็มในช่อง คำสั่ง เช่น C:/Program Files/nodejs/node.exe C:/scripts/my_flow.js

เรียกสคริปต์ที่กำหนดเองผ่าน API

สคริปต์ที่ลงทะเบียนไว้เริ่มทำงานผ่านAPI จัดการงานได้ด้วย สคริปต์หนึ่งจึงสั่งคิวงานต่อเนื่องได้:

curl -X POST http://127.0.0.1:50809/api/v1/task \
-H "Content-Type: application/json" \
-d '{
"serials": ["192.168.1.5:5555"],
"script_name": "custom_script",
"script_config": {
"custom_script_id": 1,
"custom_script_platform": "generic"
}
}'

custom_script_id คือรหัสของสคริปต์ที่คุณลงทะเบียนไว้

การเข้าถึง ADB

/api/v1/rpc/adb ให้เชลล์บนอุปกรณ์แก่สคริปต์ของคุณ — จำเป็นสำหรับการส่งไฟล์สื่อ ติดตั้ง APK และเปลี่ยนการตั้งค่าระบบ เนื่องจากมันคือเชลล์เต็มรูปแบบบนเอ็นด์พอยต์ที่ไม่มีคีย์ API จึงมาพร้อมสถานะปิด ให้เปิดที่ การตั้งค่า → Developer API → อนุญาตคำสั่ง ADB เมื่อคุณมีสคริปต์ที่ต้องใช้จริง ๆ ส่วนงานอัตโนมัติบนหน้าจอผ่าน /rpc/jsonrpc ทำงานได้โดยไม่ต้องเปิด

ขณะที่ปิดอยู่ /api/v1/rpc/adb จะตอบ 403 และ API ส่วนที่เหลือยังทำงานตามปกติ ทุกคำสั่ง ADB ที่สคริปต์รันจะถูกบันทึกลงไฟล์บันทึกของคุณ

เขียนสคริปต์ให้ใช้งานได้ยาว ๆ

  • รอหน้าจอ อย่านอนรอ d.wait_for(...) จะคืนค่าทันทีที่องค์ประกอบปรากฏ ส่วนการ sleep แบบตายตัวนั้นไม่ช้าเกินจำเป็นก็สั้นเกินไปในวันที่เครื่องอืด
  • ตรวจก่อนแตะ d.exists(...) บนกล่องขอความยินยอมหรือข้อความ "ไว้ทีหลัง" มีต้นทุนแค่ดัมป์ผังหนึ่งครั้ง แต่ช่วยกู้การรันที่มิฉะนั้นจะแตะลงที่ว่างเปล่า
  • พิมพ์สิ่งที่ทำออกมา ในโหมดจัดการ stdout คือบันทึกงาน และเป็นร่องรอยเดียวของการรันที่ไม่มีใครเฝ้าดู
  • ทำให้การรันซ้ำปลอดภัย การลองใหม่จะรันโปรแกรมทั้งตัวอีกครั้ง สคริปต์ที่โพสต์จึงควรตรวจว่าโพสต์ไปแล้วหรือยัง แทนที่จะสมมติว่าเริ่มจากศูนย์
  • หนึ่งสคริปต์ต่อหนึ่งงาน การทำงานพร้อมกันนับต่ออุปกรณ์ งานเล็ก ๆ สิบงานบนสิบเครื่องจึงเสร็จเร็วกว่าสคริปต์เดียวที่วนสิบเครื่องมาก

ข้อควรทราบและข้อจำกัด

  • คำสั่งถูกรันโดยตรง ไม่ได้ ผ่านเชลล์ ดังนั้น && และ | จะถูกมองเป็นอาร์กิวเมนต์ ไม่ใช่ตัวดำเนินการ ถ้าต้องการพฤติกรรมของเชลล์ ให้ลงทะเบียน cmd /c "..." (Windows) หรือ sh -c "..." (macOS)
  • ใส่เครื่องหมายคำพูดครอบเส้นทางที่มีช่องว่าง: "C:/Program Files/Python/python.exe" my_script.py
  • สคริปต์ที่เกินเวลาที่กำหนดจะถูกยุติและงานถูกทำเครื่องหมายว่าล้มเหลว
  • รหัสออกที่ไม่ใช่ศูนย์ทำให้งานล้มเหลว ทุกอย่างที่สคริปต์เขียนลง stdout และ stderr จะไปอยู่ในบันทึกงาน
  • สคริปต์รันด้วยสิทธิ์เดียวกับ TikMatrix เอง ให้ลงทะเบียนเฉพาะโปรแกรมที่คุณเขียนเองหรือเชื่อถือได้

การแก้ปัญหา

API access requires Pro or higher plan (403) สิทธิ์การใช้งานบนเครื่องนี้เป็น Starter หรือไม่ทำงาน ตรวจสอบที่ การตั้งค่า → สิทธิ์การใช้งาน

การเชื่อมต่อถูกปฏิเสธที่ 127.0.0.1:50809 TikMatrix ไม่ได้เปิดอยู่ หรือรันด้วยผู้ใช้คนอื่น เซิร์ฟเวอร์มีอยู่เฉพาะขณะที่แอปเปิดอยู่เท่านั้น

ได้ 409 ทุกครั้งที่พยายามจอง ไม่เครื่องนั้นไม่ว่างจริง ๆ — ดูที่ การตั้งค่า → Developer API → เซสชันอุปกรณ์ที่ใช้งานอยู่ — ก็แปลว่าช่องอุปกรณ์ทั้งหมดในแพ็กเกจถูกงานที่กำลังรันใช้หมดแล้ว

การจองหมดอายุกลางขั้นตอนที่ยาว ค่า TTL เริ่มต้นคือ 120 วินาที และไลบรารีต่ออายุให้เบื้องหลัง กรณีนี้จึงมักหมายความว่าสคริปต์บล็อกเธรดหลักนานกว่า TTL ให้เพิ่ม ttl_secs (สูงสุด 600) หรือย้ายงานที่ใช้เวลานานออกจากเธรดนั้น

d.adb(...) ล้มเหลวด้วย 403 การเข้าถึง ADB ปิดอยู่ เปิดที่ การตั้งค่า → Developer API → อนุญาตคำสั่ง ADB

ตัวเลือกไม่เคยตรงเลย print(d.hierarchy()) แสดงผังที่ find ค้นหาจริง ๆ ข้อความถูกเทียบแบบตรงตัว ช่องว่างเกินหนึ่งตัวหรือป้ายที่แปลเป็นภาษาท้องถิ่นจึงเป็นสาเหตุที่พบบ่อย การจับคู่ด้วย resource_id เสถียรกว่าการใช้ text

งานถูกทำเครื่องหมายว่าล้มเหลว แต่โทรศัพท์ดูปกติ อ่านบันทึกงาน รหัสออกที่ไม่ใช่ศูนย์ — รวมถึงข้อยกเว้นที่ไม่ได้จับตอนท้ายของการรันที่สำเร็จ — ก็ทำให้งานล้มเหลวได้แม้ระบบอัตโนมัติจะทำงานได้จริง

ขั้นต่อไป