API ของ Android 4.3

ระดับ API: 18

Android 4.3 (JELLY_BEAN_MR2) เป็นเวอร์ชันอัปเดตของ Jelly Bean ที่มีฟีเจอร์ใหม่ๆ สำหรับผู้ใช้และนักพัฒนาแอป เอกสารนี้ให้ข้อมูลเบื้องต้นเกี่ยวกับ API ใหม่ที่โดดเด่นที่สุด

ในฐานะนักพัฒนาแอป คุณควรดาวน์โหลดภาพระบบ Android 4.3 และแพลตฟอร์ม SDK จาก SDK Manager โดยเร็วที่สุด หากไม่มีอุปกรณ์ที่ใช้ Android 4.3 ในการทดสอบแอป ให้ใช้ภาพระบบ Android 4.3 เพื่อทดสอบแอปในโปรแกรมจำลอง Android จากนั้นสร้างแอปสำหรับแพลตฟอร์ม Android 4.3 เพื่อเริ่มใช้ API เวอร์ชันล่าสุด

อัปเดตระดับ API เป้าหมาย

หากต้องการเพิ่มประสิทธิภาพแอปสำหรับอุปกรณ์ที่ใช้ Android 4.3 ได้ดียิ่งขึ้น คุณควรตั้งค่า targetSdkVersion เป็น "18" ให้ติดตั้งในอิมเมจระบบ Android 4.3 แล้วเผยแพร่อัปเดตที่มีการเปลี่ยนแปลงนี้

คุณสามารถใช้ API ใน Android 4.3 ไปพร้อมกับรองรับเวอร์ชันเก่าได้ด้วยการเพิ่มเงื่อนไขลงในโค้ดที่ตรวจสอบระดับ API ของระบบก่อนที่จะเรียกใช้ API ที่ minSdkVersion ไม่รองรับ ดูข้อมูลเพิ่มเติมเกี่ยวกับการคงความเข้ากันได้ย้อนหลังได้ที่การรองรับเวอร์ชันแพลตฟอร์มที่แตกต่างกัน

นอกจากนี้ API ต่างๆ ยังพร้อมใช้งานในคลังการสนับสนุนของ Android ซึ่งช่วยให้คุณใช้ฟีเจอร์ใหม่ๆ ในแพลตฟอร์มเวอร์ชันเก่าได้

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีการทำงานของระดับ API ได้ที่ระดับ API คืออะไร

การเปลี่ยนแปลงที่สำคัญเกี่ยวกับลักษณะการทำงาน

หากคุณเคยเผยแพร่แอปสำหรับ Android โปรดทราบว่าแอปของคุณอาจได้รับผลกระทบจากการเปลี่ยนแปลงใน Android 4.3

หากแอปใช้ Intent แบบไม่เจาะจงปลายทาง...

แอปของคุณอาจทำงานผิดพลาดในสภาพแวดล้อมโปรไฟล์ที่จำกัด

ผู้ใช้ในสภาพแวดล้อมโปรไฟล์ที่ถูกจำกัดอาจไม่สามารถ มีแอป Android มาตรฐานทั้งหมดที่ใช้ได้ เช่น โปรไฟล์ที่จำกัดอาจปิดใช้เว็บเบราว์เซอร์และแอปกล้อง ดังนั้นแอปของคุณไม่ควรคาดเดาว่าแอปใดพร้อมใช้งาน เนื่องจากหากคุณเรียกใช้ startActivity() โดยไม่ตรวจสอบว่ามีแอปใดพร้อมจัดการ Intent หรือไม่ แอปอาจขัดข้องในโปรไฟล์ที่จํากัด

เมื่อใช้ Intent ที่ไม่ชัดแจ้ง คุณควรตรวจสอบเสมอว่าแอปพร้อมที่จะจัดการ Intent ดังกล่าวโดยการเรียก resolveActivity() หรือ queryIntentActivities() เช่น

Kotlin

val intent = Intent(Intent.ACTION_SEND)
...
if (intent.resolveActivity(packageManager) != null) {
    startActivity(intent)
} else {
    Toast.makeText(context, R.string.app_not_available, Toast.LENGTH_LONG).show()
}

Java

Intent intent = new Intent(Intent.ACTION_SEND);
...
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(intent);
} else {
    Toast.makeText(context, R.string.app_not_available, Toast.LENGTH_LONG).show();
}

หากแอปใช้บัญชี

แอปของคุณอาจทำงานผิดพลาดในสภาพแวดล้อมโปรไฟล์ที่จำกัด

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

หากต้องการป้องกันไม่ให้โปรไฟล์ที่จำกัดใช้แอปทั้งหมดเนื่องจาก แอปขึ้นอยู่กับข้อมูลบัญชีที่มีความละเอียดอ่อน ให้ระบุแอตทริบิวต์ android:requiredAccountType ใน <application> ของไฟล์ Manifest

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

หากแอปใช้ VideoView

วิดีโอของคุณอาจมีขนาดเล็กลงใน Android 4.3

ใน Android เวอร์ชันก่อนหน้า วิดเจ็ต VideoView ไม่ถูกต้อง คำนวณค่า "wrap_content" สำหรับ layout_height และ layout_width ให้เหมือนกับ "match_parent" ดังนั้นในขณะที่ใช้ "wrap_content" สำหรับความสูงหรือความกว้างอาจมีเลย์เอาต์วิดีโอที่คุณต้องการก่อนหน้านี้แล้ว ซึ่งอาจทำให้วิดีโอมีขนาดเล็กลงมากบน Android 4.3 และเวอร์ชันที่สูงกว่า หากต้องการแก้ไขปัญหา ให้แทนที่ "wrap_content" ด้วย "match_parent" และตรวจสอบว่าวิดีโอปรากฏตามที่คาดไว้บน Android 4.3 และเวอร์ชันเก่า

โปรไฟล์ที่ถูกจำกัด

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

UI สำหรับผู้ใช้ในการควบคุมข้อจำกัดที่คุณสร้างขึ้นจะจัดการโดยแอปพลิเคชันการตั้งค่าของระบบ หากต้องการให้การตั้งค่าข้อจำกัดของแอปปรากฏต่อผู้ใช้ คุณต้องประกาศการจำกัดที่แอปกำหนดโดยการสร้าง BroadcastReceiver ที่ได้รับ Intent ACTION_GET_RESTRICTION_ENTRIES ระบบจะเรียกใช้ Intent นี้เพื่อค้นหาข้อจำกัดที่มีในแอปทั้งหมด จากนั้นสร้าง UI เพื่ออนุญาตให้ผู้ใช้หลักจัดการข้อจำกัดสำหรับโปรไฟล์ที่ถูกจำกัดแต่ละรายการ

ในเมธอด onReceive() ของ BroadcastReceiver คุณต้องสร้าง RestrictionEntry สำหรับข้อจำกัดแต่ละรายการที่แอปมีให้ RestrictionEntry แต่ละรายการจะกำหนดชื่อ คำอธิบาย ข้อจำกัด และข้อมูลประเภทใดประเภทหนึ่งต่อไปนี้

  • TYPE_BOOLEAN สำหรับข้อจำกัดที่เป็นจริงหรือเท็จ
  • TYPE_CHOICEสำหรับข้อจำกัดที่มี ตัวเลือกที่หลากหลายซึ่งไม่เกี่ยวข้องกัน (ตัวเลือกปุ่มตัวเลือก)
  • TYPE_MULTI_SELECT สำหรับข้อจำกัดที่มีตัวเลือกหลายรายการที่ไม่เป็นตัวเลือกที่เฉพาะตัวเหมือนกัน (ตัวเลือกช่องทำเครื่องหมาย)

จากนั้นใส่ออบเจ็กต์ RestrictionEntry ทั้งหมดลงใน ArrayList แล้วใส่ลงในผลลัพธ์ของผู้รับการออกอากาศเป็นค่าสำหรับข้อมูลเพิ่มเติม EXTRA_RESTRICTIONS_LIST

ระบบจะสร้าง UI สําหรับข้อจํากัดของแอปคุณในแอปการตั้งค่า และบันทึกข้อจํากัดแต่ละรายการด้วยคีย์ที่ไม่ซ้ำกันที่คุณระบุสําหรับRestrictionEntryออบเจ็กต์แต่ละรายการ เมื่อผู้ใช้เปิดแอป คุณสามารถค้นหาข้อจํากัดปัจจุบันได้โดยเรียกใช้ getApplicationRestrictions() การดำเนินการนี้จะแสดงผล Bundle ที่มีคู่คีย์-ค่าสำหรับข้อจำกัดแต่ละรายการ ที่คุณกำหนดด้วยออบเจ็กต์ RestrictionEntry

หากต้องการระบุข้อจํากัดที่เฉพาะเจาะจงมากขึ้นซึ่งค่าบูลีน ค่าตัวเลือกเดียว และค่าตัวเลือกหลายรายการจัดการไม่ได้ คุณสามารถสร้างกิจกรรมที่ผู้ใช้ระบุข้อจํากัดได้ และอนุญาตให้ผู้ใช้เปิดกิจกรรมนั้นจากการตั้งค่าข้อจํากัด ใน Broadcast Receiver รวม EXTRA_RESTRICTIONS_INTENT เพิ่มเติม ในผลลัพธ์ Bundle ส่วนเกินนี้ต้องระบุ Intent กำลังระบุคลาส Activity ที่จะเปิดตัว (ใช้ putParcelable() เพื่อส่ง EXTRA_RESTRICTIONS_INTENT พร้อม Intent) เมื่อผู้ใช้หลักเข้าสู่กิจกรรมเพื่อตั้งค่าข้อจํากัดที่กําหนดเอง กิจกรรมของคุณจะต้องแสดงผลลัพธ์ที่มีค่าข้อจํากัดในส่วนเพิ่มเติมโดยใช้คีย์ EXTRA_RESTRICTIONS_LIST หรือ EXTRA_RESTRICTIONS_BUNDLE ทั้งนี้ขึ้นอยู่กับว่าคุณระบุออบเจ็กต์ RestrictionEntry หรือคู่คีย์-ค่าตามลําดับ

