profile-logo
سورس متریال دیزاین profile UI برای اندروید
۱۳۹۶-۰۷-۲۸
butterknife-logo
همه چیز درباره کتابخانه ButterKnife در اندروید
۱۳۹۶-۰۸-۰۷

اتصال اندروید به وب سرویس Restful با استفاده از Retrofit و Asp.net

اتصال اندروید به وب سرویس Restful با استفاده از Retrofit و Asp.net

اتصال اندروید به وب سرویس Restful با استفاده از Retrofit و Asp.net

اتصال اندروید به وب سرویس Restful با استفاده از Retrofit و Asp.net ، اپلیکیشن های اندرویدی را میتوان با استفاده از چندین کتابخانه ازجمله کتابخانه Volley و Retrofit به سرور متصل کرد و همچنین دستورات سرور نیز میتواند به زبان های مختلفی از جمله Asp.net و php باشد.

همچنین برای دانلود سورس پروژه میتونید به کانال تلگرام ما مراجعه کنید.

اتصال اندروید به وب سرویس Restful با استفاده از Retrofit و Asp.net

در این آموزش ما از Retrofit استفاده میکنیم. اگر آموزش مربوط به کتابخانه Retrofit را مطاله کرده باشید قطعا میدانید که علاوه بر امنیت بالایی که از آن برخوردار است مهم ترین ویژگی آن کم کردن کار برنامه نویس به جهت تبدیل جیسون به مدل POJO میباشد.

(POJO یا Plain Old Java Object در واقع همان کلاس های ساده معمولی جاوا است که از کلاس دیگری ارث نبرده اند و تنها برا ی معرفی یک شی بکار میروند)
در این آموزش قصد داریم که با استفاده از دستورات asp.net در سرور و retrofit در اندروید یک اتصال ایمن بین اپلیکیشن و سرور ایجاد کنیم و اطلاعات را ردوبدل کنیم.

متنها قبل از آموزش بهتر است تا توضیحی کوتاه در ارتباط با REST Api یا RESTful APi داشته باشیم.

REST یا Representational State Transfer که بیشتر در مورد اپلیکیشن های تحت وب استفاده میشود در واقع ساختار یا معماری خاصی برای انتقال داده به سرور استفاده میشود.

این ساختار برروی پروتکل HTTP است و افعال استاندارد این پروتکل که قابل درک برای وب سرویس نیز میباشد شامل GET, POST, PUT, DELETE
API یا Application Programming Interface نیز رابط و واسطی بین اپلیکیشن اندروید و سرور است.
پس در نهایت میتوان گفت RESTful Api درواقع به معنی Api هایی است که از معماری Rest پیروی میکنند.
**اگر میخواید بیشتر با این مبحث آشنا بشید حتما این مقاله رو مطالعه کنید**
خب بریم سراغ پروژه , ما قصد داریم پروژه ای طراحی کنیم که اطلاعات اشخاص شامل نام و فامیل را از کاربر بگیرد و در بانک سرور (SQL Server) ذخیره کند

و همچنین آپشن های حذف و ویرایش و خواندن تمامی اطلاعات از بانک نیز در اختیار کاربر قرار دهد تا به صورت آنلاین با استفاده از api با بانک در ارتباط باشد.
خب پس مراحلی که باید به ترتیب طی بشوند تا به نتیجه برسیم به ترتیب زیر است

۱-ایجاد بانک در SQL Server
۲-ایجاد پروژه asp.net mvc و متصل کردن بانک به پروژه
۳-کدنویسی دستورات asp جهت اعمال تغییرات برروی بانک
۴-ایجاد پروژه اندروید استودیو و متصل کردن آن به سرور
همین! به همین راحتی میتونیم یه اپلیکیشن تحت سرور بسازیم.

۱-ایجاد بانک در SQL Server
خب پس بریم سراغ مرحله اول :
یک دیتابیس ساده با یک جدول به صورت زیر ایجاد میکنیم ما اسم دیتابیس رو T_WebApiApp و اسم جدول هم Person تعیین کردیم

اتصال اندروید به وب سرویس Restful

اتصال اندروید به وب سرویس Restful

۲-ایجاد پروژه asp.net mvc و متصل کردن بانک به پروژه

خب بانک ما آماده ست حالا باید بریم سراغ مرحله دوم کار یعنی پروژه asp.net

پس ویژوال استودیو رو اجرا کنید و یک پروژه جدید از نوع Asp.net Web Application

اتصال اندروید به وب سرویس Restful

اتصال اندروید به وب سرویس Restful

و سپس نوع Web Api رو انتخاب کنید و پروژه تون رو تحویل بگیرید!خب همونطور که میبینید یکسری کنترلر و ویو های آماده هم برای راحتی کار در اختیارتون گذاشته اما ما باهاشون کاری نداریم !

حالا باید بانک رو به پروژه متصل کنیم پس روی فولدر Model کلیک راست میکنیم و گزینه Add و سپس ADO.NET Entity Model رو انتخاب میکنیم

اتصال اندروید به وب سرویس Restful

اتصال اندروید به وب سرویس Restful

خب حالا مراحل ایجاد اتصال رو باید طی کنیم , مرحله اول که اسم مدل رو باید انتخاب کنیم و میریم به مرحله بعد که باید نوع محتوی مدل رو انتخاب کنیم که گزینه EF Designer From Database رو انتخاب میکنیم

اتصال اندروید به وب سرویس Restful

اتصال اندروید به وب سرویس Restful

مرحله بعد باید کانکشن رو ایجاد کنیم پس روی گزینه New Connection کلیک میکنیم و طبق تصویر زیر ServerName رو یک نقطه ( . ) یا اسم کامل سرور قرار میدیم و Refresh رو میزنیم و در قسمت Select or Enter database name دیتابیسی رو که ایجاد کردیم رو انتخاب میکنیم

اتصال اندروید به وب سرویس Restful

اتصال اندروید به وب سرویس Restful

خب بعد از build شدن پروژه میبینیم که دیتابیس به صورت کامل به پروژه اضافه شده پس میتونیم به اطلاعات داخلش دسترسی کامل داشته باشیم هرنوع عملی رو روشون پیاده کنیم.
خب حالا میریم سراغ مرحله سوم و کد نویسی asp برای اعمال تغییرات روی بانک
برای اینکار اول روی فولدر Controllers کلیک راست میکنیم و گزینه Add وبعد گزینه Controller رو کلیک میکنیم. یک اسم مرتبط براش تعیین میکنیم(ما اسمش رو StudentApiController گذاشتیم) و بعد هم کدنویسی رو شروع میکنیم

۳-کدنویسی دستورات asp جهت اعمال تغییرات برروی بانک
قبل از نوشتن هرمتد باید یک شی از مدلی که به پروژه متصل کردیم ایجاد کنیم و با استفاده از آن به بانک دسترسی کامل پیدا کنیم

T_WebApiAppEntities db = new T_WebApiAppEntities();

متد insert برای ایجاد یک رکورد داخل جدول:
ما دونوع متد ایجاد اطلاعات رو داریم یکی از آنها اطلاعات کاربر رو به صورت شی از کلاس میگیرد
و دیگری به صورت فیلد , فیلد اطلاعات را میگیرد و ذخیره میکند که در اندروید تفاوت این دو را در Retrofit مشاهده خواهیم کرد

//اضافه در جدول با دریافت مستقیم شی
public JsonResult insertObjStu(Person person)
{
//اضافه به کلاس و ذخیره در بانک
db.Person.Add(person);
db.SaveChanges();

//آخرین رکورد جدول را میگیرد
Person p = db.Person.ToList().Last();

//ساختار آن را تبدیل به جیسون میکند و به عنوان خروجی برمیگرداند
var jsondata = Json(p, JsonRequestBehavior.AllowGet);
return jsondata;
}

//ایجاد رکورد با دریافت فیلدها به صورت جدا
public JsonResult insertStudent(String name, String family)
{
Person person = new Person()
{
Name = name,
Family = family,
};
//اضافه به کلاس و ذخیره در بانک
db.Person.Add(person);
db.SaveChanges();

//آخرین رکورد جدول را میگیرد
Person stu = db.Person.ToList().Last();

//ساختار آن را تبدیل به جیسون میکند و به عنوان خروجی برمیگرداند
var jsondata = Json(stu, JsonRequestBehavior.AllowGet);
return jsondata;
}

متد ویرایش رکورد براساس id آن

public String updateStudent(Person person)
{
Person selectedStu = db.Person.Find(person.Id);

//اگر مقدار شناسه ارسالی در جدول موجود باشد مقادیر همان رکورد تغییر میباند
if (selectedStu != null)
{
selectedStu.Name = person.Name;
selectedStu.Family = person.Family;

db.Entry(selectedStu).State = EntityState.Modified;
db.SaveChanges();

return "success";
}
else return "failed";

}

متد حذف یک رکورد براساس شناسه ارسالی

public String deleteStudent(int id)
{
String response;
Person selectedStu = db.Person.Find(id);
if (selectedStu != null)
{
db.Person.Remove(selectedStu);
db.SaveChanges();
response = "successful deleted";
}
else
response = "incorrect id";
return response;
}

متد خواندن همه اطلاعات از بانک

public JsonResult getAll()
{
ListPerson list; 
list = db.Person.ToList();
var jsonData = Json(list, JsonRequestBehavior.AllowGet);
return jsonData;

خب دستورات سرور ما هم تمام شد میتونیم هرکدام از این متدهارا در مرورگر کروم یا Postman تست کنیم تا با اطمینان کامل در اپ جاگذاری کنیم برای مثال ما متد insert را در postman بدین صورت تست میکنیم

اتصال اندروید به وب سرویس Restful

اتصال اندروید به وب سرویس Restful

۴-ایجاد پروژه اندروید استودیو و متصل کردن آن به سرور

خب اینم از مرحله سوم حالا باید بریم سراغ مرحله اخر و ایجاد پروژه اندروید و اتصال به سرور :

خب یک پروژه اندروید ایجاد میکنیم در ساده ترین حالت ممکن از نوع Blank Activity و ابتدای کار میریم ولایه activity_main.xml رو طراحی میکنیم

اما قبل از شروع به طراحی چون که ما میخواهیم از Constraint layout برای طراحی خود استفاده کنیم به دلیل مزایای زیاد از جمله کاهش میزان کدنویسی پس باید در گردل برنامه تغییراتی ایجاد کنیم !

ابتدا مطابق تصویر کلاس (build.gradle(project :Retrofit را باز کرده و در قسمت repositories
مقادیر زیر را قرارم میدهیم

maven {
    url 'https://maven.google.com'
}
اتصال اندروید به وب سرویس Restful

اتصال اندروید به وب سرویس Restful

حال کلاس (build.gradle(Module :app را باز کرده مقدار زیر را وارد و سپس sync میکنیم

compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:design:25.3.1'

حال دستورات طراحی فایل xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true"
    tools:context="com.tejariapp.mdn.serverapi.MainActivity">

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.design.widget.TextInputLayout
            android:id="@+id/input_layout_id"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginEnd="24dp"
            android:layout_marginLeft="24dp"
            android:layout_marginRight="24dp"
            android:layout_marginStart="24dp"
            android:layout_marginTop="16dp"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginBottom="8dp"
            app:layout_constraintVertical_bias="0.0"
            app:layout_constraintBottom_toTopOf="@+id/insert">

            <EditText
                android:id="@+id/id"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="right"
                android:hint="@string/id" />

        </android.support.design.widget.TextInputLayout>

        <android.support.design.widget.TextInputLayout
            android:id="@+id/input_layout_name"
            android:layout_width="0dp"
            android:layout_height="63dp"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="24dp"
            android:layout_marginLeft="24dp"
            android:layout_marginRight="24dp"
            android:layout_marginStart="24dp"
            android:layout_marginTop="16dp"
            app:layout_constraintBottom_toTopOf="@+id/insert"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/input_layout_id"
            app:layout_constraintVertical_bias="0.0">

            <EditText
                android:id="@+id/name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="right"
                android:hint="@string/name" />

        </android.support.design.widget.TextInputLayout>


        <android.support.design.widget.TextInputLayout
            android:id="@+id/input_layout_family"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginEnd="24dp"
            android:layout_marginLeft="24dp"
            android:layout_marginRight="24dp"
            android:layout_marginStart="24dp"
            android:layout_marginTop="16dp"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/input_layout_name"
            android:layout_marginBottom="8dp"
            app:layout_constraintBottom_toTopOf="@+id/read"
            app:layout_constraintVertical_bias="0.0">

            <EditText
                android:id="@+id/family"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="right"
                android:hint="@string/family" />

        </android.support.design.widget.TextInputLayout>

        <Button
            android:id="@+id/insert"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onFuncClick"
            android:text="insert"
            app:layout_constraintLeft_toRightOf="@+id/read"
            app:layout_constraintRight_toLeftOf="@+id/delete"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginBottom="8dp" />

        <Button
            android:id="@+id/read"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:onClick="onFuncClick"
            android:text="read"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/insert" />

        <Button
            android:id="@+id/update"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:onClick="onFuncClick"
            android:text="update"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toRightOf="@+id/delete"
            app:layout_constraintRight_toRightOf="parent" />

        <Button
            android:id="@+id/delete"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:onClick="onFuncClick"
            android:text="delete"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toRightOf="@+id/insert"
            app:layout_constraintRight_toLeftOf="@+id/update" />

        <android.support.v7.widget.RecyclerView
            android:id="@+id/personList"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginEnd="8dp"
            android:layout_marginLeft="8dp"
            android:layout_marginRight="8dp"
            android:layout_marginStart="8dp"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            android:layout_marginBottom="24dp"
            app:layout_constraintBottom_toTopOf="@+id/insert"
            android:layout_marginTop="24dp"
            app:layout_constraintTop_toBottomOf="@+id/input_layout_family" />
    </android.support.constraint.ConstraintLayout>
</ScrollView>

خب همانطور که داخل کدهای فایل xml مشاهده میکنید ما یک RecyclerView داریم که باید تمامی اطلاعات را از سرور گرفته و در لیست آیتم به آیتم نمایش دهد

.

پس مشخصا باید این آیتم هارا نیز طراحی کنیم پس لایه جدیدی با عنوان list_item.xml ایجاد میکنیم و به صورت زیر طراحی میکنیم

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <android.support.v7.widget.CardView
        android:id="@+id/personCard"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/id"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="8dp"
                android:layout_marginRight="8dp"
                android:layout_marginTop="8dp"
                android:text="@string/id"
                android:textColor="@android:color/background_dark"
                android:textSize="15sp"
                app:layout_constraintHorizontal_bias="1.0"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="8dp"
                android:layout_marginRight="8dp"
                android:layout_marginTop="8dp"
                android:text="@string/name"
                android:textColor="@android:color/background_dark"
                android:textSize="15sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/id"
                app:layout_constraintVertical_bias="0.0" />

            <TextView
                android:id="@+id/family"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="8dp"
                android:layout_marginLeft="8dp"
                android:layout_marginRight="8dp"
                android:layout_marginTop="8dp"
                android:text="@string/family"
                android:textColor="@android:color/background_dark"
                android:textSize="15sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintHorizontal_bias="1.0"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toLeftOf="@+id/name"
                app:layout_constraintTop_toBottomOf="@+id/id"
                app:layout_constraintVertical_bias="0.0" />
        </android.support.constraint.ConstraintLayout>

    </android.support.v7.widget.CardView>

</android.support.constraint.ConstraintLayout>

خب حالا طراحی های لازم رو انجام دادیم و حالا باید بریم سراغ کدنویسی
قبل شروع به کار چون ما میخواهیم از رتروفیت استفاده کنیم پس نیاز است که کتابخانه های مورد نیاز خود را در گردل کامپایل کنیم پس عبارات زیر را اضافه و sync میکنیم

compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
اگر در sync کردن با مشکل مواجه شدید از فیلتر شکن استفاده کنید.

خب حال باید بریم سراغ اصل ماجرا پس در ابتدای کار باید مدل (POJO)خود را طراحی کنیم . پس با اضافه کردن کلاس جدیدی در پکیج مورد نظر و نام گذاری Person شروع به کدنویسی مدل میکنیم
توجه داشته باشید که مدل باید دقیقا مطابق بانکی باشد که ایجاد کردید

package com.tejariapp.mdn.serverapi;

import com.google.gson.annotations.SerializedName;


/**
 * Created by Marjan on 29/09/2017.
 */

public class Person{

    @SerializedName("Id")
    private int pID;

    @SerializedName("Name")
    private String pName;

    @SerializedName("Family")
    private String pFamily;

    public Person(int id,String pName,String pFamily){
        this.pID=id;
        this.pName=pName;
        this.pFamily=pFamily;
    }

    public Person(String pName,String pFamily){
        this.pName=pName;
        this.pFamily=pFamily;
    }




    public int getpID() {
        return pID;
    }



    public String getpFamily() {
        return pFamily;
    }

    public String getpName() {
        return pName;
    }
}

.

دلیل استفاده از @serializedName قبل از تعریف فیلد ها این است که رتروفیت متوجه شود که فیلد هایی که به صورت جیسون به برنامه ارسال میشود هرکدام متعلق به کدام فیلد مدل ما هستند.
حال زمان آن است که interface با عنوان TaskService که درخواست های مارا همراه با پارامتر های ارسالی و آدرس EndPoint به همراه نوع درخواست ارسال میکند

package com.tejariapp.mdn.serverapi;

import java.util.List;

import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;

/**
 * Created by Marjan on 26/09/2017.
 */

public interface TaskService {
@GET("StudentApi/getAll")
Call<List<Person>> getPersons();

@POST("StudentApi/insertObjStu")
Call<Person> addNewPerson(@Body Person person);

@FormUrlEncoded
@POST("StudentApi/insertObjStu")
Call<Person> addNewPerson(@Field("name") String name
        , @Field("family") String family);

@POST("StudentApi/updateStudent")
Call<ResponseBody> updatePersonInfo(@Body Person person);

@FormUrlEncoded
@POST("StudentApi/deleteStudent")
Call<ResponseBody> deletePeronInfo(@Field("id") int id);
} 

حال کلاس دیگری با عنوان ServiceGenerator ایجاد میکنیم وظیفه این کلاس این است که بیس درخواست به سرور را ایجاد کند

و در ادامه برای ایجاد هر request یا درخواست به سرور تنها یک شی از این کلاس برا ما کافی باشد

package com.tejariapp.mdn.serverapi;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * Created by Marjan on 26/09/2017.
 */

public class ServiceGenerator {

    private static final String URL = "http://10.0.2.2:62813/";
    private TaskService apiService;

    public ServiceGenerator()
    {
        OkHttpClient okClient = new OkHttpClient.Builder()
                .addInterceptor(
                        new Interceptor() {
                            @Override
                            public Response intercept(Interceptor.Chain chain) throws IOException {
                                Request original = chain.request();
                                // Request customization: add request headers
                                Request.Builder requestBuilder = original.newBuilder()
                                        .header("Host", "localhost") 
//cues it want to run as local in local server
                                        .method(original.method(), original.body());

                                Request request = requestBuilder.build();
                                return chain.proceed(request);
                            }
                        })
                .build();

        Gson gson = new GsonBuilder()
                .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
                .create();

        retrofit2.Retrofit restAdapter = new retrofit2.Retrofit.Builder()
                .baseUrl(URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .client(okClient)
                .build();

        apiService = restAdapter.create(TaskService.class);
    }

    public TaskService getService()
    {
        return apiService;
    }
}

در کدهای فوق اگر به URL توجه کنید ما از http://10.0.2.2:62813 استفاده کردیم که برای سرور لوکال برروی دستگاه شبیه ساز خوداندروید استودیو استفاده میشود.

(توضیحات اضافه درارتباط با آدرس url این است که زمانی که ما پروژه asp خود را اجرا میکردیم آدرس api ما بدین صورت بود localhost:62813

و حال در اندروید استودیو باید بجای localhost از آدرس ۱۰٫۰٫۲٫۲ استفاده کنیم و این تنها قابل اجرا برروی شبیه ساز پیشفرض خود اندروید استودیو است)
حال باید Adapter ی سفارشی شده مناسب با داده های مطابق با مدل خود که از سرور به دست ما میرسد ایجاد کنیم

پس کلاس جدیدی ایجاد میکنیم با عنوان CustomAdapter و چنین کدنویسی میکنیم

package com.tejariapp.mdn.serverapi;

import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;

/**
 * Created by Marjan on 15/10/2017.
 */

public class CustomAdapter extends
        RecyclerView.Adapter<CustomAdapter.MyViewHolder> {

    private ArrayList<Person> personsList;
    customAdapterInterface customAdapterInterface;

    public CustomAdapter(ArrayList<Person> personsList,
                         customAdapterInterface customAdapterInterface){
        this.customAdapterInterface=customAdapterInterface;
        this.personsList=personsList;
    }

    public interface customAdapterInterface {
        void onCustomListItemClick(int position);
    }

    @Override
    public CustomAdapter.MyViewHolder onCreateViewHolder(
            ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(parent.getContext())
                .inflate(R.layout.list_item_layout,parent,false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(CustomAdapter.MyViewHolder holder
            , final int position) {
        Person person=personsList.get(position);

        holder.mIDView.setText(String.valueOf(person.getpID()));
        holder.mNameView.setText(person.getpName());
        holder.mFamilyView.setText(person.getpFamily());

        holder.mCardView.setTag(position);

    }

    @Override
    public int getItemCount() {
        return personsList.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {

        private TextView mIDView;
        private TextView mNameView;
        private TextView mFamilyView;
        private CardView mCardView;

        public MyViewHolder(final View itemView) {
            super(itemView);

            mIDView=(TextView)itemView.findViewById(R.id.id);
            mNameView=(TextView)itemView.findViewById(R.id.name);
            mFamilyView=(TextView)itemView.findViewById(R.id.family);
            mCardView=(CardView)itemView.findViewById(R.id.personCard);

            mCardView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int pos=(int)v.getTag();
                    customAdapterInterface.onCustomListItemClick(pos);
                }
            });
        }
    }
}

 

حال تنها زمان آن رسیده که درخواست های خود را به سرور ارسال کنیم و نتیجه ای که این همه براش زحمت کشیدیم رو بگیریم!
پس در ActivityMain که از interface درون CustomAdapter که در بالا ایجاد کردیم implements شده ,

و در متد آن گفته شده که مقادیر فیلدهای ایتم کلیک شده در EditText های مربوطه قرار گیرند

@Override
public void onCustomListItemClick(int position) {
    Person person = personsList.get(position);
    mIdView.setText(String.valueOf(person.getpID()));
    mNameView.setText(person.getpName());
    mFamilyView.setText(person.getpFamily());
}

در بخش onCreate اکتیویتی ,شی هارا تعریف میکنیم

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    serviceGenerator = new ServiceGenerator();

    mIdView = (EditText) findViewById(R.id.id);
    mNameView = (EditText) findViewById(R.id.name);
    mFamilyView = (EditText) findViewById(R.id.family);

    personList=new ArrayList();
    customAdapter = new CustomAdapter(personsList, this);
    recyclerView = (RecyclerView) findViewById(R.id.personList);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(customAdapter);

    read();
}

همانگونه که مشخص است ما از متد read هنگام لود شدن برنامه استفاده کرده ایم

و مشخصا این متد مسئول دریافت تمامی اطلاعات بانک از سرور و جایگذاری در RecyclerView است پس این چنین کدنویسی میشود

public void read() {
    personsList.clear();
    customAdapter.notifyDataSetChanged();
    Call<List<Person>> call = serviceGenerator.
            getService().getPersons();
    call.enqueue(new Callback<List<Person>>() {
        @Override
        public void onResponse(Call<List<Person>> call,
                               Response<List<Person>> response) {
            for (Person person : response.body()) {
                Log.d("PERSON : ", person.getpName() + person.getpFamily());
                personsList.add(person);
                customAdapter.notifyDataSetChanged();
            }
        }

        @Override
        public void onFailure(Call<List<Person>> call,
                              Throwable t) {
            Log.d("ON GET PERSON BUG", t.toString());
        }
    });
}

همانگونه که مشخص است در onResponse یکی یکی شی هایی که براساس کلاس Person دریافت شده اند

را در ارایه اضافه و همچنین اداپتر لیست را از تغییر آگاه میکند.
متد insert در سرور نیز بدین شکل است

private void insert() {
    if (!checkInsertEmpty()) {
        final String name = mNameView.getText().toString().trim();
        final String family = mFamilyView.getText().toString().trim();

        Call<Person> call = serviceGenerator.getService().
                addNewPerson(new Person(name, family));
        call.enqueue(new Callback<Person>() {
            @Override
            public void onResponse(Call<Person> call,
                                   Response<Person> response) {
                Person person=response.body();
                personsList.add(new Person(person.getpID(),person.getpName()
                        ,person.getpFamily()));
                customAdapter.notifyItemInserted(personsList.size() - 1);
                clearTexts();
            }

            @Override
            public void onFailure(Call<Person> call,
                                  Throwable t) {
                Log.d("ON INSERT PERSON BUG",
                        t.toString());
            }
        });
    }
}

متد checkInsertEmpty که مشخصا چک میکند که مقادیر جهت ارسال به سرور و ذخیره در بانک وارد شده اند یاخیر

درصورت درست وارد شدن مقادیر اطلاعات به سرور با متد addNewPerson و ارسال یک شی از مدل Person اقدام به ذخیره میکند

و در صورت گرفتن response از سرور که یک شی از مدل Person (اخرین شی اضافه شده در بانک) است آن را به ارایه اطلاعات اضافه میکند و لیست را هم از این تغییر مطلع میکند

//if return true means it has empty error
private boolean checkInsertEmpty() {
    String name = mNameView.getText().toString().trim();
    String family = mFamilyView.getText().toString().trim();
    boolean flag = false;

    if (name.isEmpty()) {
        mNameView.setError(getResources().getString(R.string.emptyError));
        flag = true;
    }
    if (family.isEmpty()) {
        mFamilyView.setError(getResources().getString(R.string.emptyError));
        flag = true;
    }
    return flag;
}

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

اما شما میتوانید از هردونوع ارسال استفاده کنید چون هردو نوع در اینترفیس تعریف شده اند
متد Update نیز مشابه متد insert است با این تفاوت که درصورت گرفتن response درست از سرور, فیلدی از آرایه لیست با id ارسال شده را تنها تغییر میدهد

public void update() {
    if (!checkInsertEmpty() && !checkIDEmpty()) {

        final int id = Integer.parseInt(mIdView.getText().toString().trim());
        final String name = mNameView.getText().toString().trim();
        final String family = mFamilyView.getText().toString().trim();

        final int pos=getIdPos(id);

        Call<ResponseBody> call = serviceGenerator.getService().
                updatePersonInfo(new Person(id, name, family));

        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call,
                                   Response<ResponseBody> response) {
                String responseResult=null;
                try {
                      responseResult=response.body().string();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                if (responseResult.equals("success")) {
                    personsList.set(pos, new Person(id, name, family));
                    customAdapter.notifyItemChanged(pos);
                    clearTexts();
                }else{
                    mIdView.setError(getResources().getString(R.string.idError));
                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call,
                                  Throwable t) {
                Log.d("on Failer", t.toString());
            }
        });
    }
}
همانگونه که مشخص است از متد getIdPos جهت پیدا کردن مقدار پوزیشن ایتم که براساس مقدار id انجام میشود
private int getIdPos(int id) {
    int pos = -1;
    for (int i = 0; i < personsList.size(); i++) {
        if (personsList.get(i).getpID() == id) {
            pos = i;
            break;
        }
    }
    return pos;
}

وهمچنین متد checkIdEmpty و clearText

//if return true means it has empty error
private boolean checkIDEmpty() {
    String id = String.valueOf(mIdView.getText().toString());
    if (id.isEmpty()) {
        mIdView.setError(getResources().getString(R.string.emptyError));
        return true;
    } else return false;
}

private void clearTexts(){
    mIdView.setText("");
    mNameView.setText("");
    mFamilyView.setText("");
}

و تنها متد حذف یا delete رکورد از سرور باقی مانده که برای این متد نیز تنها کافی است شناسه یا id رکورد را به سرور جهت حذف , ارسال کنیم و درصورت دریافت response صحیح از سرور آن آیتم را از لیست نیز حذف کنیم

public void delete() {
    if (!checkIDEmpty()) {
        final int id = Integer.parseInt(mIdView.getText().toString().trim());
        final int pos=getIdPos(id);

        Call<ResponseBody> call = serviceGenerator.
                getService().deletePeronInfo(id);
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call,
                                   Response<ResponseBody> response) {
                try {
                    String responseResult = response.body().
                            string();
                    if (responseResult.equals("successful deleted")) {
                        personsList.remove(pos);
                        customAdapter.notifyDataSetChanged();
                        clearTexts();
                    }else {
                        mIdView.setError(getResources().
                                getString(R.string.idError));
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call,
                                  Throwable t) {
                Log.d("DELETE on Failure", t.toString());
            }
        });
    }
}

پروژه به اتمام رسید. حالا میتونید برنامه رو روی یک شبیه ساز پیشفرض اندرویداستودیو اجرا کنید و بدین صورت نتیجه بگیرید

اتصال اندروید به وب سرویس Restful

اتصال اندروید به وب سرویس Restful

برای دانلود سورس پروژه میتونید به کانال تلگرام ما مراجعه کنید

موفق و خوشحال باشید 🙂

 

2 دیدگاه ها

  1. علیرضا گفت:

    سلام . اموزش جامع و کاملی ارائه دادید . خیلی ممنون
    یه سوال .اگر دراین بین کسی این اطلاعات رو که در قالب http داره ارسال و دریافت میشه ، توسط ابزار های شبکه شنود کنه ، و تمامی اطلاعاتی از قبل api , token و… رو بدست بیاره ، باید چه کاری انجام داد ؟

    • مرجان داودی نژاد گفت:

      سلام
      اگر در راه رسیدن به مقصد کسی موفق به شنود شه که میتونید با الگوریتم JWT تاحد زیادی مانع درک مقادیر برای اون شخص شید
      اما اگر زمانی که به مقصد رسید هم بخوایم مانع شنود شیم میتونیم از الگوریتم های دیگه رمز نگاری برای رمز کردن اطلاعات مثل AES , RSA , .. که یک کلید توافقی بین اپ اندروید و سرور دارند ٫ استفاده کنیم

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *