เชื่อมต่อกับเครือข่าย

หากต้องการดำเนินการกับเครือข่ายในแอปพลิเคชัน คุณต้องใส่สิทธิ์ต่อไปนี้ในไฟล์ Manifest

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

แนวทางปฏิบัติแนะนำสำหรับการสื่อสารผ่านเครือข่ายที่ปลอดภัย

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

  • ลดปริมาณข้อมูล ผู้ใช้ที่ละเอียดอ่อนหรือข้อมูลส่วนบุคคลที่คุณส่งผ่านเครือข่าย
  • ส่งการรับส่งข้อมูลเครือข่ายทั้งหมดจากแอปผ่าน SSL
  • ลองสร้างการกำหนดค่าความปลอดภัยของเครือข่าย ซึ่งจะช่วยให้แอป เชื่อถือผู้ออกใบรับรอง (CA) ที่กำหนดเองหรือจำกัดชุด CA ของระบบ ที่แอปเชื่อถือเพื่อการสื่อสารที่ปลอดภัย

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

เลือกไคลเอ็นต์ HTTP

แอปที่เชื่อมต่อเครือข่ายส่วนใหญ่ใช้ HTTP ในการรับและส่งข้อมูล แพลตฟอร์ม Android มีไคลเอ็นต์ HttpsURLConnection ซึ่งรองรับ TLS, การอัปโหลดและดาวน์โหลดแบบสตรีม, การหมดเวลาที่กำหนดค่าได้, IPv6 และการจัดกลุ่มการเชื่อมต่อ

นอกจากนี้ยังมีไลบรารีของบุคคลที่สามที่ให้บริการ API ระดับสูงกว่าสำหรับการดำเนินการเครือข่ายด้วย ซึ่งรองรับฟีเจอร์อำนวยความสะดวกต่างๆ เช่น การ ซีเรียลไลซ์เนื้อหาคำขอและการดีซีเรียลไลซ์เนื้อหาการตอบกลับ

  • Retrofit: ไคลเอ็นต์ HTTP ที่ปลอดภัยตามประเภทสำหรับ JVM จาก Square ซึ่งสร้างขึ้นบน OkHttp Retrofit ช่วยให้คุณ สร้างอินเทอร์เฟซไคลเอ็นต์แบบประกาศและรองรับไลบรารี การซีเรียลไลซ์หลายรายการ
  • Ktor: ไคลเอ็นต์ HTTP จาก JetBrains ซึ่งสร้างขึ้น สำหรับ Kotlin โดยเฉพาะและขับเคลื่อนด้วย Coroutines Ktor รองรับเครื่องมือ ตัวจัดรูปแบบ และแพลตฟอร์มต่างๆ

แปลงคำขอ DNS

อุปกรณ์ที่ใช้ Android 10 (API ระดับ 29) ขึ้นไปรองรับการค้นหา DNS เฉพาะทางในตัวผ่านทั้งการค้นหาข้อความธรรมดาและโหมด DNS-over-TLS API ของ DnsResolver มีการแก้ปัญหาแบบอะซิงโครนัสทั่วไป ซึ่งช่วยให้คุณค้นหา SRV, NAPTR และประเภทระเบียนอื่นๆ ได้ การแยกวิเคราะห์การตอบกลับเป็นหน้าที่ของแอป

ในอุปกรณ์ที่ใช้ Android 9 (API ระดับ 28) และต่ำกว่า ตัวแก้ไข DNS ของแพลตฟอร์ม รองรับเฉพาะระเบียน A และ AAAA ซึ่งจะช่วยให้คุณค้นหาที่อยู่ IP ที่เชื่อมโยงกับชื่อได้ แต่ไม่รองรับระเบียนประเภทอื่นๆ

สำหรับแอปที่ใช้ NDK โปรดดู android_res_nsend

แคปซูลการดำเนินการของเครือข่ายด้วยที่เก็บ

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

คุณใช้ Retrofit เพื่อประกาศอินเทอร์เฟซที่ระบุเมธอด HTTP, URL, อาร์กิวเมนต์ และประเภทการตอบกลับสำหรับการดำเนินการเครือข่ายได้ ดังตัวอย่างต่อไปนี้

Kotlin

interface UserService {
    @GET("/users/{id}")
    suspend fun getUser(@Path("id") id: String): User
}

Java

public interface UserService {
    @GET("/user/{id}")
    Call<User> getUserById(@Path("id") String id);
}

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

Kotlin

class UserRepository constructor(
    private val userService: UserService
) {
    suspend fun getUserById(id: String): User {
        return userService.getUser(id)
    }
}

Java

class UserRepository {
    private UserService userService;

    public UserRepository(
            UserService userService
    ) {
        this.userService = userService;
    }

    public Call<User> getUserById(String id) {
        return userService.getUser(id);
    }
}

อย่าทำการดำเนินการเครือข่ายใน เทรดหลักเพื่อหลีกเลี่ยงการสร้าง UI ที่ไม่ตอบสนอง โดยค่าเริ่มต้น Android กําหนดให้คุณดําเนินการเครือข่ายใน เธรดอื่นที่ไม่ใช่เธรด UI หลัก หากคุณพยายามดำเนินการเครือข่าย ในเทรดหลัก ระบบจะแสดง NetworkOnMainThreadException

ในตัวอย่างโค้ดก่อนหน้า ระบบไม่ได้ทริกเกอร์การดำเนินการเครือข่ายจริงๆ ผู้เรียกใช้ UserRepository ต้องใช้การสร้างเธรดโดยใช้โครูทีนหรือใช้ฟังก์ชัน enqueue() ดูข้อมูลเพิ่มเติมได้ใน Codelab รับข้อมูลจากอินเทอร์เน็ต ซึ่งแสดงวิธีใช้การทำงานแบบหลายเธรดโดยใช้โครูทีน Kotlin

รับมือกับการเปลี่ยนแปลงการกำหนดค่า

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

คุณใช้ ViewModel เพื่อให้ ข้อมูลยังคงอยู่ได้แม้จะมีการเปลี่ยนแปลงการกำหนดค่า คอมโพเนนต์ ViewModel ได้รับการออกแบบมาเพื่อจัดเก็บและจัดการข้อมูลที่เกี่ยวข้องกับ UI ในลักษณะที่คำนึงถึงวงจรของคอมโพเนนต์ เมื่อใช้ UserRepository ก่อนหน้า ViewModel จะส่งคำขอเครือข่ายที่จำเป็นและแสดงผลลัพธ์ไปยัง Fragment หรือ Activity โดยใช้ LiveData

Kotlin

class MainViewModel constructor(
    savedStateHandle: SavedStateHandle,
    userRepository: UserRepository
) : ViewModel() {
    private val userId: String = savedStateHandle["uid"] ?:
        throw IllegalArgumentException("Missing user ID")

    private val _user = MutableLiveData<User>()
    val user = _user as LiveData<User>

    init {
        viewModelScope.launch {
            try {
                // Calling the repository is safe as it moves execution off
                // the main thread
                val user = userRepository.getUserById(userId)
                _user.value = user
            } catch (error: Exception) {
                // Show error message to user
            }

        }
    }
}

Java

class MainViewModel extends ViewModel {

    private final MutableLiveData<User> _user = new MutableLiveData<>();
    LiveData<User> user = (LiveData<User>) _user;

    public MainViewModel(
            SavedStateHandle savedStateHandle,
            UserRepository userRepository
    ) {
        String userId = savedStateHandle.get("uid");
        Call<User> userCall = userRepository.getUserById(userId);
        userCall.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if (response.isSuccessful()) {
                    _user.setValue(response.body());
                }
            }

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                // Show error message to user
            }
        });
    }
}

ดูข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อนี้ได้ในคำแนะนำที่เกี่ยวข้องต่อไปนี้