| کانال توسعهدهندگان اندروید |
Open in Telegram
⭕️ کانال توسعهدهندگان اندروید دولوپیکس 💠 دولوپیکس | جامعه توسعهدهندگان ایرانی 💎 @Developix 🚀 Developix.ir 📌 پشتیبانی و تبلیغات: @DevelopixSupport
Show more1 724
Subscribers
-124 hours
-97 days
-730 days
Posts Archive
در کد زیر یک Memory Leak و استفادهی اشتباه از
Context در ViewModel وجود دارد.
چه چیزی باید اصلاح شود تا این Memory Leak رخ ندهد و کد مطابق با Best Practiceهای Android Architecture درست شود؟
class MainViewModel(private val context: Context) : ViewModel() {
private var dialog: AlertDialog? = null
fun showErrorDialog(message: String) {
val builder = AlertDialog.Builder(context)
builder.setTitle("Error")
builder.setMessage(message)
builder.setPositiveButton("OK", null)
dialog = builder.create()
dialog?.show()
}
override fun onCleared() {
super.onCleared()
dialog?.dismiss()
}
}
🔖 #Android #اندروید
👤 Developix
💎 Channel: @DevelopixAndroidRepost from کانال رسمی تک نت | Taknet
🔥 فروش ویژه سرشماره و خطوط رند سازمانی
سرشمارههای خاص و برندپذیر برای کسبوکارها و کالسنترها
✅مناسب تبلیغات
✅ برندینگ
✅ افزایش تماسهای ورودی
🎯 پیشنهاد محدود:
💥۵۰ سرشماره رند اول با ۲۰٪ تخفیف
برای دریافت لیست و خرید:
☎️ 02191555511
🔗 https://taknet.ir/services/internet-phone/
در Fragment اگه مستقیم از ViewBinding بهصورت lazy استفاده بشه، خیلی راحت دچار Memory Leak میشه و Fragment قدیمی توی حافظه میمونه. راه تمیز و امن اینه که binding رو فقط بین onCreateView و onDestroyView نگه داریم. 👇
ایده اصلی:
- یک متغیر nullable برای binding
- یک getter فقط برای استفاده داخل Fragment
- خالیکردن binding در onDestroyView
class ProfileFragment : Fragment(R.layout.fragment_profile) {
private var _binding: FragmentProfileBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentProfileBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.usernameText.text = "Android Dev"
binding.logoutButton.setOnClickListener {
// handle logout
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
چرا این الگو خوب کار میکند؟ ✅
- Lifecycle-safe: binding فقط وقتی معتبر است که view وجود دارد.
- بدون Memory Leak: در onDestroyView() همه reference های View آزاد میشوند.
- خوانا و ساده: بقیهی کد فقط با binding کار میکند، بدون nullable بازی.
این الگو روی Fragment های با Navigation Component، ViewPager2 و غیره هم بهخوبی جواب میدهد و مخصوصاً برای لیستها و صفحههای سنگین UI، جلوی نشت حافظه و کندشدن اپ را میگیرد. 🚀
برای جزئیات بیشتر درباره ViewBinding در Android:
Official ViewBinding Docs
امتحانش روی یکی از Fragmentهای پروژه، مخصوصاً جاهایی که UI سنگین و پیچیده است، نتیجهاش را خیلی سریع در مصرف حافظه و روانی ناوبری میشود دید. 😉
🔖 #Android #اندروید #Android #Kotlin #ViewBinding #Fragment #Memory_Leak #Best_Practices
👤 Developix
💎 Channel: @DevelopixAndroid📱👨💻
یک تکه کد Kotlin زیر را در نظر بگیرید که رفتار Coroutine ها و Dispatchers مختلف را روی Android نشان میدهد:
در این سناریو فرض کنید روی یک Thread اصلی Android (Main Looper) هستیم و این تابع در یک ViewModel صدا زده میشود. خروجی لاگها (ترتیب و متن هر خط) را در نظر بگیرید.
به دقت به نوع Dispatcher هر بلاک و نحوه suspend شدن و ادامه اجرای Coroutine دقت کنید.
سوال: خروجی نهایی (ترتیب دقیق خطوط لاگ) چه خواهد بود؟
پاسخ را به صورت متن لاگها (هر خط جداگانه) بنویسید.
🔖 #Android #اندروید
👤 Developix
💎 Channel: @DevelopixAndroid
🎯 استفاده از viewBinding برای کد تمیزتر و سریعتر در اندروید
استفاده از findViewById هم کند است، هم مستعد Crash به خاطر cast اشتباه یا id غلط. با viewBinding هم کدت تمیزتر میشود، هم ایمنتر و با کمتر شدن null و cast.
۱) فعال کردن viewBinding در Gradle ✅
در فایل
module/build.gradle:
android {
...
buildFeatures {
viewBinding true
}
}
۲) استفاده در Activity 🚀
اسم layout:
activity_main.xml
⟶ کلاس binding:
ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.myButton.setOnClickListener {
binding.myTextView.text = "Hello ViewBinding!"
}
}
}
۳) استفاده در Fragment (و جلوگیری از Memory Leak) 🧠
در Fragment حتماً روی lifecycle دقت کن:
class HomeFragment : Fragment() {
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.refreshButton.setOnClickListener {
binding.statusText.text = "Loaded"
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null // جلوگیری از Memory Leak
}
}
چرا این کار مفید است؟ 💡
• حذف cast اشتباه و Crashهای عجیب
• autocomplete قوی روی ویوها
• کد کمتر، خواناتر و آماده refactor
• در Fragment با null کردن binding از Memory Leak جلوگیری میشود
برای جزئیات بیشتر:
مستندات رسمی View Binding در Android Developers
امتحانش در یک Activity و یک Fragment کوچک هم زمان صرفهجویی میآورد، هم ساختار کدت را حرفهایتر میکند.
🔖 #Android #اندروید #Android #Kotlin #ViewBinding #Fragment #Memory_Leak #UI
👤 Developix
💎 Channel: @DevelopixAndroidدر لیستهای بزرگ RecyclerView اگر هر بار کل لیست رو بهروزرسانی کنی، UI لگ میزنه و اسکرول تیکهتیکه میشه. راه ساده و حرفهای برای حلش استفاده از ListAdapter و DiffUtil هست. 🔧
ایده اینه که فقط آیتمهایی که واقعاً تغییر کردن، آپدیت بشن؛ نه کل لیست. این کار هم performance رو بهتر میکنه، هم انیمیشنهای تمیزتری میگیری. 🚀
نمونهی حداقلی در Kotlin:
data class User(
val id: Long,
val name: String
)
class UserDiffCallback : DiffUtil.ItemCallback<User>() {
override fun areItemsTheSame(oldItem: User, newItem: User): Boolean =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: User, newItem: User): Boolean =
oldItem == newItem
}
class UserAdapter : ListAdapter<User, UserViewHolder>(UserDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_user, parent, false)
return UserViewHolder(view)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
// جایی در Fragment / Activity:
// userAdapter.submitList(newUserList)
نکات مهم ✅
• areItemsTheSame فقط تشخیص میده دو آیتم یکی هستن یا نه (معمولاً بر اساس id).
• areContentsTheSame چک میکنه محتوای آیتم عوض شده یا نه؛ اگر تغییری نباشه، RecyclerView اون آیتم رو دوباره رندر نمیکنه.
• همیشه از یک id پایدار (مثلاً primary key دیتابیس) برای تشخیص آیتمها استفاده بشه.
این ترکیب ListAdapter + DiffUtil معمولاً بدون هیچ کار اضافه، اسکرول رو روانتر و مصرف CPU رو کمتر میکنه، مخصوصاً وقتی دیتا از شبکه یا دیتابیس مرتب عوض میشه. 🌿
مستند رسمی:
Android Developers – ListAdapter
🔖 #Android #اندروید #Android #Kotlin #RecyclerView #Performance #DiffUtil #ListAdapter
👤 Developix
💎 Channel: @DevelopixAndroid👨💻 سوال برای Android Developers
در قطعهکد زیر که یک رفتار متداول در RecyclerView Adapter را شبیهسازی میکند، متغیرهای
a و b هر دو به یک MutableList اشاره میکنند. با توجه به ترتیب اعمال روی لیست و استفاده از subList، در انتهای اجرای main() مقدار نهایی متغیر b دقیقا چه خواهد بود؟
خروجی دقیق متغیر b را بنویسید (با در نظر گرفتن ترتیب عناصر).
🔖 #Android #اندروید
👤 Developix
💎 Channel: @DevelopixAndroidخروجی نهایی این کد Kotlin در Android (روی یک دستگاه با معماری ۶۴-بیتی) چیست؟
نکته: به تفاوت precision بین
Float و Double و همینطور نحوهی کار کردن equals برای این نوعها دقت کنید.
val f: Float = 0.1f + 0.2f
val d: Double = 0.1 + 0.2
val list = listOf(
"f=$f",
"d=$d",
"f==d? ${f.toDouble() == d}",
"fFloatEquals? ${f.equals(d.toFloat())}",
"dDoubleEquals? ${d.equals(f.toDouble())}"
)
println(list.joinToString(" | "))
خروجی println را دقیقا بنویسید.
🔖 #Android #اندروید
👤 Developix
💎 Channel: @DevelopixAndroid📱 سوال دیباگ اندروید (Kotlin)
در کد زیر یک Memory Leak جدی و یک Anti Pattern مربوط به نگهداشتن Context در Singleton وجود دارد که میتواند باعث کرش و مشکلات lifecycle در Activity شود.
چه چیزی باید در این کد اصلاح شود تا Memory Leak مربوط به Activity برطرف شود و مدیریت Context به شکل درستی انجام شود؟
🔖 #Android #اندروید
👤 Developix
💎 Channel: @DevelopixAndroid
🔥 تو این پست میریم سراغ یکی از پرکاربردترین چیزها تو هر اپ اندروید: ساخت لیست با RecyclerView و آپدیت هوشمند با DiffUtil.
سناریو رو اینطوری تصور کن: یه صفحه لیست محصولات / چت / تسکها داری که مدام آپدیت میشه. اگر هر بار
notifyDataSetChanged() بزنی، هم انیمیشنها میپرن، هم performance ضربه میخوره. اینجا ListAdapter + DiffUtil دقیقا میدرخشن ✨
این الگو دقیقا همون چیزیه که تو مستندات رسمی Android هم پیشنهاد شده:
android.com/topic/libraries/architecture/livedata#adapter
ایده کلی 👇
• استفاده از RecyclerView برای نمایش لیست
• استفاده از ListAdapter بهجای RecyclerView.Adapter معمولی
• تعریف یک DiffUtil.ItemCallback برای محاسبه تفاوت آیتمهای قدیمی و جدید
• هر وقت دیتا عوض شد، فقط submitList(newList) صدا میزنیم؛ بقیه کار رو DiffUtil با انیمیشن تمیز انجام میده ✅
یه نمونه ساده Kotlin برای لیست تسکها:
data class Task(
val id: Long,
val title: String,
val isDone: Boolean
)
class TaskAdapter :
ListAdapter<Task, TaskAdapter.TaskViewHolder>(DiffCallback) {
object DiffCallback : DiffUtil.ItemCallback<Task>() {
override fun areItemsTheSame(oldItem: Task, newItem: Task): Boolean =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Task, newItem: Task): Boolean =
oldItem == newItem
}
inner class TaskViewHolder(
private val binding: ItemTaskBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: Task) {
binding.txtTitle.text = item.title
binding.chkDone.isChecked = item.isDone
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TaskViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ItemTaskBinding.inflate(inflater, parent, false)
return TaskViewHolder(binding)
}
override fun onBindViewHolder(holder: TaskViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
نکات ریز ولی مهم 💡
• همیشه برای areItemsTheSame از یک id پایدار (مثلا از دیتابیس Room) استفاده کن تا DiffUtil بفهمه این همون آیتم قبلیه.
• توی ViewModel (یا جایی که دیتا رو میسازی) فقط یه submitList(newList) صدا بزن؛ دیگه لازم نیست notifyItem... دستی صدا بزنی.
• این الگو با LiveData و Flow خیلی خوب جور میشه؛ هر بار دیتا عوض شد، لیستت با انیمیشن نرم آپدیت میشه، بدون Memory Leak و بدون پیچیدگی.
این رو روی یه لیست ساده تو پروژهات امتحان کن؛ چند تا آیتم اضافه و حذف کن و ببین چقدر تمیز و حرفهای انیمیت میشه ✨
اگر برات مفید بود، تو پروژههات استفاده کن و کدت رو یک level حرفهایتر کن ⚙️
🔖 #Android #اندروید #Android #Kotlin #RecyclerView #DiffUtil #ListAdapter #Jetpack
👤 Developix
💎 Channel: @DevelopixAndroidاگه تا حالا برای ساخت صفحه Chat یا لیست محصولات از RecyclerView استفاده کردی و بعد از تغییر دادهها کل لیست ریلود شده و اسکرول کاربر پرید بالا، این پست دقیقا به درد همونجا میخوره 👇
امروز بر اساس راهنمای رسمی Android Developers درباره RecyclerView و DiffUtil یه مدل تمیز و کاربردی میسازیم که تو هر اپ واقعی (Chat، Feed، لیست سفارشها) میشه استفادهاش کرد.
ایده چیه؟
بهجای اینکه هر بار کل لیست رو
notifyDataSetChanged() کنیم، از ListAdapter + DiffUtil.ItemCallback استفاده میکنیم تا فقط آیتمهایی که واقعا تغییر کردن، آپدیت بشن. نتیجه:
• اسکرول نرمتر ✅
• انیمیشنهای شیک روی اضافه/حذف آیتم ✅
• مصرف کمتر CPU و باتری ✅
مثال ساده: لیست Message ها در یک صفحه Chat
data class Message(
val id: Long,
val text: String,
val timeMillis: Long
)
class MessageDiffCallback : DiffUtil.ItemCallback<Message>() {
override fun areItemsTheSame(oldItem: Message, newItem: Message): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Message, newItem: Message): Boolean {
return oldItem == newItem
}
}
class MessageAdapter : ListAdapter<Message, MessageAdapter.ViewHolder>(MessageDiffCallback()) {
class ViewHolder(val binding: ItemMessageBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ItemMessageBinding.inflate(inflater, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
holder.binding.messageText.text = item.text
}
}
حالا تو Fragment یا Activity فقط کافیه:
• یه بار adapter رو روی RecyclerView ست کنی
• هر بار داده جدید اومد (مثلا از Firebase Realtime Database، Firestore یا Room)، لیست جدید رو بده به submitList()
مثال:
class ChatFragment : Fragment() {
private val adapter = MessageAdapter()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recyclerView.adapter = adapter
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
viewModel.messagesFlow.collect { messages ->
adapter.submitList(messages)
}
}
}
}
نکات ریز ولی مهم 🧠
• حتما برای هر Message یک id یونیک نگهدار؛ DiffUtil روی همین تکیه میکنه.
• سعی کن لیست جدید رو کپی بسازی، نه اینکه همون لیست قبلی رو mutate کنی (مثلا از toList() استفاده کن).
• تو پروژههای واقعی، ترکیب این الگو با Flow/LiveData و Room یا Firestore ساختار خیلی تمیز و قابل تستی میده.
این Pattern تو هر صفحهای که لیست دیتا داره جواب میده؛ از Chat و Notification تا Shopping Cart. یه بار درست پیادهاش کنی، بعدش تو هر پروژه فقط Copy/Paste و Customize ✨
امتحانش کن روی یکی از لیستهای قدیمی پروژهات و تفاوت اسکرول و انیمیشنها رو ببین؛ اگه مفید بود برای بقیه اندرویدیها هم بفرست 🙌
🔖 #Android #اندروید #Android #Kotlin #RecyclerView #DiffUtil #ListAdapter #Chat #Jetpack #Room #Firebase
👤 Developix
💎 Channel: @DevelopixAndroid🎯 استفاده درست از viewBinding در Fragment (بدون Memory Leak)
یکی از رایجترین Memory Leak ها توی Android وقتی اتفاق میافته که توی Fragment از viewBinding استفاده میشه، ولی رفرنس به View بعد از onDestroyView() هنوز نگه داشته میشه. نتیجه؟ مصرف بیخود حافظه و رفتارهای عجیب توی UI. 😅
الگوی پیشنهادی Google اینه که binding فقط بین onCreateView() و onDestroyView() معتبر باشه و خارج از این محدوده اصلاً بهش دسترسی نداشته باشیم.
✅ پیادهسازی امن viewBinding در Fragment:
class SampleFragment : Fragment(R.layout.fragment_sample) {
private var _binding: FragmentSampleBinding? = null
private val binding get() = _binding!!
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentSampleBinding.bind(view)
// استفاده امن از binding اینجاست
binding.textViewTitle.text = "Hello Android Devs!"
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null // جلوگیری از Memory Leak
}
}
نکتههای مهم 👇
• رفرنس binding رو به هیچ وجه توی property های طولانیعمر (مثل singleton ها، object ها، ViewModel، adapter های global) نگه ندار.
• از binding فقط بعد از onViewCreated() و قبل از onDestroyView() استفاده کن. بیرون از این بازه به logic خالص (ViewModel, UseCase, Repository) تکیه کن.
• اگر از Fragment(R.layout...) استفاده میشه، نیازی به inflate دستی نیست؛ فقط bind(view) کفایت میکنه.
این الگو سادهست، ولی توی پروژههای بزرگ جلوی کلی Memory Leak ریز و درشت رو میگیره و Debug کردن رو خیلی راحتتر میکنه. 🚀
برای جزئیات بیشتر:
مستندات رسمی View Binding در Fragment ها
امتحانش روی یکی از Fragment های فعلی پروژه، معمولاً سریعترین راهه برای دیدن تاثیرش روی کد تمیزتر و Crash کمتر. 😉
🔖 #Android #اندروید #Android #Kotlin #Fragment #ViewBinding #Memory_Leak #Best_Practices
👤 Developix
💎 Channel: @DevelopixAndroidMVVM در اندروید؛ یک قدم ساده برای کد تمیزتر 🧹
یکی از کاربردیترین راهها برای تمیز و قابلنگهداری شدن کد در اندروید، استفادهٔ درست از MVVM است. ایدهٔ اصلی خیلی ساده است:
View (Activity / Fragment / Compose) فقط نمایش میدهد 👀
ViewModel منطق UI و وضعیت صفحه را نگه میدارد 🧠
Repository / UseCase با دیتا کار میکنند (Network, DB و …) 🗄
وقتی این جداسازی را رعایت کنیم:
• تستنویسی آسانتر میشود ✅
• تغییر UI بدون دستزدن به منطق ممکن میشود ✅
• کدها خواناتر و قابلتغییرتر میشوند ✅
یک نمونهٔ ساده با ViewModel و StateFlow
در این مثال، Activity هیچ منطق تجاریای ندارد؛ فقط state را observe میکند و نمایش میدهد.
data class UiState(
val isLoading: Boolean = false,
val userName: String? = null,
val error: String? = null
)
class ProfileViewModel(
private val repo: UserRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState
fun loadProfile() {
viewModelScope.launch {
_uiState.value = UiState(isLoading = true)
runCatching { repo.getUser() }
.onSuccess { user ->
_uiState.value = UiState(userName = user.name)
}
.onFailure { e ->
_uiState.value = UiState(error = e.message)
}
}
}
}
در UI (مثلاً Activity یا Compose)، فقط state را مصرف میکنیم:
class ProfileActivity : AppCompatActivity() {
private val viewModel: ProfileViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profile)
lifecycleScope.launchWhenStarted {
viewModel.uiState.collect { state ->
progressBar.isVisible = state.isLoading
nameTextView.text = state.userName.orEmpty()
errorTextView.text = state.error.orEmpty()
}
}
viewModel.loadProfile()
}
}
🔍 نکتهٔ مهم Clean Code اینجاست:
• هیچ call مستقیم به repo داخل Activity نداریم.
• هیچ CoroutineScope جداگانه داخل Activity ساخته نشده؛ همهچیز در viewModelScope است.
• وضعیت صفحه در یک UiState واحد مدیریت میشود، نه چندین متغیر پراکنده.
این سبک دقیقاً در راستای توصیههای Android Architecture Guidelines گوگل و ایدههای Clean Architecture است.
💡 از پروژهٔ بعدی، فقط همین یک قدم را جدی نگه داشتن MVVM بدون منطق در View باعث میشود اپ تمیزتر، تستپذیرتر و آمادهٔ رشد شود.
🔖 #Android #اندروید #Android #Kotlin #MVVM #Clean_Code #Architecture #Jetpack #StateFlow
👤 Developix
💎 Channel: @DevelopixAndroid+1
در نمونهکد بالا برای جمعآوری داده از چند منبع در ViewModel، بهجای استفاده از GlobalScope.launch و async/await داخل
onCleared() (که میتواند باعث Memory Leak، کرش و رفتار غیرقابل پیشبینی شود)، میتوانید از viewModelScope و combine در Flow استفاده کنید تا هم چرخهعمر (Lifecycle) بهدرستی مدیریت شود و هم کارایی و خوانایی کد در شرایط بارگذاری همزمان چند منبع داده افزایش پیدا کند.
🔖 #Android #اندروید
👤 Developix
💎 Channel: @DevelopixAndroidRetrofit از Square: کتابخانهای ساده و قدرتمند برای ارتباط با API در اپهای اندروید. حلِ چه چیزی؟ کم کردن boilerplateِ درخواست HTTP، تبدیل خودکار JSON به مدلها و پشتیبانی مستقیم از coroutines برای کد تمیز و همزمانی بهتر. 🚀
ویژگیها و مزایا:
- 🔹 تبدیلکنندههای قابلِ اضافه (Gson/Moshi)
- 🔹 ادغامِ روان با OkHttp، زمانبندی و کش
- 🔹 پشتیبانی از suspend برای خوانایی و مدیریت خطا
- 🔹 تستپذیری بالا و قرارداد محور
نمونه استفاده (Kotlin):
data class User(val id: Int, val name: String)
interface ApiService {
@GET("users/{id}")
suspend fun getUser(@Path("id") id: Int): User
}
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(OkHttpClient())
.build()
val api = retrofit.create(ApiService::class.java)
val user = api.getUser(1)
موارد عملی: استفاده در رپوزیتوری برای MVVM، پیادهسازی paging، یا ترکیب با WorkManager برای همگامسازی پسزمینه. یک نکته کوچک: زمانبندی و retry را در OkHttp اعمال کنید تا از درخواستهای اضافی جلوگیری شود. 🔧
اسناد رسمی و مخزن:
Docs • GitHub
امتحان کنید و تجربهتان را به اشتراک بگذارید — میتواند کد شبکهتان را خیلی مرتبتر کند! ✨
🔖 #Android #اندروید #Retrofit #Android #Networking #Kotlin
👤 Developix
💎 Channel: @DevelopixAndroidکوچکسازی ViewModel با Use Case — رعایت SRP در Clean Architecture
وقتی ViewModel همه منطق کسبوکار، دسترسی به داده و تبدیل مقادیر را در خود جای میدهد، سریعاً تبدیل به یک کلاس بزرگ و سختتست میشود. یک راه ساده، استخراج منطق به لایهٔ Use Case (یا Interactor) است تا Single Responsibility Principle (SRP) را رعایت کنیم و تستپذیری و خوانایی را بالا ببریم.
نکات عملی:
- ViewModel فقط مسئول مدیریت وضعیت UI و فراخوانی Use Caseها باشد.
- Use Caseها منطق کسبوکار را اجرا کنند و به repository وابسته باشند.
- این جداسازی باعث میشود تست واحد (unit test) روی Use Caseها و ViewModel بهصورت مجزا آسان شود.
مثال کوچک با Hilt، Coroutines و StateFlow:
interface UserRepository {
suspend fun getUser(id: String): User
}
class GetUserUseCase(private val repo: UserRepository) {
suspend operator fun invoke(id: String): User = repo.getUser(id)
}
@HiltViewModel
class UserViewModel @Inject constructor(
private val getUserUseCase: GetUserUseCase
) : ViewModel() {
private val _uiState = MutableStateFlow<User?>(null)
val uiState: StateFlow<User?> = _uiState
fun load(id: String) {
viewModelScope.launch {
val user = getUserUseCase(id)
_uiState.value = user
}
}
}
چرا این بهتر است؟
- تست: میتوان GetUserUseCase را با یک Fake Repository و ViewModel را با یک Fake UseCase تست کرد.
- تغییرپذیری: اگر منبع داده عوض شود، فقط Repository یا Use Case تغییر میکند، نه ViewModel.
- قابل فهمتر: هر کلاس وظیفهٔ مشخصی دارد.
برای مرجعی کاربردی به مستندات Jetpack نگاه کنید: Android Jetpack Guide
این الگو کوچک و کاربردی است اما تأثیر زیادی در نگهداری و مقیاسپذیری اپلیکیشنهای اندروید دارد. رعایت SRP و تفکیک لایهها، بهویژه در اپهای بزرگ، تفاوت چشمگیری ایجاد میکند.
🔖 #Android #اندروید #Android #CleanArchitecture #MVVM #UseCase #Hilt #Kotlin #StateFlow #SOLID
👤 Developix
💎 Channel: @DevelopixAndroid💢 کتابخانه ZXing
امروزه استفاده از بارکدها برای به دست آوردن اطلاعات محصول و سایر مشخصهها و ... خیلی رایج شده است، برای ایجاد بارکدخوان در اندروید میتوان از کتابخانه ZXing استفاده کرد.
کتابخانه ZXing یک کتابخانه بارکدخوان (Barcode Scanner) برای جاوا و اندروید میباشد که میتواند بارکدهای نوع 1D/2D را پردازش کند.
برای استفاده از این کتابخانه باید ورژن AppCompat بالای 23 و ورژن BuildTools بالای 27.0.3 باشد، در غیر این صورت ممکن است با خطای کامپایل مواجه شوید و همچنین داخل فایل Manifest نیز برای عملکرد بهتر باید ویژگی HardwareAccelerated را در بخش Application بر روی True تنظیم کنید.
🔖 #Android, #اندروید
👤 Maryam
💎 Channel: @DevelopixAndroid
✨فریلنسر ها دو دسته هستن :
1️⃣ یا با فریلنسری بین المللی آشنایی ندارن
2️⃣ یا از شرایطی که بخاطر تحریم به وجود اومده ناامید شدن و سمتش نمیرن؛
⚠️ شرایطی مثل نحوه ساخت اکانت در پلتفرم ها ، جذب پروژه ، تکمیل پروفایل و احراز هویت ، داشتن حساب خارجی و نقد کردن درآمد
✅ خبر خوب این هست که جزو هرکدوم ازین دسته ها باشی ، میتونی با خیال راحت وارد بازار کار بین المللی بشی ، پروژه بگیری و به راحتی درآمدت رو نقد کنی✨
⁉️ چجوریشو از کسی که تجربه چندین ساله ی فریلنسری داره بشنو
در یک جلسه یک ساعته رایگان
📆زمان: روز یکشنبه ساعت ۱۹
🌐لینک ثبت نام: https://links.etekanesh.com/frl3tidvpx
🆔 کانال افراد موفق:
https://t.me/TekaneshAcademy
پشتیبانی در صورت بروز مشکل در ورود به جلسه:
@Academy_Tekanesh
دانلود اندروید استودیو نسخه Koala | 2024.1.1 RC | اندروید استودیو نسخه کوآلا
Mac Windows ChromeOS Linuxلینک دسترسی به ورژن های مختلف بدون مشکل فیلترینگ و تحریم ، کلیک کنید توضیحات : این نسخه، آخرین نسخه پایدار است اما هنوز در سایت رسمی اندروید در صفحه اولیه دانلود موجود نیس. 🔖 #Android, #اندروید, #AndroidStudio 👤 Abolfazl Radmanesh 💎 Channel: @DevelopixAndroid
Repost from | کانال توسعهدهندگان وب |
پلتفرم Rive انیمیشنهایی دراختیارتون قرار میده که توی تمام پلتفرمها قابلیت استفادهش رو دارید و خاصیتش هم این هست که قابلیت کنترل کردن انیمیشنها بهتون داده میشه.
برای همین میشه برای ساخت صفحات پویاتر، بازی یا هرچیزی ازش استفاده کرد.
اپ Duolingo هم بهنظر میاد از همین پلفترم برای کنترل شخصیتها و المانهای داخل برنامهاش استفاده میکنه.
🔗 Rive.app
بعضی از موردهای استفادهاش رو هم میتونید توی این لینک ببینید.
🔖 #UI #Animation #Crossplatform
👤 Mahdiyar
💎 Channel: @DevelopixWeb
