دریافت اطلاعات از سرور و نمایش در ریسایکلر ویو     (mvvm,rxjava,retrofit,picasso)

با سلام و وقت بخیر خدمت شما دوستان عزیزتوی این مقاله قصد داریم که یک سری اطلاعات رو از api بگیریم و توی ریسایکلر ویو نمایش بدیمحالا از چه مفاهیم و کتابخانه هایی توی پروژه میخوایم استفاده کنیم؟mvvm,rxjava,retrofitابتدا لازم میدونم نکته ای رو عرض کنم :این مثال ساده ترین و ابتدایی ترین شکل استفاده از معماری mvvm و rx هست واین مقاله مناسب افرادی هست که اشنایی ابتدایی در مورد این موارد دارن و میخوان طرز استفاده از این موارد رو یاد بگیرنبرنامه ما بدین شکله:خوب بریم برای شروع :ابتدا برای این که ما بتونیم اطلاعاتمون رو توی برنامه بگیریم نیاز به یک api داریم که سایت های زیادی برای ساخت api هست و ما اینجا از سایت https://www.mocky.io/ استفاده میکنیم و ساختار json خودمون رو طبق تصویر زیر میسازیم.محیط سایت:روی generate کلیک میکنیم و لینک رو به ما میدهلینک فایل json ما:http://www.mocky.io/v2/5e9071493300002cb827d7a7در اینجا ما یه id و یه title و یه content و یه image داریم کهشناسه (id): میشه مشخصه یکتای ما برای استفاده در برنامه که ما(مثلا اینجا چهارتا آیتم داریم که آی دی از 1 تا 4 میشه)عنوان (title): میشه عنوان آیتم ما که در اینجا به عنوان اسم ماشین در نظر میگیریممحتوا (content): توضیحاتی در مورد ماشینتصویر (image): تصویر ماشین مد نظر ماشما میتونید به تعداد دلخواه اون مشخصه مورد نظرتون رو اضافه کنید و توی برنامه ازش استفاده کنید برای مثال میتونید مثلا سال ساخت ماشین رو جدا توی json تعریف کنید و یا به جای 4 آیتم ماشین 20 آیتم استفاده کنید(20ماشین مختلف)در مرحله بعدیباید یک پروژه جدید اندروید استودیوبسازیم،تنظیمات رو طبق تصویر زیر به صورت پیشفرض بزارید ،ما از زبان جاوا استفاده میکنیم توی این مثال:پروژه رو که ساختیم دومین کار این هست لایبرری های مورد نظر رو در فایل build.gradleاضافه کنیم//recyclerview and CardView implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation &quotandroidx.cardview:cardview:1.0.0&quot //retrofit implementation 'com.squareup.retrofit2:retrofit:2.7.2' implementation 'com.squareup.retrofit2:converter-gson:2.7.2' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.7.0' //rxjava implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'io.reactivex.rxjava2:rxjava:2.2.18' //picasso implementation 'com.squareup.picasso:picasso:2.71828'لایبرری های مورد نیاز ما این موارد هست که میتونید با سرچ کردن توی نت به صورت جدا پیداشون کنید یا همین ها رو توی برنامتون کپی کنید که اخرین نسخه هر لایبرری تا این لحظه هستخوب لازم میدونم توضیحات کلی در مورد کتابخانه هایی که ما توی برنامه استفاده میکینم رو عرض کنمابتدا ریسایکلر ویو هست که میتونیم بگیم نسل بعدی لیست ویو هست با مزایای بیشتری برای نمایش آیتم های ما در صفحه که میتونید تفاوتش رو با لیست ویو توی نت سرچ کنید..فرض من اینه که در موردش اطلاع دارین و برای طولانی تر نشدن مقاله از توضیح بیشتر خودداری میکنیمدومین لایبرری کارد ویو هست که یکی از ویجت های متریال دیزاین هست که خیلی ساده: برای زیبا تر کردن آیتم هایی که استفاده میکنیم مثلا هر ماشین یک آیتم هست باعث میشه بتونیم دور ویو رو گرد کنیم و مسائل دیگه ای...سه تا لایبری بعدی مربوط به retrofit هست که ابتدایی مربوط به خود رتروفیت هست که رتروفیت یک کتابخانه ای هست که ما میتونیم به وسیله اون اطلاعاتمون رو از سرور و api که داریم بگیریم که کتابخانه های مختلفی مثل والی و .. هست که رتروفیت استفادش ساده تر و خیلی راحت میتونیم ازش استفاده کنیمدومی: مربوط به تبدیل فایل های json به جاوا هست...در بالا ما یک سری اطلاعات به صورت json ذخیره کردیم که این فایل باید تبدیل به java بشه و از این کانورتر استفاده میکنیمو سومی هم به ما این قابلیت رو میده که از rx به عنوان اداپتر رتروفیت استفاده کنیملایبرری های بعدی هم مربوط به rxjava و rx android در برنامه ما هست که ما میتونیم از این مفاهیم در برناممون استفاده کنیممن انتظار دارم یه اشنایی کلی با rx java داشته باشین و اگه ندارین حتما در موردش تحقیق کنید که یک rxjava یک مفهوم هست در جاوا که یک سری قابلیت هاش برای اندروید کاستومایز شده و یک لایبرری هم به اسم rx android اضافه شدهو اخرین کتابخانه هم کتابخانه پیکاسو هست برای بارگزاری تصاویر ما در برنامه که لایبرری های مشابه زیادی مثل glide و فرسکو داریم که میتونید به دلخواه از هر کدوم که تمایل داشتین استفاده کنینسعی کردم به طور خلاصه در مورد کتابخانه هایی که استفاده میکنیم توضیحات مختصری عرض کنم که هرکدوم از این مواردخودش یک مقاله جداست و برای اطلاع بیشتر در موردشون میتونید در موردشون سرچ کنیددر مرحله بعدی ریسورس های مربوط به نمایش ویو رو میسازیم که ریسورس اولی مربوط به اکتیویتی main هست که ما در این جا طبق تصویر زیر صرفا یک ریساکلر ویوو اضافه میکنیم و یک ایدی برای اون ست میکنیم<?xml version=&quot1.0&quot encoding=&quotutf-8&quot?> <RelativeLayout xmlns:android=&quothttp://schemas.android.com/apk/res/android&quot xmlns:app=&quothttp://schemas.android.com/apk/res-auto&quot xmlns:tools=&quothttp://schemas.android.com/tools&quot android:layout_width=&quotmatch_parent&quot android:layout_height=&quotmatch_parent&quot tools:context=&quot.main.MainActivity&quot> <androidx.recyclerview.widget.RecyclerView android:id=&quot@+id/recyclerview&quot android:layout_width=&quotmatch_parent&quot android:layout_height=&quotmatch_parent&quot/> </RelativeLayout>دومین ریسورس رو خودمون با کلیک راست بر روی قسمت layout->new->layout resource file به اسم item_recycler میسازیم که کدش رو میتونید مشاهده کنید که یک image view برای نمایش عکس و دوتا textview برای نمایش عنوان و متن داریم<?xml version=&quot1.0&quot encoding=&quotutf-8&quot?> <androidx.cardview.widget.CardView xmlns:android=&quothttp://schemas.android.com/apk/res/android&quot xmlns:app=&quothttp://schemas.android.com/apk/res-auto&quot android:layout_width=&quotmatch_parent&quot android:layout_height=&quotwrap_content&quot android:layout_margin=&quot8dp&quot app:cardCornerRadius=&quot8dp&quot app:cardElevation=&quot8dp&quot> <LinearLayout android:layout_width=&quotmatch_parent&quot android:layout_height=&quotwrap_content&quot android:orientation=&quotvertical&quot> <ImageView android:id=&quot@+id/iv_main&quot android:layout_width=&quotmatch_parent&quot android:scaleType=&quotfitXY&quot android:layout_height=&quot200dp&quot /> <TextView android:id=&quot@+id/tv_main_title&quot android:layout_width=&quotmatch_parent&quot android:layout_height=&quotwrap_content&quot android:maxLines=&quot1&quot android:layout_marginStart=&quot4dp&quot android:textAppearance=&quot@style/TextAppearance.AppCompat.Large&quot /> <TextView android:id=&quot@+id/tv_main_content&quot android:layout_width=&quotmatch_parent&quot android:layout_height=&quotwrap_content&quot android:maxLines=&quot3&quot android:layout_marginStart=&quot4dp&quot android:textColor=&quot@android:color/black&quot android:textSize=&quot12sp&quot /> </LinearLayout> </androidx.cardview.widget.CardView>حالا که ریسورس هامون تکمیل شد میریم سراغ کدهای جاوا:خب توی این مرحله میایم سه تا پکیج به پروژه اضافه میکنیم برای زیبایی و بهتر فهمیدن کدمون در آینده که پکیج بندی باعث میشه که راحت تر به کلاس مورد نظرمون دسترسی داشته باشیم در آیندهطبق تصویر زیر سه تا پکیج داریمhttp:کلاس های مربوط به رتروفیت رو برای گرفتن دیتا از سرور اینجا میسازیمData:کلاس های مربوط به نمایش ویو مثل اداپتر و مدل ریسایکلر ویوو رو اینجا میسازیمMain:کلاس های اصلی و پایه برنامه رو اینجا میسازیمتوی پکیج دیتا یک کلاس جدید به اسم Car میسازیم که به عنوان مدل ریسایکلر ویوو ما استفاده میشه،مدل چیه : ساختار json ای که قبلا ساختیم رو اینجا به این صورت که توی تصویر زیر میبینید تعریف میکنیم و میگیم ماشین ما این مشخصات رو دارهبعد باید برای این مشخصه هامون getter و setter تعرف کنیم که برای این کار پایین صفحمون کلیک راست میکنیم رو گزینه generate کلیک میکنیم و روی getter and setter کلیک میکنیم و فیلدهامون رو همه انتخاب میکنیمکدنهاییمون به این صورت میشه:public class Car { private int id; private String title; private String content; private String image; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getImage() { return image; } public void setImage(String image) { this.image = image; } }البته یک نکته ای رو عرض کنم ممکنه ایتم های شما زیاد باشن و نخواید یکی یکی به صورت دستی توی مدلتون بنویسید و اینجا میتونید از پلاگین هایی که موجود هست استفاده کنید به عنوان مثال پلاگین generate POJOs form JSON که به راحتی json شما رو به همراه getter و setter تبدیل به کلاس جاوا میکنهمیتونید در موردش سرچ کنیدو نوبت میرسه به ساخت adpater برای ریسایکلر ویو:یک کلاس جدید میسازیم و اسمش رو CarAdpater میزاریم و بعد این کلاس میاد extend میکنه recyclerView.Adapterرو که در اینجا نیاز به یک ویو هولدر داره که مثل تصویر زیر اون رو میسازیمبعد موس رو روی ویو هولدر و کلاس اصلیمون نگه میداریم و alt و اینتر رو میزنیم که متد های مورد نظر ما رو implement کنه که کد به این صورت در میاد:و نوبت میرسه به فراخوانی view ها و استفاده از اون ها در این کلاس که کدنهایی adapter ما به این صورت میشه:public class CarAdapter extends RecyclerView.Adapter<CarAdapter.RvViewHolder> { private List<Car> cars; private Context context; public CarAdapter(List<Car> cars, Context context) { this.cars = cars; this.context = context; } @NonNull @Override public RvViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new RvViewHolder(LayoutInflater.from(context).inflate(R.layout.item_recycler, parent, false)); } @Override public void onBindViewHolder(@NonNull RvViewHolder holder, int position) { holder.Bind(cars.get(position)); } @Override public int getItemCount() { return cars.size(); } public class RvViewHolder extends RecyclerView.ViewHolder { private ImageView iv; private TextView tvTitle; private TextView tvContent; public RvViewHolder(@NonNull View itemView) { super(itemView); iv = itemView.findViewById(R.id.iv_main); tvTitle = itemView.findViewById(R.id.tv_main_title); tvContent = itemView.findViewById(R.id.tv_main_content); } public void Bind(Car car) { Picasso.get().load(car.getImage()).into(iv); tvTitle.setText(car.getTitle()); tvContent.setText(car.getContent()); } } }که توی کانستراکتور کلاسمون لیستی از ماشین و کانتکس رو بایند میکنیم و در متد oncreateviewholderباید اون ریسورسی که برای ریسایکلرویو ساختیم رو پاس بدیمو در bindviewholderمیگیم که این موارد رو برای ما با استفاده از id ست میکنه title,content,imageو قسمت بعدی تعداد ماشین هامون هست که طبق کد ست میکنیمومتد بایند میایم ویوهامون رو با استفاده از کلاس Car که ساختیم طبق کد بالا ست میکنیمدرمرحله بعدی ما یک اینترفیس به اسم ApiService توی پکیج http میسازیم و کد به این صورت هستpublic interface ApiService { @GET(&quot5e9071493300002cb827d7a7&quot) Single<List<Car>> getCars(); }ابتدا از انوتیشن get@ که مربوط به رتروفیت برای گرفتن اطلاعات از سرور هست استفاده میکنیم و توی " " باید انتهای ادرسی که داریم رو قرار بدیم بدین صورت که ادرس کامل ما این هست http://www.mocky.io/v2/5e9071493300002cb827d7a7 که اون قسمت انتهاییش رو ما توی دابل کوتیشن میزاریم و در قسمت بعدی یک متد داریم که میگیم یه لیست از ماشین ها با استفاده از single به ما برگردون که سینگل یک observable آرایکس جاوا هست که جلوتر در موردش توضیح میدمدر قسمت بعدی یک کلاس توی پکیج http به اسم ApiServiceSingleton میسازیم که اینجا میخوایم یک نمونه از رتروفیت بسازیم کد کامل به این صورت میشه:public class ApiServiceSingleton { private static ApiService apiService; public static ApiService getInstance() { if (apiService == null) { Retrofit retrofit = new Retrofit.Builder() .baseUrl(&quothttp://www.mocky.io/v2/&quot) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); apiService = retrofit.create(ApiService.class); } return apiService; } }ابتدا که نمونه از کلاس api service میسازیم به صورت استاتیک که مطمئن بشیم به صورت ایستا هست و جایی دیگه نمونه ازش ساخته نمیشه و توی کد چند قسمت هست که مختصر توضیح میدمیه قسمت به اسم base url که اون قسمت ابتدایی اسم سایتی که ازش استفاده میکنیم رو قرار میدیمدر قسمت بعدی میگیم که کانورتر ما برای تبدیل کدهای json به جاوا gson باشهو در قسمت بعدی میگیم که از rxjava میخوایم به عنوان کال آداپترمون استفاده کنیم بعدم با create میسازیم نمونمون رو..در قسمت بعدی یک کلاس به اسم MainViewModel میسازیم برای این که از معماری mvvm به طور خیلی ساده استفاده کرده باشیم..بدین صورت:public class MainViewModel { private ApiService apiService; public MainViewModel(ApiService apiService) { this.apiService = apiService; } public Single<List<Car>> getCars() { return apiService.getCars(); } }و میایم در کانستراکتور کلاس ApiService رو بایند میکنیمو در قسمت بعدی یک متد میسازیم که میاد متد getCars که توی Api سرویس ساخته بودیم رو برمیگردونهو در قسمت بعدی کد مربوط به کلاس main رو مینویسم که مربوط میشه به گرفتن نمونه از کلاس viewmodel و ست کردن ریسایکلر ویو که کدنهاییش به این صورت میشه:public class MainActivity extends AppCompatActivity { public RecyclerView recyclerView; private CarAdapter carAdapter; Disposable disposable; private static final String TAG=&quotMainActivity&quot @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MainViewModel mainViewModel = new MainViewModel(ApiServiceSingleton.getInstance()); mainViewModel.getCars().subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new SingleObserver<List<Car>>() { @Override public void onSubscribe(Disposable d) { disposable = d; } @Override public void onSuccess(List<Car> cars) { Log.i(TAG, &quotonSuccess: &quot+cars); recyclerView=findViewById(R.id.recyclerview); recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this, LinearLayoutManager.VERTICAL, false)); carAdapter = new CarAdapter(cars, MainActivity.this); recyclerView.setAdapter(carAdapter); } @Override public void (Throwable e) { Toast.makeText(MainActivity.this, &quoterror&quot + e.toString(), Toast.LENGTH_SHORT).show(); } }); } }توی قسمت بالا ما ابتدا میایم یه نمونه ازMainviewModelمیسازیم که توی کانسراکتورش اون نمونه ای از apiservice که ساخته بودیم رو بهش پاس میدیمدر قسمت بعدی طبق تصویر زیرما باید با استفاده از rxjava لیست ماشین هامون رو بگیریمچندتا مفهوم میبینیم که درموردشون توضیح میدمسینگل Observable توی rx java که دو حالت داره یا به ما نتیجه رو برمیگردونه و یا ارور میدهو Observableمختلفی هست توی rx مثلا Completableکه فرقش با سینگل این هست که فقط میگه کار انجام شد یا نه ..نتیجه ای برنمیگردونه که بعضی جاها کاربرد داره میتونید در مورد تفاوتشون سرچ کنید اولین مفهوم subscribe on هست که میگیم داده هامون رو که میگیری توی ترد جدید بگیر و وقتی گرفتیشون observe کن روی ترد اصلیمون و بعد میایم subscribe میکنیم و new میکنیم single که قبلا داشتیم رو که خودش سه تا متد ایمپلمنت میکنه onsubscribe و onsuccess و که توی متد onsuccess داده ما با موفقیت گرفته میشه و ما توی ریسایکلر ویو نمایش میدیم ،طبق کدبالاو میگیم اگر ارور داد به هر طریقی توی قسمت به ما نمایش بده متن ارور روحالا چه مزیتی داشت استفاده از rxjava:ابتدا باعث شد به راحتی و با دو خط کد بگیم روی ترد دیگه ای اجرا کنه این تسک رو و دیگه از async task استفاده نکردیم و قابلیت های خیلی زیادی داره که میتونیم در موردش سرچ کنیمو فراموش نکنید برای اجرای برنامه توی مانیفست دسترسی اینترنت بدینو usesCleartextTraffic:true رو هم توی مانیفست ست کنید.امیدوارم که مقاله براتون مفید بوده باشهو واقعا توضیح دادن این مطالب توی متن خیلی سخته و به صورت ویدیویی خیلی بهتر میشه توضیح داد درموردش اگرم به نظر شما جایی اشتباه بوده خوشحال میشم بگین که تصحیح کنمو لینک گیت هاب پروژه رو هم قرار میدم که بتونید اجرا کنید و کد کامل رو ببینیدhttps://github.com/iManYarahmadi/CarShowJavaو سعی میکنم طی روزهای آینده کد کاتلینش رو هم بزارم که شما بتونید مقایسه کنیدیاعلی