การสนับสนุนบัญชีในโปรไฟล์ที่ถูกจำกัด

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

  • อนุญาตให้เข้าถึงบัญชีของเจ้าของจากโปรไฟล์ที่ถูกจำกัด

    หากต้องการเข้าถึงบัญชีจากโปรไฟล์ที่ถูกจำกัด คุณต้องเพิ่มแอตทริบิวต์ android:restrictedAccountType ลงในแท็ก <application> ดังนี้

    <application ...
        android:restrictedAccountType="com.example.account.type" >

    ข้อควรระวัง: การเปิดใช้แอตทริบิวต์นี้จะทำให้ สิทธิ์เข้าถึงของแอปเพื่อเข้าถึงบัญชีของผู้ใช้หลักจากโปรไฟล์ที่ถูกจำกัด คุณจึงควรอนุญาตเฉพาะในกรณีที่แอปแสดงข้อมูลที่ไม่ได้เปิดเผยข้อมูลส่วนบุคคลที่ระบุตัวบุคคลนั้นได้ (PII) ซึ่งถือว่ามีความละเอียดอ่อน การตั้งค่าระบบจะแจ้ง ว่าแอปของคุณให้สิทธิ์โปรไฟล์ที่จำกัดกับบัญชีของตน ดังนั้นจึงควรแจ้งให้ผู้ใช้ทราบอย่างชัดเจน การเข้าถึงบัญชีนั้นสำคัญต่อฟังก์ชันการทำงานของแอป นอกจากนี้ คุณควรจัดให้มีการควบคุมการจํากัดที่เพียงพอสําหรับผู้ใช้หลักเพื่อกําหนดระดับการเข้าถึงบัญชีที่อนุญาตในแอปด้วย หากทําได้

  • ปิดใช้ฟังก์ชันบางอย่างเมื่อแก้ไขบัญชีไม่ได้

    หากต้องการใช้บัญชี แต่ไม่ได้กำหนดว่าต้องใช้กับบัญชีหลักของแอป คุณสามารถตรวจสอบความพร้อมใช้งานของบัญชี และปิดใช้ฟีเจอร์เมื่อไม่พร้อมใช้งาน คุณควรตรวจสอบก่อนว่าบัญชีที่มีอยู่ใช้งานได้หรือไม่ หากไม่เป็นเช่นนั้น ให้ค้นหาว่า คุณสร้างบัญชีใหม่ได้โดยโทรไปที่ getUserRestrictions() และตรวจสอบส่วน DISALLOW_MODIFY_ACCOUNTS เพิ่มเติมในผลลัพธ์ หากเป็นtrue คุณควรปิดใช้ฟังก์ชันใดก็ตามของแอปที่จำเป็นต้องเข้าถึงบัญชี เช่น

    Kotlin

    val um = context.getSystemService(Context.USER_SERVICE) as UserManager
    val restrictions: Bundle = um.userRestrictions
    if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
        // cannot add accounts, disable some functionality
    }

    Java

    UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
    Bundle restrictions = um.getUserRestrictions();
    if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
        // cannot add accounts, disable some functionality
    }

    หมายเหตุ: ในกรณีนี้ คุณไม่ควรประกาศแอตทริบิวต์ใหม่ในไฟล์ Manifest

  • ปิดใช้แอปเมื่อเข้าถึงบัญชีส่วนตัวไม่ได้

    หากแต่เป็นเรื่องสำคัญที่แอปจะไม่สามารถใช้ได้กับโปรไฟล์ที่ถูกจำกัดเนื่องจาก แอปของคุณต้องใช้ข้อมูลส่วนบุคคลที่มีความละเอียดอ่อนในบัญชี (และเนื่องจากโปรไฟล์ที่ถูกจำกัด ขณะนี้ไม่สามารถเพิ่มบัญชีใหม่) เพิ่ม แอตทริบิวต์ android:requiredAccountType เป็นแท็ก <application>

    <application ...
        android:requiredAccountType="com.example.account.type" >

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

  • ระบบไร้สายและการเชื่อมต่อ

    บลูทูธพลังงานต่ำ (พร้อมใช้งานแบบอัจฉริยะ)

    ตอนนี้ Android รองรับบลูทูธพลังงานต่ำ (LE) ด้วย API ใหม่ใน android.bluetooth API ใหม่นี้จะช่วยให้คุณสร้างแอป Android ที่สื่อสารกับอุปกรณ์ต่อพ่วงบลูทูธพลังงานต่ำได้ เช่น เครื่องวัดอัตราการเต้นของหัวใจและเครื่องนับก้าว

    เนื่องจากบลูทูธ LE เป็นฟีเจอร์ฮาร์ดแวร์ที่พร้อมใช้งานในอุปกรณ์ Android บางรุ่นเท่านั้น คุณจึงต้องประกาศองค์ประกอบ <uses-feature> ของ "android.hardware.bluetooth_le" ในไฟล์ Manifest ดังนี้

    <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />

    หากคุณคุ้นเคยกับ API บลูทูธแบบคลาสสิกของ Android ดีอยู่แล้ว ให้สังเกตว่าการใช้ Bluetooth LE API มีความแตกต่างกันอยู่บ้าง สิ่งสำคัญที่สุดคือตอนนี้มีคลาส BluetoothManager ที่คุณควรใช้สำหรับการดำเนินการระดับสูงบางอย่าง เช่น การรับ BluetoothAdapter การรับรายการที่เชื่อมโยง อุปกรณ์ และการตรวจสอบสถานะของอุปกรณ์ ตัวอย่างเช่น ตอนนี้คุณควรรับ BluetoothAdapterดังนี้

    Kotlin

    val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    bluetoothAdapter = bluetoothManager.adapter

    Java

    final BluetoothManager bluetoothManager =
            (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    bluetoothAdapter = bluetoothManager.getAdapter();

    หากต้องการค้นหาอุปกรณ์ต่อพ่วงบลูทูธ LE ให้เรียก startLeScan() ใน BluetoothAdapter โดยส่งการใช้งานอินเทอร์เฟซ BluetoothAdapter.LeScanCallback เมื่อบลูทูธ อะแดปเตอร์ตรวจพบอุปกรณ์ต่อพ่วง Bluetooth LE การใช้งาน BluetoothAdapter.LeScanCallback ได้รับสายเรียกเข้า onLeScan() วิธี วิธีการนี้จะแสดงออบเจ็กต์ BluetoothDevice ที่แสดงถึงอุปกรณ์ที่ตรวจพบ ค่า RSSI ของอุปกรณ์ และอาร์เรย์ไบต์ที่มีระเบียนการโฆษณาของอุปกรณ์

    หากต้องการสแกนเฉพาะอุปกรณ์ต่อพ่วงบางประเภท ให้เรียกใช้ startLeScan() และใส่อาร์เรย์ของออบเจ็กต์ UUID ที่ระบุบริการ GATT ที่แอปรองรับแทน

    หมายเหตุ: คุณจะสแกนหาได้เฉพาะอุปกรณ์บลูทูธ LE หรือสแกนหาอุปกรณ์บลูทูธคลาสสิกโดยใช้ API เวอร์ชันก่อนหน้า คุณจะสแกนหาทั้งอุปกรณ์บลูทูธ LE และบลูทูธคลาสสิกพร้อมกันไม่ได้

    แล้วโทรหา connectGatt() ที่อุปกรณ์ที่เกี่ยวข้อง เพื่อเชื่อมต่อกับอุปกรณ์ต่อพ่วง Bluetooth LE BluetoothDevice ออบเจ็กต์ผ่านการติดตั้งใช้งาน BluetoothGattCallback การติดตั้งใช้งาน BluetoothGattCallback ของคุณจะได้รับการเรียกกลับเกี่ยวกับสถานะการเชื่อมต่อกับอุปกรณ์และเหตุการณ์อื่นๆ อยู่ในช่วง onConnectionStateChange() Callback ที่คุณจะเริ่มสื่อสารกับอุปกรณ์ได้หากเมธอดผ่าน STATE_CONNECTED เป็นสถานะใหม่

    การเข้าถึงฟีเจอร์บลูทูธในอุปกรณ์ยังกำหนดให้แอปของคุณขอสิทธิ์บางอย่างจากผู้ใช้บลูทูธด้วย โปรดดูข้อมูลเพิ่มเติมในคู่มือ Bluetooth Low Energy API

    โหมดสแกน Wi-Fi เท่านั้น

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

    หากต้องการทราบตำแหน่งของผู้ใช้แต่ Wi-Fi ปิดอยู่ คุณสามารถขอให้ผู้ใช้เปิดใช้โหมดสแกน Wi-Fi เท่านั้นได้โดยเรียกใช้ startActivity() ด้วยการดำเนินการ ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE

    การกำหนดค่า Wi-Fi

    WifiEnterpriseConfig API ใหม่ช่วยให้บริการแบบองค์กรทำสิ่งต่อไปนี้ได้ กำหนดค่า Wi-Fi สำหรับอุปกรณ์ที่มีการจัดการโดยอัตโนมัติ

    การตอบกลับอย่างรวดเร็วสำหรับสายเรียกเข้า

    ตั้งแต่ Android 4.0 ฟีเจอร์ชื่อ "คำตอบด่วน" ช่วยให้ผู้ใช้สามารถตอบสนองต่อ ด้วยการส่งข้อความทันที โดยไม่จำเป็นต้องรับสายหรือปลดล็อกอุปกรณ์ ก่อนหน้านี้แอปรับส่งข้อความเริ่มต้นจะจัดการข้อความด่วนเหล่านี้เสมอ แต่ตอนนี้แอปใดก็ได้สามารถประกาศความสามารถของตนในการจัดการข้อความเหล่านี้ได้โดยการสร้าง Service ที่มีตัวกรอง Intent สำหรับ ACTION_RESPOND_VIA_MESSAGE

    เมื่อผู้ใช้ตอบกลับสายเรียกเข้าด้วยการตอบกลับอย่างรวดเร็ว แอปโทรศัพท์จะส่ง Intent ACTION_RESPOND_VIA_MESSAGE พร้อม URI ที่อธิบายผู้รับ (ผู้โทร) และ EXTRA_TEXT เพิ่มเติมพร้อมข้อความที่ผู้ใช้ต้องการส่ง เมื่อบริการได้รับความตั้งใจ บริการควรส่งมอบ ข้อความและหยุดตัวเองทันที (แอปของคุณไม่ควรแสดงกิจกรรม)

    หากต้องการรับ Intent นี้ คุณต้องประกาศสิทธิ์ SEND_RESPOND_VIA_MESSAGE

    มัลติมีเดีย

    การปรับปรุง MediaExtractor และ MediaCodec

    ตอนนี้ Android ให้คุณเขียน Dynamic Adaptive เองได้ง่ายขึ้น การสตรีมผ่านโปรแกรมเล่น HTTP (DASH) ตามมาตรฐาน ISO/IEC 23009-1 ใช้ API ที่มีอยู่ใน MediaCodec และ MediaExtractor เฟรมเวิร์กที่อยู่เบื้องหลัง API เหล่านี้ได้รับการอัปเดตให้รองรับการแยกวิเคราะห์ไฟล์ MP4 ที่กระจัดกระจาย แต่แอปของคุณยังคงต้องรับผิดชอบในการแยกวิเคราะห์ข้อมูลเมตา MPD และส่งสตรีมแต่ละรายการไปยัง MediaExtractor

    หากต้องการใช้ DASH กับเนื้อหาที่เข้ารหัส โปรดทราบว่าเมธอด getSampleCryptoInfo() จะแสดงผลข้อมูลเมตา MediaCodec.CryptoInfo ที่อธิบายโครงสร้างของตัวอย่างสื่อที่เข้ารหัสแต่ละรายการ นอกจากนี้ เรายังได้เพิ่มเมธอด getPsshInfo() ลงใน MediaExtractor เพื่อให้คุณเข้าถึงข้อมูลเมตา PSSH สำหรับสื่อ DASH ได้ เมธอดนี้จะแสดงผลแผนที่ของออบเจ็กต์ UUID เป็นไบต์ โดยที่ UUID จะระบุรูปแบบการเข้ารหัส และไบต์จะเป็นข้อมูลที่เจาะจงสำหรับรูปแบบนั้น

    DRM ของสื่อ

    คลาส MediaDrm ใหม่เป็นโซลูชันแบบโมดูลสำหรับการจัดการสิทธิ์ดิจิทัล (DRM) กับเนื้อหาสื่อของคุณโดยแยกข้อกังวลเกี่ยวกับ DRM ออกจากการเล่นสื่อ สำหรับ ตัวอย่างเช่น การแยก API นี้ช่วยให้คุณเล่นเนื้อหาที่เข้ารหัส Widevine ได้โดยไม่ต้อง เพื่อใช้รูปแบบสื่อ Widevine โซลูชัน DRM นี้ยังรองรับการเข้ารหัสทั่วไป DASH คุณจึง สามารถใช้แผน DRM ที่หลากหลายกับเนื้อหาสตรีมมิงของคุณ

    คุณสามารถใช้ MediaDrm เพื่อรับข้อความคําขอคีย์แบบทึบ และประมวลผลข้อความการตอบกลับคีย์จากเซิร์ฟเวอร์สําหรับการขอใบอนุญาตและการจัดสรร แอปของคุณเป็นแบบ รับผิดชอบการจัดการการสื่อสารของเครือข่ายกับเซิร์ฟเวอร์ คลาส MediaDrm จะให้ความสามารถในการสร้างและประมวลผลข้อความเท่านั้น

    API ของ MediaDrm มีวัตถุประสงค์เพื่อใช้ร่วมกับ MediaCodec API ที่เปิดตัวใน Android 4.1 (API ระดับ 16) ซึ่งรวมถึง MediaCodec สำหรับการเข้ารหัสและถอดรหัสเนื้อหา MediaCrypto สำหรับจัดการเนื้อหาที่เข้ารหัส และ MediaExtractor ในการแยกและแยกส่วนเนื้อหาไม่ได้

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

    การเข้ารหัสวิดีโอจาก Surface

    Android 4.1 (API ระดับ 16) เพิ่มคลาส MediaCodec สำหรับระดับต่ำ การเข้ารหัสและถอดรหัสของเนื้อหาสื่อ เมื่อเข้ารหัสวิดีโอ Android 4.1 กำหนดให้คุณระบุสื่อด้วยอาร์เรย์ ByteBuffer แต่ตอนนี้ Android 4.3 อนุญาตให้คุณใช้ Surface เป็นอินพุตให้กับโปรแกรมเข้ารหัส ตัวอย่างเช่น วิธีนี้ให้คุณเข้ารหัสอินพุต จากไฟล์วิดีโอที่มีอยู่หรือใช้เฟรมที่สร้างจาก OpenGL ES

    หากต้องการใช้ Surface เป็นอินพุตให้กับโปรแกรมเปลี่ยนไฟล์ ให้เรียกใช้ configure() สำหรับ MediaCodec ก่อน จากนั้นโทรไปที่ createInputSurface() เพื่อรับ Surface ซึ่งคุณจะสตรีมสื่อได้

    ตัวอย่างเช่น คุณสามารถใช้ Surface ที่ระบุเป็นหน้าต่างสำหรับบริบท OpenGL โดยส่งผ่านไปยัง eglCreateWindowSurface() จากนั้นขณะแสดงภาพพื้นผิว ให้เรียก eglSwapBuffers() เพื่อส่งเฟรมไปยัง MediaCodec

    หากต้องการเริ่มเข้ารหัส โปรดโทรหา start() ใน MediaCodec เมื่อเสร็จแล้ว ให้โทรหา signalEndOfInputStream() เพื่อสิ้นสุดการเข้ารหัส แล้วโทรหา release() ใน Surface

    การรวมสื่อ

    คลาส MediaMuxer ใหม่ช่วยให้สามารถมัลติเพล็กซ์ระหว่างสตรีมเสียง 1 รายการกับสตรีมวิดีโอ 1 รายการ API เหล่านี้ทำหน้าที่เป็นสิ่งที่เหมือนกับ MediaExtractor ที่เพิ่มใน Android 4.2 สำหรับสื่อการแยกสัญญาณ (Demuxing) สื่อ

    รูปแบบเอาต์พุตที่รองรับจะกำหนดไว้ใน MediaMuxer.OutputFormat ปัจจุบัน MP4 เป็นรูปแบบเอาต์พุตเดียวที่รองรับและขณะนี้ MediaMuxer รองรับ สตรีมเสียงครั้งละ 1 รายการและ/หรือ 1 สตรีมเท่านั้น

    MediaMuxer ได้รับการออกแบบมาให้ทำงานกับ MediaCodec เป็นส่วนใหญ่ เพื่อให้คุณสามารถประมวลผลวิดีโอผ่าน MediaCodec จากนั้นบันทึก ออกเป็นไฟล์ MP4 จนถึง MediaMuxer คุณยังสามารถใช้ MediaMuxer ร่วมกับ MediaExtractor เพื่อดำเนินการได้อีกด้วย แก้ไขสื่อได้โดยไม่ต้องเข้ารหัสหรือถอดรหัส

    ความคืบหน้าในการเล่นและการสครับสำหรับ RemoteControlClient

    ใน Android 4.0 (API ระดับ 14) ระบบได้เพิ่ม RemoteControlClient ลงใน เปิดใช้งานตัวควบคุมการเล่นสื่อจากไคลเอ็นต์รีโมตคอนโทรล เช่น ตัวควบคุมที่มีใน ล็อกหน้าจอ ตอนนี้ Android 4.3 ช่วยให้ตัวควบคุมดังกล่าวแสดงตำแหน่งการเล่นและตัวควบคุมสำหรับการกรอเล่นได้ ถ้าคุณเปิดใช้งานรีโมตคอนโทรลสำหรับ แอปสื่อที่มี API ของ RemoteControlClient คุณจะสามารถอนุญาตการเล่น โดยใช้อินเทอร์เฟซใหม่ 2 แบบ

    ก่อนอื่น คุณต้องเปิดใช้ Flag FLAG_KEY_MEDIA_POSITION_UPDATE โดยส่ง Flag นั้นให้กับ setTransportControlsFlags()

    จากนั้นติดตั้งใช้งานอินเทอร์เฟซใหม่ 2 รายการต่อไปนี้

    RemoteControlClient.OnGetPlaybackPositionListener
    รวมถึง Callback onGetPlaybackPosition() ที่ขอตำแหน่งปัจจุบัน ของสื่อเมื่อรีโมตคอนโทรลจำเป็นต้องอัปเดตความคืบหน้าใน UI
    RemoteControlClient.OnPlaybackPositionUpdateListener
    รวมถึง Callback onPlaybackPositionUpdate() ที่ จะบอกรหัสเวลาใหม่สำหรับสื่อของคุณแก่แอปของคุณเมื่อผู้ใช้สครับการเล่นด้วย UI ของรีโมตคอนโทรล

    เมื่ออัปเดตการเล่นด้วยตำแหน่งใหม่แล้ว ให้เรียกใช้ setPlaybackState() เพื่อระบุสถานะการเล่น ตำแหน่ง และความเร็วใหม่

    เมื่อกำหนดอินเทอร์เฟซเหล่านี้แล้ว คุณจะตั้งค่าอินเทอร์เฟซสำหรับ RemoteControlClient ได้โดยเรียกใช้ setOnGetPlaybackPositionListener() และ setPlaybackPositionUpdateListener() ตามลำดับ

    กราฟิก

    การรองรับ OpenGL ES 3.0

    Android 4.3 เพิ่มอินเทอร์เฟซ Java และการรองรับ OpenGL ES 3.0 ในตัว ฟังก์ชันใหม่ที่สำคัญ ที่มีให้ใน OpenGL ES 3.0 รวมถึง

    • การเร่งเอฟเฟกต์ภาพขั้นสูง
    • การบีบอัดพื้นผิว ETC2/EAC คุณภาพสูงเป็นฟีเจอร์มาตรฐาน
    • ภาษาการจัดแสง GLSL ES เวอร์ชันใหม่ที่รองรับจำนวนเต็มและทศนิยม 32 บิต
    • การแสดงผลพื้นผิวขั้นสูง
    • การกำหนดมาตรฐานขนาดพื้นผิวและรูปแบบบัฟเฟอร์การแสดงผลที่กว้างขึ้น

    อินเทอร์เฟซ Java สำหรับ OpenGL ES 3.0 บน Android ให้มาพร้อมกับ GLES30 เมื่อใช้ OpenGL ES 3.0 โปรดประกาศในไฟล์ Manifest ที่มีเมธอด <uses-feature> และแอตทริบิวต์ android:glEsVersion เช่น

    <manifest>
        <uses-feature android:glEsVersion="0x00030000" />
        ...
    </manifest>

    และอย่าลืมระบุบริบท OpenGL ES โดยเรียก setEGLContextClientVersion() กำลังส่ง 3 เป็นเวอร์ชัน

    ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้ OpenGL ES รวมถึงวิธีตรวจสอบเวอร์ชัน OpenGL ES ที่รองรับของอุปกรณ์ขณะรันไทม์ได้ที่คู่มือ API ของ OpenGL ES

    Mipmapping สําหรับ Drawable

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

    Android 4.2 (API ระดับ 17) เพิ่มการรองรับ MIPMAP ในคลาส Bitmap โดย Android จะสลับรูปภาพ MIP ใน Bitmap เมื่อคุณระบุแหล่งที่มาของ MIPMAP และเปิดใช้ setHasMipMap() ขณะนี้ใน Android 4.3 คุณสามารถเปิดใช้ mipmaps สำหรับออบเจ็กต์ BitmapDrawable ได้เช่นกัน โดยจัดทำเนื้อหา mipmap และ การตั้งค่าแอตทริบิวต์ android:mipMap ในไฟล์ทรัพยากรบิตแมปหรือโดยการเรียกใช้ hasMipMap()

    อินเทอร์เฟซผู้ใช้

    ดูการวางซ้อน

    คลาส ViewOverlay ใหม่จะสร้างเลเยอร์โปร่งใสเหนือ View ซึ่งคุณสามารถเพิ่มเนื้อหาที่เป็นภาพได้โดยไม่ส่งผลต่อลําดับชั้นของเลย์เอาต์ คุณรับ ViewOverlay สำหรับ View ใดก็ได้โดยโทรไปที่ getOverlay() การวางซ้อน จะมีขนาดและตำแหน่งเดียวกับมุมมองโฮสต์เสมอ (มุมมองที่ใช้สร้างมุมมองนี้) ซึ่งทำให้คุณสามารถเพิ่มเนื้อหาที่ปรากฏด้านหน้ามุมมองโฮสต์ แต่ขยายไม่ได้ ขอบเขตของมุมมองโฮสต์นั้น

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

    เมื่อคุณสร้างโฆษณาซ้อนทับสำหรับมุมมองวิดเจ็ต เช่น Button คุณจะ สามารถเพิ่มวัตถุ Drawable รายการลงในการวางซ้อนโดยเรียก add(Drawable) หากคุณเรียกใช้ getOverlay() สำหรับมุมมองเลย์เอาต์ เช่น RelativeLayout ระบบจะแสดงผลออบเจ็กต์เป็น ViewGroupOverlay คลาส ViewGroupOverlay เป็นคลาสย่อยของ ViewOverlay ซึ่งช่วยให้คุณเพิ่มออบเจ็กต์ View ได้ด้วยโดยการเรียกใช้ add(View)

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

    ตัวอย่างเช่น โค้ดต่อไปนี้จะแสดงภาพเคลื่อนไหวของมุมมองที่เลื่อนไปทางขวาโดยวางมุมมองนั้นไว้ในการวางซ้อนของมุมมองหลัก จากนั้นทำภาพเคลื่อนไหวของการแปลในมุมมองนั้น

    Kotlin

    val view: View? = findViewById(R.id.view_to_remove)
    val container: ViewGroup? = view?.parent as ViewGroup
    
    container?.apply {
        overlay.add(view)
        ObjectAnimator.ofFloat(view, "translationX", right.toFloat())
                .start()
    }

    Java

    View view = findViewById(R.id.view_to_remove);
    ViewGroup container = (ViewGroup) view.getParent();
    container.getOverlay().add(view);
    ObjectAnimator anim = ObjectAnimator.ofFloat(view, "translationX", container.getRight());
    anim.start();

    เลย์เอาต์ขอบเขตแบบออปติคัล

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

    ตัวอย่างเช่น รูปที่ 1 และ 2 แต่ละรายการแสดงเลย์เอาต์เหมือนกัน แต่เวอร์ชันในรูปที่ 1 คือ โดยใช้ขอบเขตคลิป (ลักษณะการทำงานเริ่มต้น) ในขณะที่รูปที่ 2 ใช้ขอบเขตแบบออปติคัล เนื่องจาก รูปภาพ 9 เส้นที่ใช้สำหรับปุ่ม และกรอบรูปมีระยะห่างจากขอบรอบขอบ ดูเหมือนจะไม่ปรับเข้าหากันหรือข้อความเมื่อใช้ขอบเขตของคลิป

    หมายเหตุ: ภาพหน้าจอในรูปที่ 1 และ 2 เปิดใช้การตั้งค่าสำหรับนักพัฒนาซอฟต์แวร์ "แสดงขอบเขตเลย์เอาต์" สำหรับมุมมองแต่ละมุมมอง เส้นสีแดงจะแสดงขอบเขตเชิงแสง เส้นสีน้ำเงินจะแสดงขอบเขตคลิป และสีชมพูจะแสดงระยะขอบ

    รูปที่ 1 เลย์เอาต์ที่ใช้ขอบเขตคลิป (ค่าเริ่มต้น)

    รูปที่ 2 เลย์เอาต์โดยใช้ขอบเขตแบบออปติคัล

    หากต้องการจัดแนวมุมมองตามขอบเขตเชิงแสง ให้ตั้งค่าแอตทริบิวต์ android:layoutMode เป็น "opticalBounds" ในเลย์เอาต์หลักรายการใดรายการหนึ่ง เช่น

    <LinearLayout android:layoutMode="opticalBounds" ... >

    รูปที่ 3 มุมมองแบบซูมของภาพ 9 ช่องของปุ่ม Holo พร้อมขอบเขตการมองเห็น

    เพื่อให้สามารถใช้งานได้ ภาพ 9 จุดที่ใช้กับพื้นหลังของมุมมองของคุณต้องระบุ ขอบเขตแบบออปติคัลที่ใช้เส้นสีแดงที่ด้านล่างและด้านขวาของไฟล์ 9 แพตช์ (ตาม แสดงในรูปที่ 3) เส้นสีแดงแสดงขอบเขตที่ควรหักออกจากขอบเขตคลิปเพื่อให้เหลือขอบเขตเชิงแสงของรูปภาพ

    เมื่อคุณเปิดใช้ขอบเขตออปติคัลสำหรับ ViewGroup ในเลย์เอาต์ มุมมองสืบทอดจะสืบทอดโหมดการออกแบบขอบเขตออปติคัล เว้นแต่ว่าคุณจะลบล้างโหมดนี้สำหรับกลุ่มตาม การตั้งค่า android:layoutMode เป็น "clipBounds" องค์ประกอบเลย์เอาต์ทั้งหมดจะยึดตามขอบเขตการมองเห็นของมุมมองย่อยด้วย โดยปรับขอบเขตของตัวเองตามขอบเขตการมองเห็นของมุมมองภายใน อย่างไรก็ตาม ขณะนี้องค์ประกอบเลย์เอาต์ (คลาสย่อยของ ViewGroup) ยังไม่รองรับขอบเขตเชิงแสงสำหรับรูปภาพ 9 ส่วนที่ใช้กับพื้นหลังของตนเอง

    หากคุณสร้างมุมมองที่กำหนดเองโดยการแยกคลาสย่อยของ View, ViewGroup หรือคลาสย่อยใดๆ ของคลาสดังกล่าว มุมมองของคุณจะรับค่าลักษณะการเชื่อมโยงแบบออปติคอลเหล่านี้

    หมายเหตุ: วิดเจ็ตทั้งหมดที่ธีม Holo รองรับได้รับการอัปเดตให้มีขอบเขตเชิงแสงแล้ว ซึ่งรวมถึง Button, Spinner, EditText และอื่นๆ คุณจึงได้รับประโยชน์ทันทีโดยการตั้งค่า แอตทริบิวต์ android:layoutMode เป็น "opticalBounds" หากแอปใช้ธีม Holo (Theme.Holo, Theme.Holo.Light ฯลฯ)

    หากต้องการระบุขอบเขตแบบออปติคัลสำหรับรูปภาพ 9 เส้นของคุณเองด้วยเครื่องมือวาด 9 เส้น ให้กด "ควบคุม" ค้างไว้ คลิกพิกเซลเส้นขอบ

    ภาพเคลื่อนไหวสำหรับค่าสี่เหลี่ยมผืนผ้า

    ตอนนี้คุณสามารถสร้างภาพเคลื่อนไหวระหว่างค่า Rect 2 ค่าด้วย RectEvaluator ใหม่ คลาสใหม่นี้เป็นการใช้งาน TypeEvaluator ที่คุณส่งไปยัง ValueAnimator.setEvaluator() ได้

    การแนบหน้าต่างและโฟกัส Listener

    ก่อนหน้านี้ หากต้องการฟังเมื่อมุมมองแนบ/เลิกแนบกับหน้าต่างหรือเมื่อโฟกัสของมุมมองเปลี่ยนไป คุณจะต้องลบล้างคลาส View เพื่อติดตั้งใช้งาน onAttachedToWindow() และ onDetachedFromWindow() หรือ onWindowFocusChanged() ตามลำดับ

    ตอนนี้ หากต้องการรับเหตุการณ์การแนบและถอดออก คุณสามารถใช้ ViewTreeObserver.OnWindowAttachListener และตั้งค่าในมุมมองด้วย addOnWindowAttachListener() แทน หากต้องการรับเหตุการณ์ที่มีโฟกัส ให้ติดตั้งใช้งาน ViewTreeObserver.OnWindowFocusChangeListener และตั้งค่าในมุมมองด้วย addOnWindowFocusChangeListener()

    รองรับโอเวอร์สแกนทีวี

    ตอนนี้คุณเปิดใช้การเลื่อนขอบภาพสำหรับเลย์เอาต์แอปได้แล้วเพื่อให้แอปแสดงเต็มหน้าจอบนทีวีทุกเครื่อง โหมดการเลื่อนออกจะกำหนดโดย Flag FLAG_LAYOUT_IN_OVERSCAN ซึ่งคุณเปิดใช้ได้กับธีมของแพลตฟอร์ม เช่น Theme_DeviceDefault_NoActionBar_Overscan หรือเปิดใช้สไตล์ windowOverscan ในธีมที่กำหนดเอง

    การวางแนวหน้าจอ

    ตอนนี้แอตทริบิวต์ screenOrientation ของแท็ก <activity> รองรับค่าเพิ่มเติมเพื่อปฏิบัติตามค่ากําหนดของผู้ใช้สำหรับการหมุนอัตโนมัติแล้ว ดังนี้

    "userLandscape"
    ทำงานเหมือนกับ "sensorLandscape" เว้นแต่ผู้ใช้ปิดใช้การหมุนอัตโนมัติ อุปกรณ์จะล็อกในแนวนอนตามปกติและไม่พลิก
    "userPortrait"
    มีลักษณะการทำงานเหมือนกับ "sensorPortrait" ยกเว้นในกรณีที่ผู้ใช้ปิดใช้การหมุนอัตโนมัติ ระบบจะล็อกไว้ในแนวตั้งปกติและจะไม่พลิก
    "fullUser"
    ทํางานเหมือนกับ "fullSensor" และอนุญาตให้หมุนได้ 4 ทิศทาง ยกเว้นในกรณีที่ผู้ใช้ปิดใช้การหมุนอัตโนมัติ ระบบจะล็อกการวางแนวที่ผู้ใช้ต้องการ

    นอกจากนี้ คุณยังประกาศ "locked" เพื่อล็อกการวางแนวของแอปได้ด้วย การวางแนวปัจจุบันของหน้าจอ

    ภาพเคลื่อนไหวการหมุน

    ช่อง rotationAnimation ใหม่ใน WindowManager ให้คุณเลือกภาพเคลื่อนไหว 1 แบบจาก 3 แบบ ที่ต้องการใช้เมื่อระบบเปลี่ยนการวางแนวหน้าจอ ภาพเคลื่อนไหวทั้ง 3 แบบมีดังนี้

    หมายเหตุ: ภาพเคลื่อนไหวเหล่านี้จะพร้อมใช้งานเมื่อคุณตั้งค่ากิจกรรมให้ใช้ "เต็มหน้าจอ" ซึ่งคุณสามารถเปิดใช้กับธีมอย่างเช่น Theme.Holo.NoActionBar.Fullscreen

    ตัวอย่างเช่น วิธีเปิดใช้ภาพเคลื่อนไหว "การซ้อนทับ" มีดังนี้

    Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        val params: WindowManager.LayoutParams = window.attributes
        params.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE
        window.attributes = params
        ...
    }

    Java

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        WindowManager.LayoutParams params = getWindow().getAttributes();
        params.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
        getWindow().setAttributes(params);
        ...
    }

    ข้อมูลจากผู้ใช้

    เซ็นเซอร์ประเภทใหม่

    เซ็นเซอร์ TYPE_GAME_ROTATION_VECTOR ใหม่ช่วยให้คุณตรวจจับการหมุนของอุปกรณ์ได้โดยไม่ต้องกังวลเกี่ยวกับสัญญาณรบกวนแม่เหล็ก TYPE_GAME_ROTATION_VECTOR ไม่เหมือนกับเซ็นเซอร์ TYPE_ROTATION_VECTOR ตรงที่ไม่อ้างอิงทางแม่เหล็กเหนือ

    เซ็นเซอร์ TYPE_GYROSCOPE_UNCALIBRATED และ TYPE_MAGNETIC_FIELD_UNCALIBRATED ใหม่ให้ข้อมูลดิบของเซ็นเซอร์โดยไม่มี การประเมินการให้อคติ กล่าวคือ เซ็นเซอร์ TYPE_GYROSCOPE และ TYPE_MAGNETIC_FIELD ที่มีอยู่จะให้ข้อมูลเซ็นเซอร์ที่พิจารณาความเบี่ยงเบนโดยประมาณจากการส่ายของไจโรและเหล็กแข็งในอุปกรณ์ตามลำดับ ขณะที่เซ็นเซอร์เหล่านี้เวอร์ชัน "ไม่ได้ปรับเทียบ" ใหม่จะให้ข้อมูลเซ็นเซอร์ดิบและค่าความเบี่ยงเบนโดยประมาณแยกกัน เซ็นเซอร์เหล่านี้ช่วยให้คุณทำการสอบเทียบข้อมูลเซ็นเซอร์ที่กำหนดเองได้โดยการปรับปรุงค่าเบี่ยงเบนโดยประมาณด้วยข้อมูลภายนอก

    บริการฟังการแจ้งเตือน

    Android 4.3 เพิ่มคลาสบริการใหม่ NotificationListenerService ซึ่งช่วยให้แอปได้รับข้อมูลเกี่ยวกับการแจ้งเตือนใหม่เมื่อระบบโพสต์การแจ้งเตือน

    หากปัจจุบันแอปของคุณใช้ API บริการการช่วยเหลือพิเศษเพื่อเข้าถึงการแจ้งเตือนของระบบ คุณควรอัปเดตแอปให้ใช้ API เหล่านี้แทน

    Contacts Provider

    การค้นหา "รายชื่อติดต่อ"

    คำค้นหาใหม่ของผู้ให้บริการรายชื่อติดต่อชื่อ Contactables.CONTENT_URI เป็นวิธีที่มีประสิทธิภาพในการรับ Cursor 1 รายการที่มีอีเมลและหมายเลขโทรศัพท์ทั้งหมดที่เป็นของรายชื่อติดต่อทั้งหมดที่ตรงกับคำค้นหาที่ระบุ

    ค้นหาข้อมูล Delta ของรายชื่อติดต่อ

    เราได้เพิ่ม API ใหม่ลงในผู้ให้บริการรายชื่อติดต่อ ซึ่งจะช่วยให้คุณค้นหาการเปลี่ยนแปลงล่าสุดของข้อมูลรายชื่อติดต่อได้อย่างมีประสิทธิภาพ ก่อนหน้านี้ แอปจะได้รับการแจ้งเตือนเมื่อมีการเปลี่ยนแปลงข้อมูลรายชื่อติดต่อ แต่คุณจะไม่ทราบว่ามีอะไรเปลี่ยนแปลงไปบ้าง และจะต้องดึงข้อมูลรายชื่อติดต่อทั้งหมดออกมาแล้วเรียกดูทีละรายการเพื่อค้นหาการเปลี่ยนแปลง

    หากต้องการติดตามการเปลี่ยนแปลงในการแทรกและอัปเดต ตอนนี้คุณสามารถใส่พารามิเตอร์ CONTACT_LAST_UPDATED_TIMESTAMP ลงในส่วนที่เลือกเพื่อค้นหาเฉพาะรายชื่อติดต่อที่มีการเปลี่ยนแปลงตั้งแต่ครั้งล่าสุดที่คุณค้นหาผู้ให้บริการได้แล้ว

    หากต้องการติดตามว่ารายชื่อติดต่อใดถูกลบแล้ว ตารางใหม่ ContactsContract.DeletedContacts จะแสดงบันทึกรายชื่อติดต่อที่ถูกลบไปแล้ว (แต่รายชื่อติดต่อที่ลบแต่ละรายการจะเก็บไว้ในตารางนี้ในระยะเวลาจำกัด) เช่นเดียวกับ CONTACT_LAST_UPDATED_TIMESTAMP คุณสามารถใช้พารามิเตอร์การเลือกใหม่ CONTACT_DELETED_TIMESTAMP เพื่อตรวจสอบว่ามีการลบรายชื่อติดต่อใดบ้างนับตั้งแต่ที่คุณค้นหาผู้ให้บริการครั้งล่าสุด นอกจากนี้ ตารางยังมี DAYS_KEPT_MILLISECONDS คงที่ซึ่งมีจำนวนวัน (เป็นมิลลิวินาที) ที่จะเก็บรักษาบันทึกไว้

    นอกจากนี้ ตอนนี้ Contacts Provider จะออกอากาศการดำเนินการ CONTACTS_DATABASE_CREATED เมื่อผู้ใช้ล้างพื้นที่เก็บข้อมูลรายชื่อติดต่อผ่านเมนูการตั้งค่าระบบ ซึ่งจะสร้างฐานข้อมูล Contacts Provider ขึ้นมาใหม่ได้อย่างมีประสิทธิภาพ การดำเนินการนี้มีไว้เพื่อส่งสัญญาณให้แอปต้องทิ้งข้อมูลติดต่อทั้งหมดที่เก็บไว้และโหลดข้อมูลนั้นอีกครั้งด้วยข้อความค้นหาใหม่

    สำหรับโค้ดตัวอย่างที่ใช้ API เหล่านี้เพื่อตรวจสอบการเปลี่ยนแปลงในรายชื่อติดต่อ โปรดดูใน ApiDemos ตัวอย่างที่มีอยู่ในการดาวน์โหลดตัวอย่าง SDK

    การแปล

    ปรับปรุงการรองรับข้อความแบบ 2 ทิศทาง

    Android เวอร์ชันเก่ารองรับภาษาและเลย์เอาต์แบบขวาไปซ้าย (RTL) แต่บางครั้งอาจจัดการข้อความแบบผสมทิศทางไม่ถูกต้อง ดังนั้น Android 4.3 จึงเพิ่ม BidiFormatter API ที่ช่วยคุณจัดรูปแบบข้อความที่มีเนื้อหาในทิศทางตรงข้ามอย่างเหมาะสมโดยไม่ทำให้ข้อความส่วนใดส่วนหนึ่งอ่านไม่ออก

    เช่น เมื่อต้องการสร้างประโยคที่มีตัวแปรสตริง เช่น "หรือคุณหมายถึง 15 Bay Street, Laurel, CA?" โดยปกติแล้วคุณจะส่งแหล่งข้อมูลสตริงที่แปลแล้วและตัวแปรไปยัง String.format():

    Kotlin

    val suggestion = String.format(resources.getString(R.string.did_you_mean), address)

    Java

    Resources res = getResources();
    String suggestion = String.format(res.getString(R.string.did_you_mean), address);

    อย่างไรก็ตาม ถ้าภาษาเป็นภาษาฮีบรู สตริงรูปแบบจะมีลักษณะดังนี้

    האם התכוונת ל 15 Bay Street, Laurel, CA?

    ไม่ถูกต้องเนื่องจาก "15" ควรอยู่บนถนน "Bay Street" วิธีแก้ไขคือใช้ BidiFormatter และเมธอด unicodeWrap() ตัวอย่างเช่น โค้ดด้านบนจะเป็นดังนี้

    Kotlin

    val bidiFormatter = BidiFormatter.getInstance()
    val suggestion = String.format(
            resources.getString(R.string.did_you_mean),
            bidiFormatter.unicodeWrap(address)
    )

    Java

    Resources res = getResources();
    BidiFormatter bidiFormatter = BidiFormatter.getInstance();
    String suggestion = String.format(res.getString(R.string.did_you_mean),
            bidiFormatter.unicodeWrap(address));

    โดยค่าเริ่มต้น unicodeWrap() จะใช้เมธอด การคาดการณ์ทิศทางแรกอย่างแม่นยำ ซึ่งอาจทําให้เกิดข้อผิดพลาดหาก สัญญาณสำหรับทิศทางของข้อความไม่ได้แสดงถึงทิศทางที่เหมาะสมสำหรับเนื้อหาโดยรวม หากจำเป็น คุณสามารถระบุวิธีการเรียนรู้แบบอื่นโดยการส่งผ่านค่าคงที่ TextDirectionHeuristic ค่าใดค่าหนึ่งจาก TextDirectionHeuristics ไปยัง unicodeWrap()

    หมายเหตุ: API ใหม่เหล่านี้ยังพร้อมใช้งานสำหรับ Android เวอร์ชันก่อนหน้าผ่านคลังการสนับสนุนของ Android ด้วยคลาส BidiFormatter และ API ที่เกี่ยวข้อง

    บริการการช่วยเหลือพิเศษ

    จัดการเหตุการณ์สําคัญ

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

    เลือกข้อความและคัดลอก/วาง

    ตอนนี้ AccessibilityNodeInfo มี API ที่ช่วยให้ AccessibilityService เลือก ตัด คัดลอก และวางข้อความในโหนดได้

    ในการระบุการเลือกข้อความที่จะตัดหรือคัดลอก บริการการเข้าถึงสามารถใช้ การดำเนินการ, ACTION_SET_SELECTION, ผ่าน แล้วเลือกตำแหน่งเริ่มต้นและสิ้นสุดด้วย ACTION_ARGUMENT_SELECTION_START_INT และ ACTION_ARGUMENT_SELECTION_END_INT หรือจะเลือกข้อความด้วยการปรับตำแหน่งเคอร์เซอร์โดยใช้การดำเนินการที่มีอยู่ ACTION_NEXT_AT_MOVEMENT_GRANULARITY (ก่อนหน้านี้มีไว้สำหรับการย้ายตำแหน่งเคอร์เซอร์เท่านั้น) และเพิ่มอาร์กิวเมนต์ ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN ก็ได้

    จากนั้นคุณสามารถตัดหรือคัดลอกด้วย ACTION_CUT ACTION_COPY แล้ววางในภายหลังด้วย ACTION_PASTE

    หมายเหตุ: API ใหม่เหล่านี้จะพร้อมใช้งานสำหรับเวอร์ชันก่อนหน้าด้วยเช่นกัน ของ Android ผ่านทางการสนับสนุนของ Android คลัง โดยมีAccessibilityNodeInfoCompat

    ประกาศฟีเจอร์การช่วยเหลือพิเศษ

    เริ่มตั้งแต่ Android 4.3 บริการการช่วยเหลือพิเศษจะต้องประกาศความสามารถในการเข้าถึง ในไฟล์ข้อมูลเมตาเพื่อใช้ฟีเจอร์การช่วยเหลือพิเศษบางอย่าง หากไม่ได้ขอความสามารถดังกล่าวในไฟล์ข้อมูลเมตา ฟีเจอร์จะไม่ทำงาน หากต้องการประกาศความสามารถด้านการช่วยเหลือพิเศษของบริการ คุณต้องมีแอตทริบิวต์ XML ที่สอดคล้องกับค่าคงที่ "capability" ต่างๆ ในคลาส AccessibilityServiceInfo

    ตัวอย่างเช่น หากบริการไม่ได้ขอความสามารถ flagRequestFilterKeyEvents บริการดังกล่าวจะไม่ได้รับการแจ้งเตือนเหตุการณ์สำคัญ

    การทดสอบและการแก้ไขข้อบกพร่อง

    การทดสอบ UI อัตโนมัติ

    คลาส UiAutomation ใหม่มี API ที่ให้คุณจำลองผู้ใช้ได้ การดำเนินการสำหรับการทดสอบอัตโนมัติ การใช้ AccessibilityService API ของแพลตฟอร์มจะทำให้UiAutomation API ช่วยให้คุณตรวจสอบเนื้อหาบนหน้าจอและแทรกแป้นพิมพ์และเหตุการณ์การแตะได้ตามต้องการ

    หากต้องการรับอินสแตนซ์ของ UiAutomation ให้เรียกใช้ Instrumentation.getUiAutomation() อยู่ในคำสั่งซื้อ คุณต้องใส่ตัวเลือก -w พร้อมกับคำสั่ง instrument เพื่อให้ดำเนินการนี้ได้ เมื่อเรียกใช้ InstrumentationTestCase จาก adb shell

    เมื่อใช้อินสแตนซ์ UiAutomation คุณจะเรียกใช้เหตุการณ์ที่กําหนดเองเพื่อทดสอบแอปได้โดยการเรียก executeAndWaitForEvent() โดยส่ง Runnable ให้ดำเนินการ ระยะเวลาหมดเวลาสําหรับการดำเนินการ และการใช้งานอินเทอร์เฟซ UiAutomation.AccessibilityEventFilter คุณจะได้รับการเรียกใช้ภายในการติดตั้งใช้งาน UiAutomation.AccessibilityEventFilter ซึ่งช่วยให้คุณกรองเหตุการณ์ที่สนใจและระบุความสําเร็จหรือความล้มเหลวของกรณีทดสอบหนึ่งๆ ได้

    หากต้องการสังเกตเหตุการณ์ทั้งหมดระหว่างการทดสอบ ให้สร้างการใช้งาน UiAutomation.OnAccessibilityEventListener และส่งไปยัง setOnAccessibilityEventListener() จากนั้นอินเทอร์เฟซโปรแกรมฟังจะได้รับการเรียกใช้ onAccessibilityEvent() ทุกครั้งที่เกิดเหตุการณ์ โดยจะได้รับออบเจ็กต์ AccessibilityEvent ที่อธิบายเหตุการณ์

    UiAutomation API ยังมีการดำเนินการอื่นๆ อีกมากมายที่แสดงในระดับต่ำมากเพื่อส่งเสริมการพัฒนาเครื่องมือทดสอบ UI เช่น uiautomator ตัวอย่างเช่น UiAutomation ยังสามารถ:

    • แทรกเหตุการณ์อินพุต
    • เปลี่ยนการวางแนวของหน้าจอ
    • ถ่ายภาพหน้าจอ

    และที่สำคัญที่สุดสำหรับเครื่องมือทดสอบ UI คือ UiAutomation API จะทำงานข้ามขอบเขตแอปพลิเคชัน ต่างจากใน Instrumentation

    เหตุการณ์ Systrace สำหรับแอป

    Android 4.3 เพิ่มคลาส Trace ที่มีเมธอดแบบคงที่ 2 รายการ ได้แก่ beginSection() และ endSection() ซึ่งช่วยให้คุณกำหนดบล็อกโค้ดที่จะรวมไว้ในรายงาน systrace ได้ โดยการสร้าง ส่วนของโค้ดที่ตรวจสอบย้อนกลับได้ในแอปของคุณ บันทึกของ systrace จะแสดงรายละเอียดเพิ่มเติม การวิเคราะห์ตำแหน่งที่เกิดการชะลอตัวภายในแอป

    อ่านข้อมูลเกี่ยวกับการใช้เครื่องมือ Systrace ได้ที่การวิเคราะห์การแสดงผลและประสิทธิภาพด้วย Systrace

    ความปลอดภัย

    แหล่งเก็บคีย์ Android สำหรับคีย์ส่วนตัวของแอป

    ตอนนี้ Android มีผู้ให้บริการความปลอดภัย Java ที่กําหนดเองในKeyStore สถานะที่เรียกว่า Android Key Store ซึ่งช่วยให้คุณสร้างและบันทึกคีย์ส่วนตัวที่แอปของคุณเท่านั้นที่จะเห็นและใช้ได้ หากต้องการโหลด Android Key Store ให้ส่ง"AndroidKeyStore"ไปยัง KeyStore.getInstance()

    ในการจัดการข้อมูลเข้าสู่ระบบส่วนตัวของแอปใน Android Key Store ให้สร้างคีย์ใหม่ด้วย KeyPairGenerator ด้วย KeyPairGeneratorSpec ก่อนอื่น ให้รับอินสแตนซ์ของ KeyPairGenerator โดยการเรียกใช้ getInstance() จากนั้นโทร initialize() ส่งอินสแตนซ์ KeyPairGeneratorSpecซึ่งคุณใช้ได้ใน KeyPairGeneratorSpec.Builder สุดท้าย รับ KeyPair ด้วยการโทรหา generateKeyPair()

    ที่เก็บข้อมูลเข้าสู่ระบบฮาร์ดแวร์

    ขณะนี้ Android รองรับพื้นที่เก็บข้อมูลแบบฮาร์ดแวร์สำหรับ KeyChain แล้ว ข้อมูลเข้าสู่ระบบของคุณ เพื่อเพิ่มความปลอดภัยโดยทำให้คีย์ไม่สามารถใช้ดึงข้อมูลได้ กล่าวคือ เมื่อคีย์อยู่ในที่เก็บคีย์ที่รองรับฮาร์ดแวร์ (องค์ประกอบที่ปลอดภัย, TPM หรือ TrustZone) จะใช้คีย์ดังกล่าวสําหรับการดำเนินการเข้ารหัสได้ แต่จะส่งออกเนื้อหาคีย์ส่วนตัวไม่ได้ แม้แต่เคอร์เนลของระบบปฏิบัติการก็ไม่สามารถเข้าถึงข้อมูลคีย์นี้ได้ แม้ว่าอุปกรณ์ที่ใช้ Android บางรุ่นจะไม่รองรับพื้นที่เก็บข้อมูลบน คุณสามารถตรวจสอบขณะรันไทม์ว่าพื้นที่เก็บข้อมูลที่ใช้ฮาร์ดแวร์พร้อมใช้งานหรือไม่โดยการเรียกใช้ KeyChain.IsBoundKeyAlgorithm()

    การประกาศไฟล์ Manifest

    ฟีเจอร์ที่จำเป็นซึ่งประกาศได้

    ตอนนี้ <uses-feature> รองรับค่าต่อไปนี้แล้ว องค์ประกอบเพื่อให้มั่นใจได้ว่าแอปของคุณจะติดตั้งเฉพาะในอุปกรณ์ที่มีฟีเจอร์ ที่แอปของคุณต้องใช้

    FEATURE_APP_WIDGETS
    ประกาศว่าแอปของคุณมีวิดเจ็ตแอปและควรติดตั้งในอุปกรณ์ที่มีหน้าจอหลักหรือตำแหน่งที่คล้ายกันซึ่งผู้ใช้สามารถฝังวิดเจ็ตแอปได้เท่านั้น ตัวอย่าง
    <uses-feature android:name="android.software.app_widgets" android:required="true" />
    FEATURE_HOME_SCREEN
    ประกาศว่าแอปของคุณทำงานเหมือนเป็นอุปกรณ์ทดแทนหน้าจอหลักและควรติดตั้งเฉพาะใน อุปกรณ์ที่สนับสนุนแอปหน้าจอหลักของบุคคลที่สาม ตัวอย่าง
    <uses-feature android:name="android.software.home_screen" android:required="true" />
    FEATURE_INPUT_METHODS
    ประกาศว่าแอปของคุณมีวิธีการป้อนข้อมูลที่กำหนดเอง (แป้นพิมพ์ที่สร้างด้วย InputMethodService) และควรติดตั้งในอุปกรณ์ที่รองรับวิธีการป้อนข้อมูลของบุคคลที่สามเท่านั้น ตัวอย่าง
    <uses-feature android:name="android.software.input_methods" android:required="true" />
    FEATURE_BLUETOOTH_LE
    ประกาศว่าแอปของคุณใช้ Bluetooth Low Energy API และควรติดตั้งในอุปกรณ์ที่สื่อสารกับอุปกรณ์อื่นๆ ผ่านบลูทูธพลังงานต่ำได้เท่านั้น ตัวอย่าง
    <uses-feature android:name="android.software.bluetooth_le" android:required="true" />

    สิทธิ์ของผู้ใช้

    ตอนนี้ระบบรองรับค่าต่อไปนี้ใน <uses-permission> เพื่อประกาศสิทธิ์ที่จําเป็นต่อแอปในการเข้าถึง API บางรายการ

    BIND_NOTIFICATION_LISTENER_SERVICE
    จำเป็นสำหรับการใช้ NotificationListenerService API ใหม่
    SEND_RESPOND_VIA_MESSAGE
    ต้องระบุเพื่อรับ ACTION_RESPOND_VIA_MESSAGE Intent

    สำหรับมุมมองโดยละเอียดของการเปลี่ยนแปลง API ทั้งหมดใน Android 4.3 โปรดดูที่ รายงานความแตกต่างของ API