مقدمه
برای بسیاری از کارها لازم است که برنامههای اندروید به اینترنت متصل شوند و دادهها را از اینترنت بگیرند یا دادههایی که در برنامه تولید شده را به سرور ارسال کنند. قطعاً مثالهای زیادی میتوانید از نیاز به اتصال اینترنتی در برنامهها مثالهای زیادی بزنید. بنابراین به جای پرداختن به حاشیهها، مستقیم میرویم سراغ کد!
ممکن است برخی دوست داشته باشند که به جای استفاده از کتابخانههای موجود، برای هر کاری از جمله اتصال به اینترنت، همه کارها را خودشان بکنند که این روش به هیچ وجه از نظر من درست نیست. من ترجیح میدهم به جای این که خودم را با جزئیات پروتکل HTTP و نکات سطح پایین شبکه درگیر کنم، به منطق برنامه بپردازم و از یک کتابخانه قوی استفاده کنم که این نیازهای سطح پایین را به خوبی انجام دهد. چندین کتابخانه معروف برای انجام دادن امور اتصال به اینترنت وجود دارند که یکی از معروفترین و کاربردیترین آنها OkHttp است. در این مطلب از این کتابخانه استفاده میکنیم.
مجوز دسترسی برنامه به اینترنت
هر برنامه اندرویدی اگر بخواهد به اینترنت دسترسی داشته باشد باید مجوز آن را از کاربر گرفته باشد. بنابراین حتما در مانیفست برنامه و در قسمت مجوزها، مجوز دسترسی به اینترنت را وارد کنید:
<uses-permission android:name="android.permission.INTERNET"/>
نصب OkHttp
نصب OkHttp اگر از گریدل استفاده میکنید بسیار ساده است. خط زیر را به اسکریپت بیلد ماژول برنامه اضافه کنید:
compile 'com.squareup.okhttp3:okhttp:3.4.1'
نکته: اگر به پنجره Project در اندروید استودیو بروید، دو شاخه اصلی میبینید: app و Gradle scripts. شاخه گریدل اسکریپت را باز کنید. حداقل دو فایل build.gradle میبینید:
فایلی که جلوی آن در پرانتز نوشته شده است (Module:app) را باز کنید و در قسمت dependencies خط بالا را اضافه کنید:
نحوه فرستادن درخواست
فرستادن درخواست GET برای گرفتن اطلاعات بسیار ساده است. در OkHttp به دو صورت میتوان درخواست فرستاد: همزمان یا synchronous و ناهمزمان یا asynchronous. اما تفاوت اینها در چیست؟
هر برنامه اندروید در یک ترد Thread جاوا اجرا میشود. اگر به هر دلیلی در این ترد وقفهای ایجاد شود، برنامه از کار میافتد و دیگر به درخواستهای کاربر پاسخی نمیدهد. اگر این زمان پاسخگو نبودن برنامه از چند ثانیه بیشتر شود، اندروید پیغام ANR یا همان Activity Not Responding معروف را به کاربر نشان میدهد و کاربر میتواند برنامه را ببندد و از آن خارج شود. از سوی دیگر اتصال به اینترنت فرایندی است که حداقل چند ثانیه زمان میبرد و بنابراین قطعاً موجب بروز خطای ANR میشود. بنابراین اندروید اجازه نمیدهد که در ترد اصلی برنامه که UI برنامه در آن اجرا میشود هیچگونه اتصال اینترنتی برقرار کنید. برای اتصال اینترنتی، باید یک ترد مجزا ایجاد کرد که به صورت موازی ترد اصلی برنامه اجرا میشود و باعث از کار افتادن ترد برنامه (یا همان UI Thread) نمیشود.
اگر یک ترد جدا ساختهاید و در آن ترد میخواهید به اینترنت متصل شده و اطلاعاتی را دریافت یا ارسال کنید، از روش همزمان یا synchronous استفاده کنید. ولی اگر در ترد UI هستید، حتماً باید از روش asynchronous یا ناهمزمان استفاده کنید. در این حالت OkHttp خودش ترد مجزایی میسازد و کد را در آن ترد اجرا میکند و از طریق بازخوانی (یا callback) شما را از نتیجه کار آگاه میکند. تصویر زیر را ببینید:
همانطور که در عکس میبینید در زمانی که Worker Thread در حال ارسال و دریافت اطلاعات است UI Thread به کار خود ادامه میدهد و هیچ خللی در عملکردش به وجود نمیآید.
گرفتن اطلاعات به صورت همزمان:
اگر تردی مجزا از ترد UI برنامه دارید و میخواهید در آن ترد به اینترنت متصل شده و تبادل اطلاعات کنید، میتوانید از روش اتصال همزمان OkHttp استفاده کنید. فرض کنید میخواهیم فایل README.md که در آدرس https://github.com/alibehzadian/PersianDatePicker/blob/master/README.md قرار دارد را در برنامه بخوانیم:
// ۱ OkHttpClient client = new OkHttpClient(); // ۲ Request request = new Request.Builder() .url("https://github.com/alibehzadian/PersianDatePicker/blob/master/README.md") .build(); // ۳ Response response = client.newCall(request).execute(); // ۴ if (!response.isSuccessful()) { // manage error Log.e("Unexpected code " + response); return; } // ۵ Log.i(response.body().string());
توضیح:
۱- برای هر نوع اتصالی، چه برای گرفتن اطلاعات (GET)، چه برای فرستادن اطلاعات (POST)، باید ابتدا از کلاس OkHttpClient یک نمونه بسازیم.
۲- اطلاعات مورد نیاز برای درخواست (request) اینترنتی را در یک نمونه از کلاس Request قرار میدهیم. برای ساخت یک نمونه از کلاس Request، از کلاس Builder استفاده میکنیم. در این مثال ما فقط نشانی یا url فایل مورد نظر را به درخواست دادهایم. با کلاس Request بعد از این بیشتر کار میکنیم.
۳- از نمونه ساخته شده از OkHttpClient میخواهیم که درخواست ساخته شده را اجرا کند. این خط درخواست را اجرا کرده و نتیجه را به شکل یک نمونه از کلاس Response به ما برمیگرداند. اجرای این خط ممکن است چند ثانیه یا چند دقیقه (بسته به حجم اطلاعات رد و بدل شده، سرعت اینترنت، زمان پاسخگویی سرور و …) طول بکشد. بعد از اجرای این خط یا اطلاعاتی که میخواستیم را به دست آوردیم یا این که میدانیم دقیقاً چه خطایی رخ داده است.
۴- چک میکنیم تا ببینیم آیا پاسخ یا Response درست دریافت کردهایم؟ اگر به هر دلیلی خطایی رخ داده است، آن را مدیریت کرده و در صورت نیاز درخواست را اصلاح میکنیم یا این که مثل نمونه برنامه بالا خطا را لاگ کرده و خارج میشویم.
۵- اگر هیچ خطایی رخ نداده باشد، Response شامل اطلاعات زیاد از جمله متن فایل درخواستی است. برای دسترسی به متن فایل درخواستی مثل کد بالا عمل میکنیم.
ساده است، نه؟ حالا برویم سراغ روش ناهمزمان!
گرفتن اطلاعات به صورت ناهمزمان:
فرض کنید در ترد UI برنامه میخواهیم محتوای فایل README.md را از اینترنت بگیریم. در این حالت OkHttp خودش یک ترد ایجاد میکند و به صورت موازی برنامه اصلی سعی میکند تا اطلاعات را بگیرد. تنها فرق این روش با روش قبلی در این است که دیگر ما منتظر نمیمانیم تا Response آماده شود. اینجا باید توابعی را در اختیار client قرار بدهیم تا پس از اجرای درخواست، بسته به این که درخواست با موفقیت اجرا شده یا به خطا برخورده است، اجرا شوند. برای این کار یک نمونه از کلاس Callback میسازیم و آن را به client میدهیم:
// ۱ OkHttpClient client = new OkHttpClient(); // ۲ Request request = new Request.Builder() .url("https://github.com/alibehzadian/PersianDatePicker/blob/master/README.md") .build(); // ۳ Callback callback = new Callback() { // ۴ @Override public void onFailure(Call call, IOException e) { // manage failure ! } // ۵ @Override public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { // manage error Log.e("Unexpected code " + response); return; } // show body content Log.i(response.body().string()); } }; // ۶ client.newCall(request).enqueue(callback);
توضیح:
۱- ساختن یک نمونه از کلاس OkHttpClient مشابه حالت قبل
۲- ساختن یک نمونه از کلاس Request مشابه حالت قبل
۳- ساختن یک نمونه از کلاس Callback که دو متد onFailure و onResponse آن باید حتماً پیادهسازی شوند. این متدها پس از اجرای Request اطلاعات مورد نیاز را به ما میدهند.
۴- متد onFailure زمانی صدا زده میشود که خطایی در اتصال رخ دهد. خطاهایی مانند وصل نبودن به اینترنت و امثال آن. پارامترهای این متد یک نمونه از کلاس Call و یک نمونه از کلاس IOException است.
۵- متد onResponse زمانی صدا زده میشود که اتصال اینترنتی برقرار شده و پاسخی از سرور دریافت شده است. ممکن است پاسخ دریافتی اطلاعات مورد نیاز ما نباشد. مثلاً فایلی در آدرس داده شده به Request وجود نداشته باشد (خطای ۴۰۴ معروف) یا این که سرور دسترسی به اطلاعات را ندهد، یا خطاهایی مانند اینها.
۶- در صف قرار دادن Request برای اجرا. اینجا بر خلاف حالت قبل از متد execute استفاده نمیکنیم و به جای آن از متد enqueue استفاده میکنیم. این متد درخواست را به صورت ناهمزمان اجرا میکند. نمونه ساخته شده از کلاس Callback را به این متد میدهیم.
در ادامه…
در ادامه این سلسله مطالب، به نحوه ارسال اطلاعات، گرفتن عکس از اینترنت، REST و مواردی از این دست خواهیم پرداخت. از قسمت بعد یک برنامه نمونه مینویسیم و همزمان با مطلب، برنامه را هم توسعه میدهیم! با ما باشید!














