התחברות לרשת

כדי לבצע פעולות רשת באפליקציה, המניפסט חייב לכלול את ההרשאות הבאות:

<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 ברמה גבוהה יותר לפעולות רישות זמין גם הוא. המפתחות האלה תומכים בתכונות נוחות שונות, כמו סריאליזציה של גופי הבקשות וריאליזציה (deserialization) של גופי התגובה.

  • Retrofit: HTTP שאינו בטוח לסוג לקוח ל-JVM מתוך Square, שמבוסס על OkHttp. Retrofit מאפשר לך ליצור ממשק לקוח באופן הצהרתי והוא תומך בכמה של ספריות בהמשכים.
  • Ktor: לקוח HTTP מ-JetBrains, שנבנה במיוחד עבור Kotlin ומופעלים על ידי קורוטינים. 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);
    }
}

כדי להימנע מיצירת ממשק משתמש שלא מגיב, אל תבצעו פעולות רשת ה-thread הראשי. כברירת מחדל, Android דורש לבצע פעולות רשת מלבד ה-thread הראשי של ממשק המשתמש. אם מנסים לבצע פעולות ברשת ב-thread הראשי, NetworkOnMainThreadException נזרק.

בדוגמת הקוד הקודמת, פעולת הרשת לא מופעלת בפועל. מבצע הקריאה החוזרת של UserRepository חייבים להטמיע את השרשורים באמצעות קורוטינים או באמצעות הפונקציה enqueue() מותאמת אישית. מידע נוסף זמין במאמר קבלת נתונים מ-Codelab אינטרנט, שמדגים איך להטמיע שרשורים באמצעות קורוטינים של Kotlin.

להימנע משינויי הגדרות אישיות

כאשר מתרחש שינוי בתצורה, כגון סיבוב מסך, מקטע הפעילות מושמדת ונוצרת מחדש. נתונים שלא נשמרים במכונה עבור פעילות המקטעים שלך, שיכולה להכיל כמויות קטנות בלבד של נתונים, אבד. במקרה כזה, יכול להיות שתצטרכו להגיש שוב את בקשות הרשת.

אפשר להשתמש ב-ViewModel כדי לאפשר הנתונים שלכם מתקיימים גם לאחר שינויים בתצורה. הרכיב ViewModel הוא לאחסון ולניהול נתונים שקשורים לממשק המשתמש תוך התייחסות למחזור החיים בדרך הזו. באמצעות הפונקציה UserRepository הקודמת, ViewModel יכול להפוך את את בקשות הרשת הדרושות ומספקות את התוצאה למקטע או לפעילות שלך באמצעות 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
            }
        });
    }
}

מידע נוסף על הנושא הזה זמין במדריכים הקשורים הבאים: