নেটওয়ার্কে কানেক্ট করুন

আপনার অ্যাপ্লিকেশনে নেটওয়ার্ক অপারেশন করতে, আপনার ম্যানিফেস্টে নিম্নলিখিত অনুমতিগুলি অন্তর্ভুক্ত থাকতে হবে:

<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: OkHttp-এর উপরে তৈরি, Square থেকে JVM-এর জন্য একটি টাইপ-সেফ HTTP ক্লায়েন্ট। Retrofit আপনাকে একটি ক্লায়েন্ট ইন্টারফেস ডিক্লারেটিভভাবে তৈরি করতে দেয় এবং এতে বেশ কয়েকটি সিরিয়ালাইজেশন লাইব্রেরির জন্য সহায়তা রয়েছে।
  • Ktor: JetBrains-এর একটি HTTP ক্লায়েন্ট, যা সম্পূর্ণভাবে Kotlin-এর জন্য তৈরি এবং Coroutines দ্বারা পরিচালিত। Ktor বিভিন্ন ইঞ্জিন, সিরিয়ালাইজার ও প্ল্যাটফর্ম সাপোর্ট করে।

ডিএনএস কোয়েরি সমাধান করা

Android 10 (API লেভেল 29) এবং তার পরবর্তী যেকোনও ভার্সনে চলা ডিভাইসে ক্লিয়ারটেক্সট লুকআপ এবং DNS-ওভার-TLS মোড, উভয়ের মাধ্যমে বিশেষায়িত DNS লুকআপের জন্য বিল্ট-ইন সাপোর্ট আছে। DnsResolver API জেনেরিক, অ্যাসিঙ্ক্রোনাস রেজোলিউশন প্রদান করে, যা আপনাকে SRV, NAPTR এবং অন্যান্য রেকর্ডের ধরন দেখতে দেয়। অ্যাপের মাধ্যমে উত্তর পার্স করার কাজ বাকি আছে।

Android 9 (API লেভেল ২৮) এবং তার আগের ভার্সনে চলে এমন ডিভাইসে, প্ল্যাটফর্ম 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
}

জাভা

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

জাভা

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() ফাংশন ব্যবহার করে থ্রেডিং প্রয়োগ করতে হবে। আরও তথ্যের জন্য, ইন্টারনেট থেকে ডেটা পান কোডল্যাব দেখুন, যেখানে Kotlin coroutines ব্যবহার করে কীভাবে থ্রেডিং প্রয়োগ করতে হয় তা দেখানো হয়েছে।

কনফিগারেশন পরিবর্তন থেকে রক্ষা পাওয়া

স্ক্রিন রোটেট করার মতো কনফিগারেশন পরিবর্তন হলে, আপনার ফ্র্যাগমেন্ট বা অ্যাক্টিভিটি নষ্ট হয়ে যায় এবং আবার তৈরি হয়। আপনার ফ্র্যাগমেন্ট অ্যাক্টিভিটির জন্য ইনস্ট্যান্স স্টেটে সেভ না করা যেকোনও ডেটা, যা শুধুমাত্র অল্প পরিমাণ ডেটা ধরে রাখতে পারে, তা হারিয়ে যায়। এটি হলে, আপনাকে হয়ত নেটওয়ার্ক রিকোয়েস্ট আবার করতে হবে।

কনফিগারেশন পরিবর্তন করার পরেও আপনার ডেটা যাতে সুরক্ষিত থাকে তার জন্য আপনি ViewModel ব্যবহার করতে পারেন। ViewModel কম্পোনেন্টটি লাইফসাইকেল-সচেতন উপায়ে UI-সম্পর্কিত ডেটা স্টোর এবং ম্যানেজ করার জন্য ডিজাইন করা হয়েছে। আগের 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
            }

        }
    }
}

জাভা

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

এই বিষয়ে আরও জানতে, নিচে উল্লেখ করা সম্পর্কিত গাইড দেখুন: