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

با سلام و وقت بخیر خدمت شما دوستان عزیز

توی این مقاله قصد داریم که یک سری اطلاعات رو از api بگیریم و توی ریسایکلر ویو نمایش بدیم

حالا از چه مفاهیم و کتابخانه هایی توی پروژه میخوایم استفاده کنیم؟mvvm,rxjava,retrofit

ابتدا لازم میدونم نکته ای رو عرض کنم :

این مثال ساده ترین و ابتدایی ترین شکل استفاده از معماری mvvm و rx هست واین مقاله مناسب افرادی هست که اشنایی ابتدایی در مورد این موارد دارن و میخوان طرز استفاده از این موارد رو یاد بگیرن

برنامه ما بدین شکله:

خوب بریم برای شروع :

ابتدا برای این که ما بتونیم اطلاعاتمون رو توی برنامه بگیریم نیاز به یک api داریم که سایت های زیادی برای ساخت api هست و ما اینجا از سایت https://www.mocky.io/ استفاده میکنیم و ساختار json خودمون رو طبق تصویر زیر میسازیم.

محیط سایت:

روی generate کلیک میکنیم و لینک رو به ما میده
روی 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

و سعی میکنم طی روزهای آینده کد کاتلینش رو هم بزارم که شما بتونید مقایسه کنید

یاعلی