ساخت اپلیکیشن اندروید با استفاده از کوتلین – قسمت دوم

آموزش کوتلین قسمت سوم
آموزش کوتلین

با سلام، پس از آموزش ساخت اپلیکیشن با استفاده از کوتلین قسمت های مقدمه و اول نوبت به قسمت دوم رسید:

  • مقدمه: یه مقدار در مورد پروژه ای که میخوایم انجام بدیم
  • قسمت اول: کانفیگ کردن اندروید استادیو با کوتلین
  • قسمت دوم : سینتکس کوتلین، Null Safety و موارد دیگر در اندروید.
  • قسمت سوم: NewsFragment.kt : توابع Extention و Android Extention
  • قسمت چهارم: RecyclerView : اعمال Adapters و کلاس های داده با استفاده از کوتلین
  • قسمت پنجم: کوتلین، RxJava و RxAndroid 
  • قسمت ششم: API – رتروفیت و کوتلین (retrofit – kotlin)
  • قسمت هفتم: اسکرول بی نهایت: توابع مرتبه بالاتر و لامبداس
  • قسمت هشتم: تغییر جهت (چرخش صفحه) – Mockito, RxJava & Spek
  • قسمت نهم: یونیت تست با استفاده از کوتلین
  • قسمت دهم: کوتلین و داگر 2 (Kotlin & Dagger 2)
  • قسمت یازدهم: یکپارچگی ادامه دار با استفاده از کوتلین BuddyBuild
  • قسمت دوازدهم و پایانی: نتیجه گیری و تمام

در این قسمت از آموزش تمرکز بر فهم سینتکس های پایه زبان کوتلین و همچنین افزودن تمام کد های مورد نیاز برای MainActivity.kt جهت باز کردن fragment مورد نظرمان

سینتکس کوتلین (Kotlin Syntax) :

حالا دیگه MainActivity یه فایل کوتلین می باشد و آماده این هستیم که به شروع  یادگیری سینتکس کوتلین بپردازیم. بار اول که کد رو می بینید امکان داره یه مقدار احساس ترس داشته باشید، منتهی اطمینان داشته باشید که عاشقش می شید. 🙂

کوتلین به روش های زیادی تعریف میشه اما این تعریف زیر که به نظرم تعریف زیبایی از کوتلین هست، تلاش بر این داره که تمام خصوصیات مهم کوتلین را در یک جمله مطرح کنه:

کوتلین زبان برنامه نویسی مختصر، ایمن و استاتیکی است که تمرکز بر تعامل با جاوا دارد.

بیاید از این تعریف فوق برای شروع به مرور کد تولید شده در MainActivity.kt بپردازیم. در ادامه جفت فایل MainActivity برای جاوا و کوتلین آورده شده است.

اولین کد : MainActivity.class قبل از تبدیل به کوتلین

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }
}

کد زیر نیز MainActivity.kt می باشد:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val toolbar = findViewById(R.id.toolbar) as Toolbar
        setSupportActionBar(toolbar)

        val fab = findViewById(R.id.fab) as FloatingActionButton
        fab.setOnClickListener { view -> Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG).setAction("Action", null).show() }
    }
}

مختصر بودن

توجه کنید که فایل جاوا 19 خط داره و فایل کوتلین تنها 12 خط!!

این به این دلیل ست که اکثر گزافه گویی های جاوا حذف شده است. کد مختصر منجر به صرف زمان کمتر برای نوشتن و خواندن می شود خود این قضیه که منجر به ارتقا بهره وری می شود.

 

گسترش و پیاده سازی (extend , implement):

واژه های extends و implement با یک دو نقطه جایگزین شده اند (:). در این مورد ما کلاس را از  AppCompatActivity ، گسترش (extend) داده ایم (که یک کلاس Java می باشد)

class MainActivity : AppCompatActivity()

توابع fun__ctions:

خب دیگه ساختار کلاسیک “public void methodName()” را نداریم. حال به تعریف توابع در داخل کلاس با لغت fun می پردازیم و مقدار return آن نیز در انتهایش می آید. اما type مقدار بازگشتی از متود onCreate کجا هست؟

override fun onCreate(savedInstanceState: Bundle?) {

در مواردی که ما نیاز به return هیج مقداری را در جاوا نداریم از void استفاده می کنیم، در اینجا جایگزین unit هست که به همان روش ازش استفاده می کنیم. کامپایلر خواهد فهمید که ما چیزی را return نمی کنیم پس می توانیم ازش صرف نظر کنیم (مثال بالا). در موردی که میخواهیم اضافه اش کنیم کافیه از روش زیر اقدام کنیم:

override fun onCreate(savedInstanceState: Bundle?) : Unit {

پارامتر ها نیز به ترتیب متفاوتی هستند، ابتدا آنها را تعریف می کنید (نام متغیر) و سپس نوع (type) آن.

 

 Semicolon خداحافظ ;

در کوتلین نیازی نیست که از نقطه ویرگول در انتهای جملات استفاده کنیم، منتهی اگه دوست داشته باشید بازم امکانش هست که از نقطه ویرگول استفاده کنید. (البته استفاده نکنید)

 

 متغیر و مقادیر:

روش تعریف یک متغیر با استفاده از کلمه کلیدی “var” می باشد. نوع از روی Context استنتاج می شود و برای مقادیر ثابت (constant) مشابه است و از کلید واژه val استفاده می شود.

val price = 100        // Int
price = 30             // don't compile! it's a constant
var total = price * 3  // Int
val name = "Juancho"   // String

نوع (type) را می توان صریحا نیز مشخص کرد:

val lastname : String = "Keddit" // explicit type definition
var size : Double = 30.0
var time : Float = 15f

ممکن متوجه شده باشید که نوع اولیه ای (primitive type) وجود نداره، از double استفاده نمی کنیم منتهی Double داریم. این به این دلیل است که هر چیزی در کوتلین یک شئ (object) می باشد. برای کارایی، کامپایلر تعدادی از این اشیاء را به طور داخلی به نوع primitive تبدیل می کند.

 

فیلد ها و خصوصیات (Properties and Fields):

در کوتلین دسترسی به properties همانند دسترسی به field در جاوا می باشد. به جای فراخوانی متد getResource() از یک Activity به طور مستقیم به روش زیر انجام می دهیم:

resources.getString(R.string.id)
// vs
getResources().getString(R.string.id) // still allowed

همچنین می توان متد getResource() را فراخوانی کرد، اما اندروید استادیو به شما پیشنهاد تغییر دادن اون رو خواهد داد.

به این معنا نیست که شما به field ها به صورت مستقیم دسترسی دارید، داریم متد getResource()  را فراخوانی می کنیم اما به روش راحت تر انجام می شود.

Safe ?: Safe!

این یکی از بزرگترین موارد در مورد Kotlin است، هر چیزی در کوتلین nullable نمی باشد مگر آنکه به طور خاص آن را اعلام کنید. روش انجام این کار استفاده از علامت سوال “?” است که به شما پیشنهاد این را می دهد که مقدار باید باشد یا خیر.

یا توجه به چیزی که گفته شد بیاید به چند تا مثال بپردازیم:

val a : String = null  // don't compile!
var b : Int          // neither as must be initialized or abstract.
val ok : String? = null // OK :)

کامپایلر حضور یه شیء null احتمالی را بررسی می کند، این کار ما را از انجام خطای رایج NullPointException یا همان اشتباه میلیون دلاری (گفته Tony Hoare ) باز می دارد.

 

فراخوانی امن (Safe Call)

برا تعامل با شیء Nullable، واقعا ساده هست، علامت سوال “؟” تنها اجازه گرفتن مقادیری که وجود دارند را به شما می دهد، در غیر این صورت آن را رَد خواهد کرد و شما برای ادامه اجرای برنامه ایمن هستید.

val context : Context? = null
val res = context?.getResources() // not crash, res will be null

Smart cast

اگر به ادامه بازی کردن با اشیاء nullable بپردازید، احتمالا انجام موارد زیر را کنار بگذارید:

val context : Context? = null
val res = context?.getResources()
val appName = res?.getString(R.string.app_name)
val shortName = appName?.substring(0, 2)

این خیلی ناخوشایند می باشد، اما شما میتوانید به روش بهتری با استفاده از smart cast آن را انجام دهید. فقط بررسی کنید که زمینه (context) نال هست یا خیر و در داخل بلاک if، زمینه به عنوان شیء non-nullable در نظر گرفته می شود:

val context : Context? = null
if (context != null) {
    val res = context.getResources()    // Don't need '?' anymore
    val appName = res.getString(R.string.app_name)
    val shortName = appName.substring(0, 2)
}
val context : Context? = null
context?.let {
    val res = context.getResources()    // Don't need '?' anymore
    val appName = res.getString(R.string.app_name)
    val shortName = appName.substring(0, 2)
}

عملگر elvis 

الویس نامی هست که برای عملگر “?:” استفاده می شود و میتوانید از آن برای دادن یه مقدار ثانویه برای شیء زمانی که null می شود بدهید. این شبیه روش مختصر انجام دادن بررسی null بودن می باشد. در این مثال “message” می تواند null باشد، نوعش “?String” است. در مواردی که شیء null نیست، مقدار message را ارسال خواهیم کرد، در غیر این صورت، مقداری که بعد از عملگر elvis در نظر گرفته شده ارسال می شود:

try {
    // code...
} catch (e: Throwable) {
    Log.e("TAG", e.message ?: "Error message")
}

توجه: برای آشنایی بیشتر با null safety بهتره یه نگاهی به این لینک بندازید: https://kotlinlang.org/docs/reference/null-safety.html

 

انواع استاتیک (Statically Typed)

به این معنی است که کوتلین نیاز داره که type هر چیزی که در کد تعریف کرده اید را بداند، دلیلش نیز اینه که کامپایلر در هنگام کامپایل بررسی type را انجام می دهد. با توجه به پشتیبانی عالی در اندروید استادیو (تشکر از jetBrains) ، محیط توسعه یکپارچه (IDE) کار بزرگی را جهت کمک به ما برای آنکه بفهمیم که مقدار درستی را به متغیر نسبت داده ایم انجام خواهد داد. 

علاوه بر این، ما لازم نیست type را زمانی که یک variable می سازیم تعیین کنیم (یا زمانی که یک مقدار ثابت (constant) می سازیم). مقدار ثابت toolbar را وارسی کنید، type از زمینه (context) استنتاج می شود، و این یک ویژگی عالی دیگر از کوتلین Kotlin می باشد:

val toolbar = findViewById(R.id.toolbar) as Toolbar

استنتاج نوع (type inference)، مزایای بزرگی را به زبان برنامه نویسی اضافه می کند: قابلیت اطمینان (Reliability) (کامپایلر صحت برنامه را تایید می کند)، قابلیت نگهداری (Maintainability) (کد خودش را توضیح می دهد)، پشتیبانی از ابزار(Tool support)(قبلا نیز ذکر شد، تایپ استاتیک قابلیت های refactoring قابل اطمینان، تکمیل کد و غیره را فراهم می کند) و کارایی (Performance) (در اکثر موارد نیازی نیست که در زمان اجرا مشخص شود که کدام متد باید فراخوانی شود )

100 درصد قابل همکاری با جاوا

این هم یکی دیگه از ویژگی های بزرگ در مورد کوتلین، شما می تونید فایل جاوا را از یک فایل کوتلین اجرا کنید و برعکس. در اپلیکیشن keddit از AppCompatActivity شروع به گسترش کرده ایم، استفاده از شیء Bundle در متُد onCreate و همچنین استفاده از Android SDK برای ساختن app :))

همچنین از دیگر کتابحانه های جاوا همچون Retrofit ، Picasso و غیره نیز استفاده خواهیم کرد.

چیزی که این مورد واقعا مهمه، این هست که کوتلین مجموعه کتابخانه خودش را ندارد، و وابسته به کلاس های کتابخانه های استاندارد جاوا است، اون کلاس ها را با توابع خودش گسترش می دهد.به این معنی است که شما هیچ گاه نیاز به تبدیل اشیاء بین کوتلین و جاوا ندارید.

بروزرسانی MainActivity 

تنها تعدادی متُد به MainActivity جهت باز کردن Fragment که در قسمت بعدی در موردش حرف می زنم اضافه کرده ایم، و همچینین تعدادی کد که اینجا بهشون نیازی نداریم را نیز پاک کرده ایم.

توجه کنید که چگونه از Fragment Manager استفاده کرده ایم:

val ft = supportFragmentManager.beginTransaction();
// and not getSupportFragmentManager()

 

نتیجه گیری

این فقط قسمت اندکی از مفاهیم کوتلین بود که گفته شد، در هنگام توسعه اپلیکیشن در قسمت های بعدی از آموزش با دیگر مفاهیم نیز آشنا خواهیم شد.

اگر نیاز به مفاهیم بیشتر دارید نیز این لینک مناسب خواهد بود.

 

در قسمت سوم از آموزش مجدد در کنارتون خواهم بود 🙂