התחברות לרשת

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

<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 ומופעל באמצעות קורוטינים. 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);
    }
}

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

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

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