اشتراک گذاری کد بین اندروید و iOS با ++C — از صفر تا صد
در این مقاله به توضیح روش اشتراک گذاری کد بین سیستمهای عامل اندروید و iOS با استفاده از کدنویسی در زبان ++C میپردازیم.
چرا باید کد را به اشتراک گذاشت؟
اغلب توسعهدهندگان حرفهای علاقهمند هستند که اپلیکیشنهای نیتیو بنویسند و به باور این دسته بهترین UX از طریق رابط کاربری روان و بومی به دست میآید. اگر چه غالباً ضروری است که لایه ارائه نیتیو باشد، به طور عکس منطق تجاری اپلیکیشن را میتوان به اشتراک گذاشت. زمانی که منطق تجاری اپلیکیشن به اشتراک گذارده میشود، میتوان کد آن را یک بار نوشت و از این رو هزینه نگهداری کاهش مییابد و امکان طراحی رفتار مشابهی بین سیستمهای عامل اندروید و iOS پدید میآید.
در اغلب پروژهها دستکم بخشی از کد هست که میتوان بین سیستمهای عامل مختلف به اشتراک گذاشت. این بخشها شامل منطق پایگاه داده، اعتبارسنجی کاربر و نظایر آن میشود. در اغلب این موارد، از ++C به عنوان یک زبان مشترک استفاده میشود. در این مقاله، ما با روش انجام این کار و مزایا و معایب استفاده از ++C برای توسعه اپلیکیشنهای موبایل آشنا میشویم.
++C روی موبایل
سیستمهای عامل موبایل امروزه به دو دسته iOS و اندروید تقسیم میشوند.
iOS
++C در سیستم عامل iOS یک شهروند درجه یک محسوب میشود. اگر شما نیز این روزها همچنان به این زبان برنامهنویسی میکنید، میتوانید به سادگی با تغییر دادن فایل منبع از پسوند m. به nm. شروع به کدنویسی برای این سیستم عامل بکنید. البته این تغییر برخی ترفندهای جالب دیگر را نیز در سوئیفت در اختیار ما قرار میدهد. در سوی دیگر سوئیفت هیچ همکاری متقابلی با ++C ندارد. شما میتوانید تابعهای C و ++C را به هم مرتبط کنید؛ اما باید از یک پوشش ++Objective-C برای عرضه کلاسها بهره بگیرید.
اندروید
اندروید نیز با استفاده از کیت توسعه نیتیو (NDK) میتواند از ++C در مواردی که به کدهای با عملکرد بالا نیاز هست یا لازم است کتابخانههایی بر مبنای کدهای موجود ساخته شود استفاده کند. اینترفیس نیتیو جاوا (JNI) شیوه تعامل بین این کد نیتیو و بایتکد نوشته شده در جاوا یا کاتلین را تعیین میکند. پس از راهاندازی زنجیره ابزار، این فرایند به سادگی تعریف کردن و استفاده از متدهای نیتیو در جاوا خواهد بود:
1// Tell Java that the method will be provided with a native implementation
2public native String stringFromJNI();
3
4// Use native method
5tv.setText( stringFromJNI() );
اعلان تابع در سمت پیادهسازی C نیز چنین است:
1// Function name must follow several rules
2// Assuming java counterpart in hellojni/src/com/example/hellojni/HelloJni.java
3JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz )
4{
5 return (*env)->NewStringUTF(env, "Hello from JNI!");
6}
در روزهای آغازین اشتراک کد در ++C نوشتن چنین اعلانهای رمزآلودی، بخشی جداییناپذیر از فرایند توسعه محسوب میشد. خوشبختانه در سال 2014، Dropbox ابزاری به نام Djinni را منتشر ساخت که میتوانست هر دو اتصال Objective-C++ و JNI را تولید کند.
Djinni
Djinni از یک زبان تعریف اینترفیس (IDL) ساده برای توصیف اینترفیسی که از سوی ++C عرضه میشود، استفاده کرده است. این IDL از رکوردها برای شیءهای مقدار خالص داده و از اینترفیسها برای اشیایی که در یکی از محیطهای ++C یا Objective-C/Java پیادهسازی میشوند بهره میگیرد.
1# A simple user model with id and name
2user = record {
3 id: i32;
4 name: string;
5}
6
7# A logger that has to be provided by the client
8logger = interface +j +o {
9 log_string(str: string);
10}
11
12# A database class that will be implemented in C++
13database = interface +c {
14 load_user(id: i32, logger: logger): user;
15}
Djinni از انواع مختلفی از داده از enum، بولی، عدد، رشته تا آرایه و نگاشت برای تعریف اینترفیس برای هر نوع اشتراک کدبیس پشتیبانی میکند و امکان پیادهسازی انواع داده سفارشی را نیز در صورت نیاز دارد. برای کسب اطلاعات بیشتر در مورد آن میتوانید به این صفحه (+) مراجعه کنید.
متأسفانه دراپباکس اخیراً اعلام کرده است که نگهداری از Djinni را متوقف میکند. اما این پروژه به قدر کافی بالغ و پایدار است که بتوان بر مبنای آن کدنویسی کرد و تصور نمیشود در طی زمان معقولی در آینده مشکل چندانی پیش بیاید.
تنظیم Djinni
هر پروژهای یک ریپازیتوری GIT دارد که شامل تعاریف اینترفیس و کد ++C اشتراکی است. متأسفانه Djinni تنها یک فایل IDL میپذیرد. از این رو باید برای هر IDL در ریپازیتوری یک فراخوانی به djinni/run داشته باشیم. در حال حاضر فرایند Make برای اندروید به صورت دستی آغاز میشود. روی iOS این فرایند به صورت یک Run Script Phase به فرایند build اضافه شده است.
روی هر دو پلتفرم پیادهسازی ++C به همراه فایلهای تولید شده مستقیماً به پروژه اضافه میشوند. همچنین یک کتابخانه استاتیک ساخته میشود، اما افزودن همه چیز به یک پروژه موجب میشود که فرایند توسعه و دیباگ کردن به مقدار زیادی آسانتر شود. روی اندروید، CMakeList را راهاندازی میکنیم که شامل همه فایلها در چند دایرکتوری است. اما روی iOS به صورت دستی فایلهای جدید را اضافه میکنیم.
افزودن کد جدید ++C
در این بخش مراحلی را که برای افزودن یک قابلیت جدید به کدبیس اشتراکی نیاز داریم توضیح دادهایم. در این مثال، کار خود را از سمت Xcode/iOS آغاز میکنیم، اما شما میتوانید کار خود را از سمت اندروید نیز شروع کنید. ما میخواهیم فاصله بین دو رشته را محاسبه کنیم. این مسئله به نام «فاصله لوناشتاین» (Levenshtein Distance) مشهور است.
تعریف اینترفیس
ابتدا باید یک فایل جدید IDL برای تعریف اینترفیس اضافه کنیم. Makefile ما انتظار دارد که یک فایل با پسوند.jinni دریافت کند. پیادهسازی آن کاملاً سرراست است. ما یک اینترفیس جدید تعریف میکنیم و c+ را نیز اضافه میکنیم تا به djinni اعلام کنیم که اینترفیس ++C را پیادهسازی خواهیم کرد. سپس یک متد استاتیک اضافه میکنیم که دو رشته را گرفته و یک عدد صحیح باز میگرداند.
1Levenshtein = interface +c
2{
3static distance(from:string, to:string):i32;
4}
تولید فایلها
پس از افزودن یک فایل جدید IDL پروژه خود را یک بار build میکنیم تا فایلهای bridging جدید تولید شوند. سپس آنها را به صورت دستی به پروژه Xcode اضافه میکنیم. برای این که فایلها در اختیار سوئیفت قرار گیرند، باید آنها را به هدر bridging نیز اضافه کنیم. چنان که پیشتر اشاره شد، باید Make را به صورت دستی فراخوانی کنیم تا فایلها را برای bridging مربوط به JNI اندروید تولید کند.
پیادهسازی ++C
در نهایت میتوانیم شروع به پیادهسازی «منطق تجاری» (business logic) کوچک خود بکنیم. ابتدا یک فایل ++C میسازیم و آن را به پروژه Xcode اضافه میکنیم. در واقع ما صرفاً اعلان تابع را از هدر عمومی کپی کرده و به پیادهسازی حاصل از Rosettacode (+) اضافه کردیم.
1//
2// Levenshtein.cpp
3// DjinniExample
4//
5// Created by Nicolas Märki on 28.04.19.
6// Copyright © 2019 Ubique Innovation AG. All rights reserved.
7//
8
9#include "Levenshtein.hpp"
10
11int32_t Levenshtein::distance(const std::string &from, const std::string &to)
12{
13 const int32_t m((int32_t)from.size());
14 const int32_t n((int32_t)to.size());
15
16 if( m==0 ) return n;
17 if( n==0 ) return m;
18
19 size_t *costs = new size_t[n + 1];
20
21 for( size_t k=0; k<=n; k++ ) costs[k] = k;
22
23 size_t i = 0;
24 for ( std::string::const_iterator it1 = from.begin(); it1 != from.end(); ++it1, ++i )
25 {
26 costs[0] = i+1;
27 size_t corner = i;
28
29 size_t j = 0;
30 for ( std::string::const_iterator it2 = to.begin(); it2 != to.end(); ++it2, ++j )
31 {
32 size_t upper = costs[j+1];
33 if( *it1 == *it2 )
34 {
35 costs[j+1] = corner;
36 }
37 else
38 {
39 size_t t(upper<corner?upper:corner);
40 costs[j+1] = (costs[j]<t?costs[j]:t)+1;
41 }
42
43 corner = upper;
44 }
45 }
46
47 size_t result = costs[n];
48 delete [] costs;
49
50 return result;
51}
استفاده در پروژهها
کار به همین سادگی بود که شرح دادیم، ما فاصله را در ++C پیادهسازی کردیم و اینک میتوان آن را در Objective-C ،Swift ،Java یا Kotlin استفاده کرد.
سخن پایانی
آیا ++C زبان مناسبی برای نوشتن یک پروژه اپلیکیشن موبایل محسوب میشود؟ پاسخ این است که اگر تمایل به نوشتن کد ++C داشته باشید چنین است و این تنها شرط لازم و کافی برای چنین کاری محسوب میشود. یک پیکربندی با سازماندهی مناسب از djinni بخش عمدهای از دشواری پل زدن بین سیستمهای عامل را رفع میکند، اما موجب کاهش پیچیدگی نوشتن کدهای ++C نمیشود. وقتی به زبانهای Objective-C ،Swift ،Java یا Kotlin کدنویسی میکنید، احتمال بروز باگ خطرناک در اپلیکیشن کم است، اما در زمان کدنویسی ++C چنین امری کاملاً محتمل است. در هر حال، اگر تیم شما از قبل تجربهای در کدنویسی به زبان ++C دارد و آن را زبان مناسبی میداند، میتوانید با استفاده از این زبان به شدت سریع و بالغ و دارای پشتیبانی مناسب اقدام به اشتراک کد بین پلتفرمهای مختلف بکنید.
مزیتها
- هر دو پلتفرم اندروید و iOS خودشان بر مبنای ++C توسعه یافتهاند و از این رو زبان رسمی آنها است و کاملاً پشتیبانی میشود.
- ++C سریع و بالغ است.
- Djinni بخش عمده دشواری پل زدن را به خصوص از طریق JNI از میان برمیدارد.
معایب
- یادگیری ++C دشوار است.
- بروز خطا در ++C بسیار محتملتر است.
- برای تعریف اینترفیسها به یک زبان سوم نیاز دارید.
- متدهای بصری تولید شده به صورت خودکار، یک سطح غیرضروری از انتزاع محسوب میشوند.
- پشتیبانی دیباگر به اندازه زبانهای ابتدایی پلتفرمها خوب نیست.
- در نهایت با پیچیدگیهای کدنویسی به زبان ++C روبرو خواهید بود.
در سالهای اخیر بسیاری از اپلیکیشنهای بزرگ از این تکنیک اشتراک کد از طریق ++C و Djinni استفاده کردهاند. این تکنیک امکان بهرهبرداری حداکثری از سختافزار موجود، تکرارهای سریع بدون نگرانی از واگرایی و تمرکز روی نکتهای که برای همه توسعهدهندگان مهم است، یعنی ایجاد یک تجربه کاربری عالی را فراهم ساخته است.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی
- آموزش برنامه نویسی C++
- مجموعه آموزشهای برنامهنویسی اندروید
- انتخاب پلتفرم مناسب برای ساخت اپلیکیشن
- مقایسه Xamarin ،React Native و Flutter برای توسعه چند پلتفرمی — راهنمای کاربردی
==