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 ระบบเรียกใช้ความตั้งใจนี้เพื่อค้นหา แอปทั้งหมดสำหรับข้อจำกัดที่มีอยู่ แล้วสร้าง UI เพื่อให้ผู้ใช้หลัก จัดการข้อจำกัดสำหรับแต่ละโปรไฟล์ที่ถูกจำกัด

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

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

จากนั้นคุณใส่ออบเจ็กต์ RestrictionEntry ทั้งหมดลงใน ArrayList และใส่ไว้ในผลลัพธ์ของ Broadcast Receiver เป็นค่าสำหรับ เพิ่มเติม 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 ออบเจ็กต์หรือคู่คีย์-ค่าตามลำดับ

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

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

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

    <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();
    

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

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

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

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

    การเข้าถึงฟีเจอร์บลูทูธในอุปกรณ์กำหนดให้คุณต้องส่งคำขอบางอย่าง สิทธิ์ของผู้ใช้บลูทูธ สำหรับข้อมูลเพิ่มเติม โปรดดูที่คู่มือ 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 ฟีเจอร์ชื่อ "คำตอบด่วน" ช่วยให้ผู้ใช้สามารถตอบสนองต่อ ด้วย SMS ทันทีโดยไม่ต้องรับสายหรือปลดล็อกอุปกรณ์ ก่อนหน้านี้แอปการรับส่งข้อความเริ่มต้นจะจัดการข้อความด่วนเหล่านี้เสมอ ตอนนี้ทุกแอป สามารถประกาศความสามารถในการจัดการข้อความเหล่านี้โดยการสร้าง 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 ออบเจ็กต์ จากนั้นคุณจะสามารถเข้าถึงการระบุรูปแบบ DRM UUID ซึ่งมักมาจากข้อมูลเมตาในเนื้อหาและใช้เพื่อสร้าง อินสแตนซ์ของออบเจ็กต์ MediaDrm พร้อมตัวสร้าง

    การเข้ารหัสวิดีโอจากแพลตฟอร์ม

    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_KEY_MEDIA_POSITION_UPDATE โดยส่งต่อไปที่ 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 สำหรับทรัพยากรที่ถอนออกได้

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

    Android 4.2 (API ระดับ 17) เพิ่มการรองรับ Mipmaps ใน 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 มุมมองแบบซูมของปุ่ม Holo แบบ 9 จุดพร้อม ขอบเขตแสง

    เพื่อให้สามารถใช้งานได้ ภาพ 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_LAYOUT_IN_OVERSCAN ซึ่งคุณสามารถเปิดใช้ด้วยธีมแพลตฟอร์มต่างๆ ได้ เช่น Theme_DeviceDefault_NoActionBar_Overscan หรือโดยการเปิดใช้ สไตล์ windowOverscan ในธีมที่กำหนดเอง

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

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

    "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 หนึ่งรายการที่มีอีเมลและหมายเลขโทรศัพท์ทั้งหมดที่เป็นของรายชื่อติดต่อทั้งหมดที่ตรงกับคำค้นหาที่ระบุ

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

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

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

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

    นอกจากนี้ ผู้ให้บริการรายชื่อติดต่อจะประกาศการดำเนินการ 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 แคลิฟอร์เนีย

    ไม่ถูกต้องเนื่องจาก "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 สามารถรับการติดต่อกลับสำหรับ เหตุการณ์การป้อนข้อมูลที่สำคัญด้วยเมธอด Callback 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 ที่สอดคล้องกับ "ความสามารถ" ค่าคงที่ใน 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() อินเทอร์เฟซของ Listener ของคุณจะได้รับการเรียกไปยัง onAccessibilityEvent() ทุกครั้งที่เกิดเหตุการณ์ขึ้น โดยได้รับออบเจ็กต์ AccessibilityEvent ที่อธิบายเหตุการณ์

    มีการดำเนินการอื่นๆ อีกหลายอย่างที่ API ของ UiAutomation แสดง ในระดับที่ต่ำมาก เพื่อกระตุ้นการพัฒนาเครื่องมือทดสอบ 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 ให้ส่งบัตร "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
    ประกาศว่าแอปของคุณใช้ 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