התחברות לרשת

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

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

שיטות מומלצות לתקשורת מאובטחת ברשת

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

  • צריך לצמצם את כמות נתוני המשתמשים הרגישים או האישיים שמועברים ברשת.
  • שליחת כל תעבורת הנתונים מהאפליקציה ברשת באמצעות SSL.
  • מומלץ ליצור הגדרת אבטחת רשת, שמאפשרת לאפליקציה שלכם לבטוח ברשויות אישורים (CA) מותאמות אישית או להגביל את קבוצת רשויות האישורים של המערכת שהיא בוטחת בהן לצורך תקשורת מאובטחת.

מידע נוסף על יישום עקרונות של רשת מאובטחת זמין בטיפים לאבטחת רשת.

בחירת לקוח HTTP

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

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

  • Retrofit: לקוח HTTP בטוח לטיפוסים עבור JVM מבית Square, שמבוסס על OkHttp. ‫Retrofit מאפשרת ליצור ממשק לקוח באופן הצהרתי, ויש לה תמיכה בכמה ספריות סריאליזציה.
  • Ktor: צד לקוח ב-HTTP מבית JetBrains, שנוצר כולו עבור Kotlin ומופעל על ידי קורוטינות. ‫Ktor תומך במנועים, בסריאליזציה ובפלטפורמות שונות.

פתרון שאילתות DNS

במכשירים עם Android מגרסה 10 (API ברמה 29) ומעלה יש תמיכה מובנית בחיפושי DNS מיוחדים באמצעות חיפושים בטקסט גלוי ומצב DNS-over-TLS. ‫DnsResolver API מספק פתרון כללי ואסינכרוני, שמאפשר לכם לחפש SRV, NAPTR וסוגים אחרים של רשומות. האפליקציה צריכה לנתח את התגובה.

במכשירים שפועלת בהם מערכת Android 9 (רמת API‏ 28) ומטה, פותר ה-DNS של הפלטפורמה תומך רק ברשומות A ו-AAAA. כך אפשר לחפש את כתובות ה-IP שמשויכות לשם, אבל אי אפשר לחפש סוגים אחרים של רשומות.

לגבי אפליקציות שמבוססות על NDK, אפשר לעיין במאמר בנושא android_res_nsend.

הוספת פעולות ברשת למאגר

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

אפשר להשתמש ב-Retrofit כדי להצהיר על ממשק שמציין את ה-method ב-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);
    }
}

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

בדוגמת הקוד הקודמת, הפעולה ברשת לא מופעלת בפועל. המתקשר של UserRepository צריך להטמיע את השרשור באמצעות קורוטינות או באמצעות הפונקציה enqueue(). מידע נוסף זמין במעבדת התכנות Get data from the internet (קבלת נתונים מהאינטרנט), שבה מוסבר איך להטמיע שרשור באמצעות קורוטינות של 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
            }
        });
    }
}

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