سلام دوست عزیزم
من سایت های زیاد رو در مورد آموزش اندروید مطالعه کردم بهترین و سلیس ترین نوعش رو تو سایت شما دیدم . خواستم عرض ادب و تشکر ویژه از شما و روش آموزش بسیار عالی تان کنم.
فقط یه انتقاد کوچیک داشتم اون هم پراکندگی مطالب هستش اگر قسمت آموزش اندروید به صورت یه دست بندی منظم در یک صفحه باشد بسیار عالی است.
ارادمند گودرزی
متشکرم
گفته شد برای نصب okhttp کد زیر را در اسکریپت بیلد ماژول برنامه وارد کنید.
compile ‘com.squareup.okhttp3:okhttp:3.4.2’
میشه توضیح بدید یعنی دقیقا در کدوم فایل و در کدوم پوشه باید این خط رو وارد کنیم؟
آیا باید در فایل build.gradle که در پوشه src قرار داره باید این کد رو وارد کنیم؟
مطلب را ویرایش کردم. قسمت نصب okhttp را دوباره بخوانید.
دو نوع کد همزمان و غیر همزمان گفتید ولی نگفتید که این کدها باید در کجا ثبت بشن؟
آیا باید داخل یک متد داخل کلاس اصلی اکتیویتی تعریف بشن و بعد تو یک رویداد اون متد فراخوانی بشه؟
موضوع اتصال برنامههای اندروید به سرور موضوع خیلی طولانیای هست. تا الان ۸ بخش شده و احتمالا هنوز چند بخش دیگه داره. بعد از اتمام آموزشهای مربوط به اینترنت کمی درباره معماری MVP خواهم گفت و بعد از آن پروژه صفر تا صد رو ادامه میدم. در پروژه صفر تا صد قراره یه پروژه واقعی و حرفهای رو مرحله به مرحله انجام بدیم. اونجا از همه این مطالب استفاده خواهم کرد.
با عرض سلام خدمت آقای بهزادیان بنده یکی از طرفداران سایت شما هستم و بسیار تشکر می کنم که دانسته ها و توانایی ها تون رو در اختیار ما میزارید.
آقای بهزادیان من یک سوال دارم که بد جور مغزمو درگیر کرده
قضیه همزمان و غیر همزمان رو خوب متوجه نشدم
در روش همزمان ما با ایجاد و اجرا کردن یک ترد در کنار ترد UI درخواست ها رو به سمت سرور ارسال و نتیجه رو اجرا می کنیم در این روش هر دو ترد به صورت همزمان توسط پردازنده اجرا میشوند.
در روش غیر هم زمان ترد خاصی به وجود نمیاد؟؟
در روش همزمان، ترد جدایی برای اتصال ساخته نمیشود و در همان ترد فعلی درخواست ارسال میشود. بنابراین اگر کد درخواست همزمان را در ترد UI برنامه اجرا کنید برنامه شما با خطای NetworkOnMainUiThread مواجه خواهد شد.
در روش ناهمزمان، OkHttp خودش یک ترد مجزا ایجاد میکند و درخواستها را در آن اجرا میکند. بنابراین میتوانید کدهای ناهمزمان را در ترد UI برنامه هم اجرا کنید.
سلام
OKHttp مثل volley عمل میکنه؟
از اونجایی که volley توسط خود گوگل توسعه داده میشه بهتر نیست به جای OKHttp از Volley استفاده کنیم؟
این دو تا کتابخونه تنها شباهتی که با هم دارن تفاوتشونه. والی هم به صورت رسمی عرضه نشده و گوگل اون رو پشتیبانی نمیکنه. API والی به نظر من بیش از حد پیچیده است و مستندات به درد بخوری هم نداره و … در کل خیلیها نظر مساعدی بهش ندارن.
با سلام
من از کدهای شما در اندروید استودیو استفاده کردم
برنامه من با emulator با api 26 جواب میده ولی با گوشی k10 برنامه به محض اجرای دستورات okhttp متوقف میشه و از برنامه میپره بیرون
کدهای برنامه من:
OkHttpClient client = new OkHttpClient();
// ۲
Request request = new Request.Builder()
// .url(“https://github.com/alibehzadian/PersianDatePicker/blob/master/README.md”)
.url(“http://www.imodares.ir/json-url/imodares-json.html”)
.build();
client.newCall(request).enqueue(callback);
برنامه اصلن وارد قسمت callback نمیشه
با روش غیرهمزمان هم امتحان کردم یعنی excute ولی بازم همین نتیجه رو گرفتم
دقیقا چه خطایی میده؟