התחברות לרשת

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

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

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

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

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

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

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

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