---
title: "استدعاء خدمة ويب باستخدام Android Architecture Component"
description: "تركّز هذه المقالة على استدعاء خدمة ويب (web service) باستخدام Android Architecture Component، مع التعامل مع الأخطاء وحالة التحميل."
canonical: https://oozou.com/ar/blog/calling-web-service-with-android-architecture-component-11
locale: ar
date: 2017-07-17
author: "OOZOU Team"
---

# استدعاء خدمة ويب باستخدام Android Architecture Component

> تركّز هذه المقالة على استدعاء خدمة ويب (web service) باستخدام Android Architecture Component.

Published: 2017-07-17 · Author: OOZOU Team (Engineering) · 6 min read

# مقدمة
كان **Android Architecture Component** من أكثر المواضيع التي أثارت اهتمامي (وأظن أنني لست الوحيد، بل كثير من مطوّري Android). توجد اليوم بنى برمجية كثيرة مثل MVP وMVVM وغيرها. لكن Google قدّمت هذه المرة دليلًا رسميًا يساعدكم على تصميم بنية تطبيق Android، ولذلك يستحق التجربة فعلًا. سأركّز في هذه المقالة على استدعاء خدمة ويب (web service) باستخدام هذه البنية.
الكود المصدري لهذا المشروع موجود على حسابي في [Github](https://github.com/BenBoonya/calling-webservice).
قبل البدء
**_تركّز هذه المقالة على استدعاء خدمة ويب باستخدام_ **_Android Architecture Component._** _ولن تتناول أساسيات_ **_Kotlin_** _أو المكتبات الأخرى مثل_ **_Dagger وRetrofit_** _.
_

# كيف تبدو هذه البنية؟
الصورة أعلاه مأخوذة من موقع Android الرسمي. وأود تقسيمها إلى طبقتين.

**طبقة البيانات (Data Layer)**

1. **Repository**: يتواصل مع مصدر البيانات المحلي (قاعدة البيانات) ومصدر البيانات البعيد (خدمة الويب)
2. **مصدر البيانات البعيد:** يتصل بخدمة الويب

**طبقة العرض (Presentation Layer)**

1. **ViewModel:** يتواصل مع الـ **Repository** ثم يحدّث البيانات في الواجهة (fragment/activity) تبعًا لذلك
2. **Fragment/Activity:** كل ما يخص الواجهة (تجهيزها وتحديثها)

**التطبيق التجريبي**

يجلب التطبيق التجريبي البيانات من [https://swapi.co](https://swapi.co/)، ويعرض قائمة بالأنواع (species) في سلسلة Star War.

سأتناول في هذه المقالة أيضًا الإجراءات الشائعة عند التعامل مع خدمة الويب، ومنها الحصول على النتيجة ومعالجة الأخطاء وعرض واجهة التحميل. يمكنكم نسخ الكود من [هنا](https://github.com/BenBoonya/calling-webservice) ومتابعته مع هذه المقالة.

# طبقة البيانات

**مصدر البيانات البعيد**

نستخدم **Retrofit** مصدرًا للبيانات البعيدة للاتصال بخدمة الويب. الكود أدناه هو الـ interface الخاص بالـ API. إذا كنتم تستخدمون **Retrofit**، فهو مألوف لكم بالتأكيد.

**_حزمة الكود المصدري_**_: boonya.ben.callingwebservice.api
_

```kotlin
interface Swapi {
    @GET("species/")
    fun getSpecies(): Call<SpeciesList>
}
```

يحتوي على endpoint واحد فقط من أجل التبسيط.
**Repository**

هنا يحدث استدعاء خدمة الويب، ويشمل أيضًا استقبال البيانات والأخطاء منها. لنلقِ نظرة على الكود أدناه.

**_حزمة الكود المصدري:_** _boonya.ben.callingwebservice.species_

```kotlin
class SpeciesRepositoryImpl(val apiService: Swapi) : SpeciesRepository {

    override fun getSpecies(successHandler: (List<Species>?) -> Unit, failureHandler: (Throwable?) -> Unit) {
        apiService.getSpecies().enqueue(object : Callback<SpeciesList> {
            override fun onResponse(call: Call<SpeciesList>?, response: Response<SpeciesList>?) {
                response?.body()?.let {
                    successHandler(it.speciesList)
                }
            }

            override fun onFailure(call: Call<SpeciesList>?, t: Throwable?) {
                failureHandler(t)
            }
        })
    }
}
```

كما ترون، الدالة **getSpecies** هي **دالة من رتبة أعلى (higher-order function)**_الدالة من رتبة أعلى_** _هي دالة تستقبل دوالًا كمعاملات أو تُرجع دالة._
تستقبل **getSpecies** دالتين كوسائط

1. **_(successHandler: (List\<Species\>?) -> Unit )_** تُستدعى هذه الدالة عندما ينجح استدعاء خدمة الويب. وتستقبل **_List\<Species\>_**، أي بيانات الاستجابة من خدمة الويب، كوسيط. وسترون لاحقًا كيف يصل الـ **ViewModel**إلى هذه البيانات
2. **_(failureHandler: (Throwable?) -> Unit)_** تُستدعى هذه الدالة عندما يفشل استدعاء خدمة الويب. وتستقبل أيضًا **Throwable** كوسيط، وسنتعامل معه في الـ **ViewModel.**

بوجود الـ **Repository،** لن تحتاج طبقة العرض (Activity/Fragment وViewModel) إلى الوصول مباشرة إلى المصادر البعيدة. لذلك إذا أردنا استخدام مصدر بعيد آخر بدلًا من **Retrofit**، يمكننا فعل ذلك دون تغيير الكود في طبقة العرض.

# طبقة العرض

قبل أن نتابع، دعوني أعرّفكم بمكوّن جديد آخر اسمه **LiveData.**

إليكم شرحًا موجزًا.

**_LiveData_** _يجعل صنف النموذج العادي قابلًا للمراقبة (observable)._**_MutableLiveData_** _هو تطبيق لـ_ **_LiveData_**_._ **_MutableLiveData_** _يمكنه استخدام دالة اسمها_ **_setValue_** _لإسناد قيمة جديدة إلى الـ_ **_LiveData.
_**ViewModel**

الـ **ViewModel** مكوّن جديد قُدّم ضمن Android Architecture Component. فبدلًا من وضع كل شيء في **Activity/Fragment،** يمكننا فصل الكود غير المرتبط بالواجهة ونقله إلى **ViewModel.** إضافة إلى ذلك، يبقى الـ **ViewModel** قائمًا عند حدوث _تغيير في الإعدادات (configuration change) مثل تدوير الشاشة_. ففي العادة يُعاد إنشاء **Activity/Fragment** وكل ما بداخلهما عند حدوث _تغيير في الإعدادات._ لكن باستخدام **ViewModel،** يمكن إعادة استخدام النسخة الموجودة دون إنشاء نسخة جديدة.

لنلقِ نظرة على مثال.
**_حزمة الكود المصدري:_** _boonya.ben.callingwebservice.species_

```kotlin
class SpeciesListViewModel : ViewModel() {

    @Inject
    lateinit var repository: SpeciesRepository

    var isLoading = MutableLiveData<Boolean>()

    var apiError = MutableLiveData<Throwable>()

    var speciesResponse = MutableLiveData<List<Species>>()

    fun getSpecies() {
        isLoading.value = true
        repository.getSpecies(
                {
                    speciesResponse.value = it
                    isLoading.value = false
                },

                {
                    apiError.value = it
                    isLoading.value = false
                })
    }
    //ignore other code
}
```

جهّزنا ثلاثة متغيرات من نوع **MutableLiveData** لتُراقَب من الـ activity/fragment.

1. **_isLoading:_** تُضبط على **true** عند بدء استدعاء خدمة الويب، وتتغير إلى **false** عند نجاح الاستدعاء أو فشله.
2. **_apiError_**: إذا حدث خطأ في استدعاء خدمة الويب، تُسند بيانات الـ **Throwable** إلى هذا المتغير.
3. **_speciesResponse:_** عند الحصول على البيانات من خدمة الويب بنجاح، تُسند بيانات الاستجابة إلى هذا المتغير.

**استدعاء الـ Repository من الـ ViewModel**

لكي يستدعي الـ **Repository** خدمة الويب، علينا تشغيل الدالة **getSpecies.** وتستدعي هذه الدالة بدورها دالة أخرى في الـ **Repository** شرحتها في قسم **Repository**.

كما ترون، تستقبل **repository.getSpecies(…. , ….)** دالتين كوسائط. وإليكم شرح كل وسيط.

1. الوسيط الأول هو الدالة التي تُستدعى عند نجاح استدعاء خدمة الويب. وتعود بيانات الاستجابة من خدمة الويب، ويمكن الإشارة إليها بـ **it**2.  أما دالة الوسيط الثاني فتُستدعى عند فشل استدعاء خدمة الويب، ويمكن الإشارة إلى بيانات الـ **Throwable** بـ **it**.

**Activity**

لن تحتوي الـ activity إلا على الكود المرتبط بالواجهة. وإليكم كود الـ Activity.

```kotlin
class SpeciesActivity : AppCompatActivity() {

    private val registry = LifecycleRegistry(this)
    private lateinit var viewModel: SpeciesListViewModel
    private lateinit var adapter: SpeciesListAdapter

    override fun getLifecycle(): LifecycleRegistry = registry

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_species)

        //set up ui
        viewModel = createViewModel()
        attachObserver()
        viewModel.getSpecies()
    }

    private fun attachObserver() {
        viewModel.isLoading.observe(this, Observer<Boolean> {
            it?.let { showLoadingDialog(it) }
        })
        viewModel.apiError.observe(this, Observer<Throwable> {
            it?.let { Snackbar.make(rvSpecies, it.localizedMessage, Snackbar.LENGTH_LONG).show() }
        })
        viewModel.speciesResponse.observe(this, Observer<List<Species>> {
            it?.let { adapter.notifyDataSetChanged() }
        })
    }

    private fun createViewModel(): SpeciesListViewModel =
            ViewModelProviders.of(this).get(SpeciesListViewModel::class.java).also {
                ComponentInjector.component.inject(it)
            }

    private fun showLoadingDialog(show: Boolean) {
        if (show) progressBar.visibility = View.VISIBLE else progressBar.visibility = View.GONE
    }
}
```

لكي تعمل الـ activity مع Android Architecture Component، يجب أن تطبّق **LifecycleRegistryOwner** وأن تعيد تعريف الدالة **getLifeCycle()** كما في الكود أعلاه.

لنلقِ نظرة أقرب على الدالة **attachObserver()**. ستراقب هذه الـ activity ثلاثة متغيرات **MutableLiveData** في الـ **ViewModel.**

1. **_viewModel.isLoading:_** عند حدوث تغيير في هذا المتغير، تُستدعى الدالة **showLoadingDialog** لإظهار **progressBar** أو إخفائه2.  **_viewModel.apiError:_** يظهر Snackbar برسالة الخطأ عند حدوث تغيير في هذا المتغير.
3. **_viewModel.speciesResponse:_** يُبلَّغ الـ recycler adapter عند إسناد بيانات إلى هذه القيمة

# الخلاصة
أحب Android Architecture Component كثيرًا. فهو لا يتيح لكم كتابة كود أسهل في الاختبار والصيانة فحسب، بل يحل أيضًا مشكلات يعاني منها معظم مطوّري Android، مثل التعامل مع تغيير الإعدادات. وإذا كنتم على وشك بدء مشروع جديد، فهذه البنية تستحق التجربة فعلًا.

**المرجع**

\- https://blacklenspub.com/พอกันทีกับ-god-activity-มาวางโครงดีๆ-ด้วย-architecture-components-d89302734ab8

---

Contact OOZOU: hello@oozou.com · +66 2544 0000 · https://oozou.com/ar/contact

More for AI agents: https://oozou.com/llms.txt · every page has a markdown twin at its URL plus ".md"
