আপনার অ্যাপ্লিকেশনে নেটওয়ার্ক অপারেশন সম্পাদন করার জন্য, আপনার ম্যানিফেস্টে নিম্নলিখিত অনুমতিগুলি অবশ্যই অন্তর্ভুক্ত থাকতে হবে:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
নিরাপদ নেটওয়ার্ক যোগাযোগের জন্য সর্বোত্তম অনুশীলন
আপনার অ্যাপে নেটওয়ার্কিং কার্যকারিতা যোগ করার আগে, আপনাকে নিশ্চিত করতে হবে যে নেটওয়ার্কের মাধ্যমে ডেটা ও তথ্য প্রেরণের সময় তা সুরক্ষিত থাকে। এটি করার জন্য, নেটওয়ার্কিং সুরক্ষার এই সর্বোত্তম অনুশীলনগুলো অনুসরণ করুন:
- নেটওয়ার্কের মাধ্যমে প্রেরিত সংবেদনশীল বা ব্যক্তিগত ব্যবহারকারীর তথ্যের পরিমাণ ন্যূনতম রাখুন।
- আপনার অ্যাপ থেকে সমস্ত নেটওয়ার্ক ট্র্যাফিক SSL-এর মাধ্যমে পাঠান।
- একটি নেটওয়ার্ক নিরাপত্তা কনফিগারেশন তৈরি করার কথা বিবেচনা করুন, যা আপনার অ্যাপকে কাস্টম সার্টিফিকেট অথরিটি (CA)-কে বিশ্বাস করতে অথবা নিরাপদ যোগাযোগের জন্য এটি যে সিস্টেম CA-গুলোকে বিশ্বাস করে, তার সেটকে সীমাবদ্ধ করতে দেয়।
নিরাপদ নেটওয়ার্কিং নীতিগুলি কীভাবে প্রয়োগ করতে হয় সে সম্পর্কে আরও তথ্যের জন্য, নেটওয়ার্কিং নিরাপত্তা টিপস দেখুন।
একটি HTTP ক্লায়েন্ট বেছে নিন
অধিকাংশ নেটওয়ার্ক-সংযুক্ত অ্যাপ ডেটা আদান-প্রদানের জন্য HTTP ব্যবহার করে। অ্যান্ড্রয়েড প্ল্যাটফর্মে HttpsURLConnection ক্লায়েন্ট রয়েছে, যা TLS, স্ট্রিমিং আপলোড ও ডাউনলোড, কনফিগারযোগ্য টাইমআউট, IPv6 এবং কানেকশন পুলিং সমর্থন করে।
নেটওয়ার্কিং অপারেশনের জন্য উচ্চ-স্তরের এপিআই প্রদানকারী থার্ড-পার্টি লাইব্রেরিও পাওয়া যায়। এগুলি বিভিন্ন সুবিধাজনক বৈশিষ্ট্য সমর্থন করে, যেমন রিকোয়েস্ট বডির সিরিয়ালাইজেশন এবং রেসপন্স বডির ডিসিরিয়ালাইজেশন।
- রেট্রোফিট : স্কয়ারের তৈরি JVM-এর জন্য একটি টাইপ-সেফ HTTP ক্লায়েন্ট, যা OkHttp-এর উপর ভিত্তি করে নির্মিত। রেট্রোফিট আপনাকে ডিক্লারেটিভভাবে একটি ক্লায়েন্ট ইন্টারফেস তৈরি করতে দেয় এবং এতে বেশ কয়েকটি সিরিয়ালাইজেশন লাইব্রেরির জন্য সমর্থন রয়েছে।
- Ktor : JetBrains-এর একটি HTTP ক্লায়েন্ট, যা সম্পূর্ণরূপে Kotlin-এর জন্য নির্মিত এবং কো-রুটিন দ্বারা চালিত। Ktor বিভিন্ন ইঞ্জিন, সিরিয়ালাইজার এবং প্ল্যাটফর্ম সমর্থন করে।
ডিএনএস কোয়েরি সমাধান করুন
যেসব ডিভাইসে অ্যান্ড্রয়েড ১০ (এপিআই লেভেল ২৯) এবং তার উচ্চতর সংস্করণ চলে, সেগুলোতে ক্লিয়ারটেক্সট লুকআপ এবং ডিএনএস-ওভার-টিএলএস মোড—উভয়ের মাধ্যমেই বিশেষায়িত ডিএনএস লুকআপের জন্য বিল্ট-ইন সাপোর্ট রয়েছে। DnsResolver এপিআই জেনেরিক, অ্যাসিঙ্ক্রোনাস রেজোলিউশন প্রদান করে, যা আপনাকে SRV , NAPTR এবং অন্যান্য রেকর্ড টাইপ লুকআপ করতে দেয়। রেসপন্সটি পার্স করার দায়িত্ব অ্যাপের ওপর ছেড়ে দেওয়া হয়।
অ্যান্ড্রয়েড ৯ (এপিআই লেভেল ২৮) এবং এর নিচের সংস্করণে চালিত ডিভাইসগুলিতে, প্ল্যাটফর্ম ডিএনএস রিজলভার শুধুমাত্র A এবং AAAA রেকর্ড সমর্থন করে। এর মাধ্যমে আপনি একটি নামের সাথে যুক্ত আইপি অ্যাড্রেসগুলি খুঁজে বের করতে পারেন, কিন্তু অন্য কোনো ধরনের রেকর্ড সমর্থন করে না।
NDK-ভিত্তিক অ্যাপের জন্য, android_res_nsend দেখুন।
একটি রিপোজিটরি দিয়ে নেটওয়ার্ক অপারেশনগুলিকে আবদ্ধ করুন
নেটওয়ার্ক অপারেশন সম্পাদনের প্রক্রিয়াকে সহজ করতে এবং আপনার অ্যাপের বিভিন্ন অংশে কোডের পুনরাবৃত্তি কমাতে, আপনি রিপোজিটরি ডিজাইন প্যাটার্ন ব্যবহার করতে পারেন। রিপোজিটরি হলো এমন একটি ক্লাস যা ডেটা অপারেশন পরিচালনা করে এবং কোনো নির্দিষ্ট ডেটা বা রিসোর্সের উপর একটি পরিচ্ছন্ন এপিআই অ্যাবস্ট্রাকশন প্রদান করে।
আপনি Retrofit ব্যবহার করে একটি ইন্টারফেস ডিক্লেয়ার করতে পারেন যা নেটওয়ার্ক অপারেশনের জন্য HTTP মেথড, URL, আর্গুমেন্ট এবং রেসপন্স টাইপ নির্দিষ্ট করে, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
কোটলিন
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); }
একটি রিপোজিটরি ক্লাসের মধ্যে, ফাংশনগুলো নেটওয়ার্ক অপারেশনগুলোকে এনক্যাপসুলেট করতে পারে এবং সেগুলোর ফলাফল প্রকাশ করতে পারে। এই এনক্যাপসুলেশন নিশ্চিত করে যে, যে কম্পোনেন্টগুলো রিপোজিটরিকে কল করে, ডেটা কীভাবে সংরক্ষিত আছে তা তাদের জানার প্রয়োজন নেই। ডেটা সংরক্ষণের পদ্ধতিতে ভবিষ্যতে যেকোনো পরিবর্তনও রিপোজিটরি ক্লাসের মধ্যেই সীমাবদ্ধ থাকে। উদাহরণস্বরূপ, আপনার এপিআই এন্ডপয়েন্ট আপডেটের মতো কোনো রিমোট পরিবর্তন থাকতে পারে, অথবা আপনি লোকাল ক্যাশিং প্রয়োগ করতে চাইতে পারেন।
কোটলিন
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 যাতে সাড়া না দেয়, তা এড়াতে মেইন থ্রেডে নেটওয়ার্ক অপারেশন করবেন না। ডিফল্টভাবে, অ্যান্ড্রয়েড চায় যে আপনি মেইন UI থ্রেড ছাড়া অন্য কোনো থ্রেডে নেটওয়ার্ক অপারেশনগুলো সম্পাদন করুন। আপনি যদি মেইন থ্রেডে নেটওয়ার্ক অপারেশন করার চেষ্টা করেন, তাহলে একটি NetworkOnMainThreadException থ্রো করা হয়।
পূর্ববর্তী কোড উদাহরণে, নেটওয়ার্ক অপারেশনটি আসলে ট্রিগার করা হয়নি। UserRepository এর কলারকে অবশ্যই কোরাউটিন অথবা enqueue() ফাংশন ব্যবহার করে থ্রেডিং ইমপ্লিমেন্ট করতে হবে। আরও তথ্যের জন্য, "Get data from the internet " কোডল্যাবটি দেখুন, যেখানে কোটলিন কোরাউটিন ব্যবহার করে কীভাবে থ্রেডিং ইমপ্লিমেন্ট করতে হয় তা দেখানো হয়েছে।
কনফিগারেশন পরিবর্তন থেকে টিকে থাকুন
যখন স্ক্রিন ঘোরানোর মতো কোনো কনফিগারেশন পরিবর্তন ঘটে, তখন আপনার ফ্র্যাগমেন্ট বা অ্যাক্টিভিটি ধ্বংস হয়ে যায় এবং পুনরায় তৈরি হয়। আপনার ফ্র্যাগমেন্ট অ্যাক্টিভিটির ইনস্ট্যান্স স্টেটে যে ডেটা সেভ করা থাকে না, তা হারিয়ে যায়; কারণ এটি কেবল অল্প পরিমাণ ডেটা ধারণ করতে পারে। এমনটা ঘটলে, আপনাকে হয়তো আপনার নেটওয়ার্ক রিকোয়েস্টগুলো আবার করতে হতে পারে।
কনফিগারেশন পরিবর্তনের পরেও আপনার ডেটাকে সুরক্ষিত রাখতে আপনি একটি ViewModel ব্যবহার করতে পারেন। ViewModel কম্পোনেন্টটি UI-সম্পর্কিত ডেটাকে তার জীবনচক্রের কথা মাথায় রেখে সংরক্ষণ ও পরিচালনা করার জন্য ডিজাইন করা হয়েছে। পূর্ববর্তী UserRepository ব্যবহার করে, ViewModel প্রয়োজনীয় নেটওয়ার্ক রিকোয়েস্ট পাঠাতে পারে এবং LiveData ব্যবহার করে আপনার fragment বা activity-তে তার ফলাফল সরবরাহ করতে পারে।
কোটলিন
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 } }); } }
সম্পর্কিত নির্দেশিকাগুলো পড়ুন
এই বিষয়ে আরও জানতে, নিম্নলিখিত সম্পর্কিত নির্দেশিকাগুলো দেখুন:
- নেটওয়ার্কের ব্যাটারির ক্ষয় কমান: সংক্ষিপ্ত বিবরণ
- নিয়মিত আপডেটের প্রভাব হ্রাস করুন
- ওয়েব-ভিত্তিক বিষয়বস্তু
- প্রয়োগের মৌলিক বিষয়গুলি
- অ্যাপ আর্কিটেকচারের নির্দেশিকা