ProGuard va R8 — Android ilovalari uchun obfuskatsiya, minifikatsiya va optimallashtirish vositalari. 2002-yilda yaratilgan ProGuard uzoq vaqt davomida Java kodini himoya qilish uchun de-fakto standart bo'lgan. R8 — uning vorisi, Google tomonidan ishlab chiqilgan va AGP 3.4 dan boshlab Android Gradle Plugin-ga o'rnatilgan. Ikkala vosita APK hajmini kamaytiradi, o'lik kodni o'chiradi va reverse engineering-ni qiyinlashtiradi. Android Developers ma'lumotlariga ko'ra, R8 taqqoslanadigan obfuskatsiya sifatida ProGuard-dan 2–3 marta tezroq qurishni bajaradi.
Asosiy ma'lumotlar
ProGuard — Java bayt kodini obfuskatsiya, minifikatsiya, optimallashtirish va preverifikatsiya qilish uchun ochiq manbali vosita (Apache 2.0). Erik Lafourj tomonidan 2002-yilda SourceForge loyihasi doirasida ishlab chiqilgan. ProGuard kirishda kompilyatsiya qilingan Java sinflarini (.class) yoki JAR arxivlarini qabul qiladi va bir xil formatdagi, lekin kichikroq hajmli va nomlari o'zgartirilgan elementlar bilan qayta ishlangan sinflarni chiqaradi.
Uzoq vaqt davomida ProGuard Android ilovalarini reverse engineering-dan himoya qilishning yagona standarti edi. Google uni Android SDK da foydalanishni rasman tavsiya qilgan va SDK tools ichida proguard-android-optimize.txt faylida standart konfiguratsiyani yetkazib bergan. ProGuard Java kodi bayt kodiga kompilyatsiya qilingandan so'ng va DEX-ga paketlashdan oldin ishga tushiriladigan alohida vosita sifatida ishlagan.
ProGuard to'rtta ketma-ket fazadan iborat: shrink (foydalanilmayotgan sinflarni o'chirish), optimize (bayt kodini optimallashtirish — inlining, o'lik kodni o'chirish), obfuscate (sinf, metod va maydon nomlarini qisqa nomlarga o'zgartirish), preverify (JVM bilan muvofiqligini tekshirish). Har bir faza konfiguratsiya fayllaridan alohida qoidalar bilan boshqariladi.
Obfuskatsiya bosqichida ProGuard asl nomlarni obfuska qilingan nomlarga moslashtiruvchi mapping faylini (mapping.txt) yaratadi. Bu fayl retrace vositasi orqali release qurilmalaridan crash loglarni dekodlash uchun juda muhimdir. Mapping faylisiz stack trace a(), b(), c() harflar to'plamiga aylanadi va asl kontekstni tiklash imkonsiz bo'ladi.
| ProGuard fazasi | Maqsad | Natija |
|---|---|---|
| Shrink | Chaqiruv grafigini tahlil qilish va o'lik kodni o'chirish | APK-dagi sinflar sonini kamaytirish |
| Optimize | Metodlarni inlining qilish, foydalanilmayotgan parametrlarni o'chirish | Kod bajarilishini tezlashtirish |
| Obfuscate | Sinf, maydon va metod nomlarini o'zgartirish | Reverse engineering-dan himoya |
| Preverify | JVM uchun StackMap atributlarini qo'shish | Java 6+ bilan muvofiqlik |
R8 — Google-dan keyingi avlod obfuskatsiya va minifikatsiya vositasi, birinchi marta Android Studio 3.3 (noyabr 2018) da taqdim etilgan va AGP 3.4 (avgust 2019) da standart bo'lgan. ProGuard-dan farqli o'laroq, R8 Java bayt kodini DEX formatiga o'zgartiruvchi D8/R8 kompilyatorining bir qismidir. R8 barcha fazalarni — obfuskatsiya, minifikatsiya va optimallashtirishni — bir o'tishda, vositalar o'rtasida oraliq fayllarni uzatmasdan bajaradi.
Google R8 ni ikki maqsad bilan ishlab chiqqan: qurishni tezlashtirish (ProGuard tashqi vosita sifatida ishlagan) va zamonaviy Android stack (Desugar, Core Library Desugaring, D8) bilan uzluksiz integratsiyani ta'minlash. R8 Kotlin va Java tillarida yozilgan va AOSP (Android Open Source Project) dagi R8/Desugar repozitoriyasining bir qismidir.
R8 ning muhim afzalligi — ProGuard rules bilan to'liq orqaga qarab muvofiqlik. Mavjud .pro fayllari o'zgarishsiz ishlaydi. R8 hatto ProGuard-ga xos -whyareyoukeeping, -printconfiguration va -printmapping direktivalarini qo'llab-quvvatlaydi. Bu ProGuard-dan R8 ga o'tish shaffof tarzda amalga oshishini anglatadi: AGP ni yangilash kifoya.
// build.gradle.kts — R8 ni minifyEnabled orqali yoqish
android {
buildTypes {
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
// Android SDK dan asosiy konfiguratsiya
getDefaultProguardFile("proguard-android-optimize.txt"),
// Loyihaning foydalanuvchi qoidalari
"proguard-rules.pro"
)
}
}
}Kod standart release qurish konfiguratsiyasini namoyish etadi. isMinifyEnabled = true bayrog'i obfuskatsiya va optimallashtirish uchun R8 ni faollashtiradi. isShrinkResources = true qo'shimcha ravishda foydalanilmayotgan resurslarni o'chiradi. getDefaultProguardFile SDK dan asosiy qoidalarni yuklaydi, proguard-rules.pro esa loyihaga xususiy sozlamalarni o'z ichiga oladi.
Obfuskatsiya — manba kodini inson tahlil qilishi qiyin, ammo to'liq funksionallikni saqlaydigan shaklga o'zgartirish jarayoni. Android kontekstida obfuskatsiya sinflar, metodlar va maydonlar nomlarini qisqa, ma'nosiz nomlarga almashtirishni anglatadi: com.example.app.auth.LoginManager a.a.a ga, authenticateUser metodi a ga, userToken maydoni esa b ga aylanadi.
Android APK fayllari har qanday arxivlovchi (ZIP, 7z, WinRAR) bilan ochiladigan arxivlardir. Obfuskatsiyasiz tajovuzkor ilovaning to'liq xaritasini oladi: paket, sinf, metod va maydon nomlari. jadx yoki Bytecode Viewer kabi vositalar DEX fayllaridan deyarli asl Java kodini bir necha soniyada tiklaydi. Obfuskatsiya kodni zaif qilmaydi, lekin kirish chegarasini sezilarli darajada oshiradi: mazmunli nomlar o'rniga o'quvchi a(), b(), c() ni ko'radi.
Obfuskatsiyaning odatiy maqsadlari: tijorat mantiqini himoya qilish (algoritmlar, hisoblash formulalari), API kalitlari va tokenlarni o'g'irlashni qiyinlashtirish, reflection orqali sinflarni almashtirishning oldini olish, APK ni patching va modifikatsiyadan himoya qilish (repackage attack). Amalda vazifalarning 70% aynan nomlarni o'zgartirish bilan hal qilinadi — shuning uchun ProGuard / R8 ishga tushiriladi.
Quyida Retrofit, Gson va Parcelable bilan Android loyihasi uchun odatiy proguard-rules.pro fayli keltirilgan. -keep qoidalari reflection orqali kutubxonalarning ishlashi uchun zarur bo'lgan sinf va metodlarni saqlaydi. Bu qoidalarsiz R8 kutubxona satr nomi bilan murojaat qiladigan sinflarni o'chiradi yoki nomini o'zgartiradi.
# =====================
# Retrofit — interfeyslarni saqlash
# =====================
-keep,allowobfuscation,allowshrinking interface retrofit2.** { *; }
-keepattributes Signature, Exceptions
# =====================
# Gson — JSON seriyalashtirish
# =====================
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}
-keep class *.serialization.** {
<fields>;
}
# =====================
# Parcelable — Creator
# =====================
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
# =====================
# Logging — release dan loglarni o'chirish
# =====================
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(String, int);
public static int v(...);
public static int d(...);
public static int i(...);
public static int w(...);
public static int e(...);
}
# =====================
# Kotlin data sinflari — konstruktorlarni saqlash
# =====================
-keepclassmembers class * {
@kotlin.Metadata <fields>;
}
# =====================
# Activity — kirish nuqtasi
# =====================
-keep class * extends android.app.Activity {
@android.annotation.SuppressLint <methods>;
}.pro faylidagi har bir direktiv aniq vazifani hal qiladi. -keep sinfni butunlay o'chirish yoki nomini o'zgartirishni oldini oladi. -keepclassmembers faqat sinf a'zolarini (maydonlar va metodlar) himoya qiladi, lekin sinfning o'zi foydalanilmasa, o'chirilishiga ruxsat beradi. -assumenosideeffects R8 ga metod chaqiruvining yon ta'siri yo'qligini va xavfsiz o'chirilishi mumkinligini ko'rsatadi. -keepattributes direktivi bayt kodida metama'lumotlarni — annotatsiyalar, imzolar, istisnolarni saqlaydi.
Retrofit uchun -keep,allowobfuscation,allowshrinking qoidasi R8 ga interfeyslar nomlarini o'zgartirishga ruxsat beradi, lekin ularni o'chirishga yo'l qo'ymaydi. Bu zarur, chunki Retrofit interfeyslarga dinamik proksi (java.lang.reflect.Proxy) orqali murojaat qiladi va o'chirilish runtime da ClassNotFoundException ga olib keladi. Xuddi shunday, Gson @SerializedName annotatsiyasi bo'lgan maydonlarga kirish uchun reflection dan foydalanadi — -keepclassmembers bo'lmasa, maydonlar foydalanilmayotgan sifatida o'chiriladi.
Minifikatsiya (shrinking) — yakuniy qurilishdan foydalanilmayotgan kod va resurslarni o'chirish jarayoni. ProGuard va R8 chaqiruv grafigini kirish nuqtalaridan (Activity, Service, BroadcastReceiver) boshlab tahlil qiladi va chaqiruv zanjiri orqali erishib bo'lmaydigan sinf va metodlarni o'chiradi. ShrinkResources — res/ (layout, drawable, string, color) dan foydalanilmayotgan resurslarni o'chiradigan qo'shimcha bosqich.
Minifikatsiya kutubxonalari bo'lgan katta loyihalarda eng katta foyda beradi. Oddiy manzara: loyiha ulangan kutubxonaning (masalan, Google Play Services) 10% kodidan foydalanadi. Minifikatsiyasiz kutubxonaning butun kodi APK ga tushadi. Minifikatsiya bilan R8 kutubxona kodining 70–90% ni o'chiradi, faqat haqiqatda ishlatiladigan sinf va metodlarni qoldiradi. Bu to'g'ridan-to'g'ri APK hajmiga, yuklash vaqtiga va xotira iste'moliga ta'sir qiladi.
ShrinkResources mexanizmi kod minifikatsiyasi bilan birga ishlaydi. R8 qaysi sinflar ishlatilishini aniqlagandan so'ng, resurs shrinking koddan resurslarga havolalarni tahlil qiladi: R.layout.main, R.drawable.icon, getString(R.string.title). To'g'ridan-to'g'ri yoki bilvosita havolasi bo'lmagan barcha resurslar yakuniy APK yoki AAB dan o'chiriladi. Buning uchun resources.arsc resurs fayli va res/ papkalari ishlatiladi.
Muhim nuance: resurslar R sinfini chetlab o'tib, getIdentifier() yoki Resources.getResourceName() orqali satr nomi bilan chaqirilishi mumkin. Bunday hollarda R8 to'g'ridan-to'g'ri aloqani ko'rmaydi va haqiqatda ishlatilayotgan resursni o'chirishi mumkin. Bunday resurslarni himoya qilish uchun -keep class **.R$* { *; } direktivi mavjud — u R sinfining barcha identifikatorlarini saqlaydi.
<!-- Misol: faqat getIdentifier() orqali ishlatiladigan resurs -->
<string name="dynamic_title_welcome">Xush kelibsiz</string>
<string name="dynamic_title_share">Ulashish</string>
<!-- Satr orqali murojaat qiladigan Kotlin kodi -->
<!-- val title = getString(resources.getIdentifier( -->
<!-- \"dynamic_title_${type}\", \"string\", packageName)) -->Bu holda R8 R sinfidagi dynamic_title_welcome ga statik havolani ko'rmaydi, chunki murojaat dinamik nom bilan getIdentifier orqali amalga oshiriladi. Bunday resurslarni saqlash uchun proguard-rules.pro fayliga -keepclassmembers class **.R$string { *; } direktivi qo'shilishi kerak — u barcha R$string sinflaridan istalgan maydonlarni o'chirishni taqiqlaydi.
| Direktiv | Maqsad | Misol |
|---|---|---|
| -keep | Sinfni va uning barcha a'zolarini saqlaydi | -keep class com.example.api.** { *; } |
| -keepclassmembers | Faqat sinf a'zolarini saqlaydi | -keepclassmembers class * { @SerializedName <fields>; } |
| -keepattributes | Bayt kodining metama'lumotlarini saqlaydi | -keepattributes *Annotation*, Signature |
| -assumenosideeffects | Yon ta'siri bo'lmagan chaqiruvlarni o'chiradi | -assumenosideeffects class Log { d(...); } |
| -dontwarn | Ogohlantirishlarni bostiradi | -dontwarn com.example.legacy.** |
R8 ProGuard-ning vorisi bo'lishiga qaramay, vositalar o'rtasida arxitektura, ishlash va xatti-harakatda tub farqlar mavjud. Google AGP 7.0 dan boshlab Android Gradle Plugin da ProGuard qo'llab-quvvatlashni rasman to'xtatdi, ammo ProGuard R8 da mavjud bo'lmagan maxsus optimallashtirish xatti-harakati talab qilinadigan loyihalarda foydalanishda davom etmoqda.
| Xususiyat | ProGuard | R8 |
|---|---|---|
| Ishlab chiqaruvchi | GuardSquare (Erik Lafourj) | |
| Chiqarilgan yili | 2002 | 2018 (barqaror 2019) |
| Arxitektura | 4 alohida faza (shrink → optimize → obfuscate → preverify) | Bir o'tish: shrink + optimize + obfuscate bir vaqtda |
| AGP ga integratsiya | Tashqi vosita, javac dan keyin ishga tushadi | D8 DEX kompilyatoriga o'rnatilgan |
| Qurish tezligi | 2–3 marta sekin | Yagona o'tish va mahalliy integratsiya tufayli tezroq |
| Kotlin qo'llab-quvvatlash | Cheklangan (inline, lambdas, coroutines bilan muammolar) | To'liq: coroutines, inline-funksiyalar, data class |
| Mapping fayli | mapping.txt (retrace bilan mos) | mapping.txt (bir xil format) |
| Optimallashtirishni sozlash | 60+ variant -optimizationpasses, -optimizations | Cheklangan: ko'pchilik optimallashtirishlar sukut bo'yicha yoqilgan |
| Qo'llab-quvvatlash holati | R8 bilan almashtirilgan (AGP 7.0+ ishlatmaydi) | Faol rivojlanish, AOSP qismi |
R8 ProGuard-dan ko'ra o'lik deb hisoblagan kodni agressivroq o'chiradi. Bu debug qurilishi ishlaydigan, ammo release ClassNotFoundException yoki NoSuchMethodException bilan ishlamay qoladigan holatlarga olib keladi. Odatiy holatlar: sinf nomi bilan reflection dan foydalanadigan kutubxonalar (Gson, Moshi, Retrofit, Room, Dagger); ServiceLoader yoki java.util.ServiceLoader chaqiruvlari; dinamik proksilar (java.lang.reflect.Proxy); mahalliy metodlar (JNI). Yechim — reflection orqali chaqiriladigan barcha sinflar uchun -keep qo'shish.
# Reflection odatiy muammolari — R8 statik aloqani ko'rmaydi
# Room — DAO va migratsiyalarni saqlaymiz
-keep class * extends androidx.room.RoomDatabase { *; }
-keep class *.DatabaseMigrations { *; }
# Dagger / Hilt — komponentlarni saqlaymiz
-keep class * extends dagger.hilt.android.components.** { *; }
# JNI — mahalliy metodlarning nomini o'zgartirmaymiz
-keepclasseswithmembernames class * {
native <methods>;
}
# Data Binding — Binding sinflarini saqlaymiz
-keep class *.databinding.** { *; }Agar qoidalarni qo'shgandan keyin qurish hali ham ishlamasa, proguard-rules.pro faylida -printconfiguration full-config.txt bayrog'idan foydalaning. R8 qaysi qoidalar qo'llanilganini va qaysi sinflar saqlanayotganini ko'rsatadigan to'liq konfiguratsiya faylini yaratadi. Shuningdek -whyareyoukeeping class com.example.MyClass direktivi foydali — u R8 nima uchun ushbu sinfni saqlashga qaror qilganini ko'rsatadi.
ProGuard rules ni to'g'ri sozlash — runtime da xatolarsiz obfuskatsiyaning barqaror ishlashining kalitidir. Quyida yangi loyiha yoki obfuskatsiya xatolarga sabab bo'layotgan loyiha uchun bosqichma-bosqich sozlash jarayoni keltirilgan.
Android SDK ning standart faylini — proguard-android-optimize.txt ni ulash bilan boshlang. U asosiy Android komponentlari uchun qoidalarni o'z ichiga oladi: Activity, Service, BroadcastReceiver, ContentProvider, View, Fragment. Bu fayl SDK papkasida joylashgan: $ANDROID_HOME/tools/proguard/proguard-android-optimize.txt. Agar AGP ishlatsangiz, getDefaultProguardFile uni avtomatik yuklaydi.
Har bir mashhur kutubxona tavsiya etilgan ProGuard qoidalariga ega. Retrofit, OkHttp, Glide, Fresco, Coil, Room, Dagger/Hilt, Kotlin Coroutines — bularning barchasi maxsus -keep qoidalarini talab qiladi. Odatda qoidalar AAR kutubxonasiga kiritilgan va consumer guard rules orqali avtomatik ulanadi. Kutubxona AAR ichida proguard.txt faylini taqdim etadimi yoki yo'qligini tekshiring — bu qoidalar allaqachon hisobga olinganligining belgisidir.
Nashrdan oldin release qurilishini majburiy ravishda haqiqiy qurilmada yoki emulyatorda sinab ko'ring. Obfuskatsiya muammolari faqat runtime da namoyon bo'ladi. Tekshiring: avtorizatsiya (kirish/ro'yxatdan o'tish), tarmoqdan ma'lumotlarni yuklash, ekranlar o'rtasida navigatsiya, kamera va galereya, push bildirishnomalari, Deeplinks, WebView. Release qurilishidagi har bir crash mapping fayli bilan retrace orqali dekodlanishi va etishmayotgan -keep qoidalari qo'shilishi kerak.
Mapping fayli build/outputs/mapping/release/mapping.txt da yaratiladi. Bu fayl saqlash uchun majburiydir: usiz Google Play Console dan crash loglarni dekodlash mumkin emas. Mapping.txt ni versiyalarni boshqarish tizimiga qo'shing yoki CI-artefakt sifatida yuklang. Google Play Console uploading mapping.txt yoqilgan holda AAB yuklanayotganda mapping faylini avtomatik qabul qiladi.
Quyida har bir qoidalar guruhi uchun izohlar bilan proguard-rules.pro faylida obfuskatsiyani sozlashning to'liq workflow i keltirilgan.
# ===========================================
# proguard-rules.pro — to'liq misol
# ===========================================
# --- Umumiy sozlamalar ---
-keepattributes *Annotation*, Signature, Exceptions, InnerClasses, EnclosingMethod
-dontpreverify
# --- Android komponentlari ---
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.Fragment
-keep public class * extends androidx.fragment.app.Fragment
-keep public class * extends android.view.View
# --- OkHttp / Retrofit ---
-dontwarn okhttp3.**
-dontwarn okio.**
-keep class retrofit2.** { *; }
-keepattributes Exceptions
# --- Gson / Moshi ---
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}
-keep class com.google.gson.** { *; }
# --- Firebase ---
-keep class com.google.firebase.** { *; }
-keep class com.google.android.gms.** { *; }
# --- Kotlin Coroutines ---
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
# --- Seriyalashtirish ---
-keepclassmembers class * implements java.io.Serializable {
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# --- Faqat R8: majburiy saqlash ---
# (ProGuard bu direktivni e'tiborsiz qoldiradi)
-keep,allowobfuscation class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}Sozlashdan keyin qurilishni bajaring: ./gradlew assembleRelease. build/outputs/mapping/release/ da fayllar paydo bo'lganligini tekshiring: mapping.txt (asl va obfuska qilingan nomlar muvofiqligi), seeds.txt (-keep qoidalari bilan saqlangan sinflar), usage.txt (minifikatsiya paytida o'chirilgan sinflar). Obfuskatsiyadan keyin APK hajmi ulangan kutubxonalar soniga qarab 20–50% ga kamayishi kerak.
Ko'p beriladigan savollar
R8 — Google tomonidan ishlab chiqilgan ProGuard ning vorisi. R8 obfuskatsiya, minifikatsiya va optimallashtirishni bir o'tishda bajaradi, ProGuard-dan 2–3 marta tez ishlaydi va to'g'ridan-to'g'ri Android Gradle Plugin ga integratsiya qilingan. ProGuard to'rtta alohida fazadan foydalanadi va tashqi ishga tushirishni talab qiladi. AGP 7.0 dan boshlab ProGuard ishlatilmaydi — sukut bo'yicha R8 ishlaydi.
Ha, R8 bir xil ProGuard rules (.pro fayllari) dan foydalanadi. -keep, -keepclassmembers, -keepattributes, -assumenosideeffects direktivlari bir xil ishlaydi. Asosiy qoidalar Android SDK dan proguard-android-optimize.txt da keladi, kutubxonalar (Retrofit, Room, Gson) uchun xos bo'lganlar loyihaning proguard-rules.pro fayliga qo'shiladi. Bu qoidalarsiz R8 reflection orqali ishlaydigan kutubxonalar uchun zarur sinflarni o'chirishi mumkin.
R8 AGP 3.4 dan boshlab Android Gradle Plugin da sukut bo'yicha yoqilgan. Minifikatsiyani faollashtirish uchun build.gradle.kts faylining release buildType blokida isMinifyEnabled = true ni o'rnating. Qo'shimcha isShrinkResources = true bayrog'i foydalanilmayotgan resurslarni o'chirishni yoqadi. gradle.properties da android.enableR8=false orqali R8 ni majburiy o'chirish mumkin, ammo bu tavsiya etilmaydi — R8 tezroq va barqarorroq.
Obfuskatsiya — sinflar, metodlar va maydonlar nomlarini qisqa ma'nosiz nomlarga (a, b, c) o'zgartirish. com.example.app.auth.LoginManager sinfi a.a.a ga, authenticateUser metodi a ga aylanadi. Bu ilovaning reverse engineering-ni qiyinlashtiradi, ammo bajarish mantiqiga ta'sir qilmaydi. ProGuard va R8 faqat -keep qoidalari bilan himoyalanmagan elementlarning nomlarini o'zgartiradi. Mapping fayli crash loglarni dekodlash uchun asl va obfuska qilingan nomlarning muvofiqligini saqlaydi.
Stack trace ni dekodlash uchun retrace utilitesi (ProGuard/R8 SDK tarkibiga kiradi) ishlatiladi. Komanda: retrace mapping.txt crash-stacktrace.txt. Mapping fayli build/outputs/mapping/release/mapping.txt da joylashgan. Google Play Console ham AAB nashr qilinayotganda mapping.txt yuklashni qo'llab-quvvatlaydi — crash loglar konsolda avtomatik dekodlanadi. Mapping faylisiz stack trace faqat obfuska qilingan a.b.c() nomlarini o'z ichiga oladi, bu tuzatish uchun foydasiz.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.