قسمت های دیگر این مطلب:

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

از کاربردهای این بخش به موارد زیر اشاره می‌کنیم:

  • تولید برنامه‌هایی که اساسا کلیه اطلاعات خود را از سرور دریافت می‌کنند.
  • تولید برنامه‌هایی که برای بروز رسانی خود نیازمند اطلاعات سمت سرور هستند.

برای کار با سرور باید برنامه سمت سرور خود را به یک زبان سروری مانند PHP بنویسید. لینک دوره آموزش برنامه نویسی PHP هیتوس را ببینید.

بهترین راه برای دریافت اطلاعات از سرور لوکال دریافت آن‌ها در قالب JSON است. یک فایل با نام getposts.php در مسیر لوکال سرور خود ایجاد می‌کنیم. در زیر یک سورس ساده به زبان PHP می‌بینید که یک آرایه در قالب JSON ایجاد می‌کند:

<?php
$array= array(
                array(
                    "عنوان اولین مطلب",
                    "متن کامل اولین مطلب را در اینجا ببینید."),
                array(
                    "عنوان دومین مطلب",
                    "متن کامل دومین مطلب را در اینجا ببینید."),
                array(
                    "عنوان سومین مطلب",
                    "متن کامل سومین مطلب را در اینجا ببینید.")
    );
echo json_encode($array);
?>

بدلیل ارتباط برنامه اندروید با سرور، باید سطح دسترسی ارتباط به اینترنت را به فایل Manifest نرم افزار خود اضافه کنید. دستور زیر را بالای تگ application اضافه می‌کنیم:

<uses-permission android:name="android.permission.INTERNET"/>

حال نوبت پیاده سازی کدهای سمت اندروید برای دریافت اطلاعات از سرور رسیده است. برای این کار می‌توانید از کلاس‌ها و متدهای خود اندروید استفاده کنید ولی اکیدا توصیه می‌شود از کتابخانه مخصوصی به نام volley استفاده کنید که این کار را برای شما راحت و قدرتمند می‌کند.

علاوه بر این، کتابخانه volley برخی از عملیات مهم نظیر کش کردن را به صورت خودکار انجام می‌دهد. این کلاس استفاده از منابع را بهینه کرده و توسط گوگل پیاده سازی شده است.

برای اضافه کردن کتابخانه volley در فایل build.gradle (Module: app) به dependencies‌ها کتابخانه زیر را اضافه کنید:

compile 'com.android.volley:volley:1.0.0'

در برنامه Android خود در کلاس MainActivity دستورات زیر را ایجاد می‌کنیم:

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

        JsonArrayRequest request = new JsonArrayRequest(
                Request.Method.GET,
                "http://192.168.1.2/getposts.php",
                null,
                new Response.Listener<JSONArray>() {
                    @Override
                    public void onResponse(JSONArray response) {
                        Log.i("Get_Response: ", response.toString());
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.i("Get_Error: ", error.toString());
                    }
        });
        request.setRetryPolicy(new DefaultRetryPolicy(8000, 1, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
    }

در سطر ششم یک JsonArrayRequest ایجاد می‌کنیم که 5 ورودی دارد که در پایین آن‌ها را شرح می‌دهیم:

ورودی اول: نوع درخواست را مشخص می‌کند که در اینجا از نوع Request و از متد GET استفاده شده است.

ورودی دوم: آدرس سمت سرور که باید اطلاعات را از آن دریافت کنیم. توجه کنید که نمی‌توانید از آدرس‌هایی مانند localhost و یا 127.0.0.1 استفاده کنید و باید IP شبکه خود را در این مکان قرار دهید. برای یافتن IP داخل شبکه به این مطلب مراجعه کنید.

ورودی سوم: که مشخص کننده پارامترهای ورودی است که ما در این جا null را وارد می‌کنیم.

ورودی چهارم: این ورودی که Response.Listener است دستوراتی را شامل می‌شود که در صورت انجام موفقیت آمیز عملیات باید اجرا شوند. در سطر 13 یک لاگ ایجاد می‌کنیم که مقدار دریافتی که از نوع JSONArray است را با toString به رشته تبدیل و در کنسول اندروید استودیو نمایش دهد.

ورودی پنجم: ورودی Response.ErrorListener دستوراتی را شامل می‌شود که در صورت شکست عملیات باید اجرا شوند. در سطر 18 یک لاگ ایجاد می‌کنیم که مقدار دریافتی که از نوع VolleyError است را با toString به رشته تبدیل و در کنسول اندروید استودیو نمایش دهد.

در سطر 21 سورس فوق قصد داریم مقادیر پیش فرض JsonArrayRequest را مطابق نیاز برنامه تغییر دهیم. لازم به ذکر است که این کار الزمانی نیست.

متد setRetryPolicy یک ورودی دارد که خود این ورودی سه ورودی مخصوص به خود را داراست که ورودی‌ها به شرح زیر هستند:

ورودی اول: زمان لازم برای هر درخواست را مشخص می‌کند که در این جا 8000 میلی ثانیه معادل هشت ثانیه وارد شده است.

ورودی دوم: تعداد دفعات تلاش در صورت شکست عملیات که در این جا یک وارد شده است.

ورودی سوم: مقدار زمان لازم برای درخواست بعد از شکست اولین درخواست که می‌توانید این مقدار را بر حسب میلی ثانیه وارد کنید ولی در این جا از DefaultRetryPolicy.DEFAULT_BACKOFF_MULT استفاده می‌کنیم تا مقدار پیش فرض Volley استفاده شود.

برای عملیاتی شدن درخواست فوق باید یک RequestQueue ایجاد کنیم. این کد به شرح زیر است که آن را در انتهای کدهای فوق ایجاد می‌کنیم:

        RequestQueue requestQueue=Volley.newRequestQueue(this);
        requestQueue.add(request);

در سطر اول یک RequestQueue ایجاد کردیم که مقدار ورودی newRequestQueue متعلق به Context است.

در سطر دوم request ایجاد شده در بالا را با استفاده از add به پردازش RequestQueue کتابخانه Volley اضافه می‌کنیم.

در اجرای کدهای فوق اگر مشکلی پیش نیاید و از سمت سرور مشکلی وجود نداشته باشد باید بتوانید رشته Json را در کنسول مشاهده کنید:

مشاهده نتیجه اجرای کد

پردازش اطلاعات Json

فرایند پردازش رشته Json باید در متد onResponse صورت بپذیرد. متغیر response حاوی رشته Json است که برای پردازش آن باید یک شی به نام JSONArray ایجاد کرده و با دستور getJSONArray شروع به دریافت آرایه‌ها کنیم. متد onResponse را مانند زیر باز نویسی می‌کنیم:

public void onResponse(JSONArray response) {
    try {
        for (int i = 0; i < response.length(); i++) {
            JSONArray postArray = response.getJSONArray(i);
            Toast.makeText(MainActivity.this, postArray.getString(0), Toast.LENGTH_SHORT).show();
            Toast.makeText(MainActivity.this, postArray.getString(1), Toast.LENGTH_SHORT).show();
        }
    } catch (JSONException e) {
        Toast.makeText(MainActivity.this, "خطا در دریافت اطلاعات", Toast.LENGTH_SHORT).show();
        e.printStackTrace();
    }
}

دلیل استفاده از try catch در مثال بالا وجود getJSONArray است و این متد نیازمند یک try catch می‌باشد.

آرایه اصلی که ما در این مثال پیاده سازی کردیم یک آرایه با سه آرایه درون خودش بود.

در سطر سوم با ایجاد یک حلقه for کلیه آرایه‌های درون response را پردازش می‌کنیم. با response.length() می توان تعداد آرایه‌های موجود در آرایه اصلی را پیدا کرد.

در سطر چهارم یک شی از نوع JSONArray ایجاد کردیم و درون آن یک آرایه با اندیس i را قرار دادیم.

در سطر پنجم و ششم آیتم‌های شماره صفر و یک آرایه دریافت شده در سطر چهار را با Toast نمایش می‌دهیم.

ایجاد کلاس APIGettingPosts

در بالا به سادگی پست‌ها را از یک سرور دریافت و به صورت Toast در برنامه نمایش دادیم. کاری که در بالا انجام دادیم با وجود این که درست اجرا شد ولی از نظر منطق برنامه نویسی کار صحیحی نبود.

از نظر برنامه نویسان خبره همیشه باید به هر کلاس تنها یک عملیات مشخص را اختصاص دهید. مثلا کلاس‌های Activity اندروید مختص مقدار دهی و یا نمایش لایه‌ها هستند و ما بقی فرایندها باید در کلاس‌های دیگر انجام پذیرد.

از این رو تصمیم داریم کلاس APIGettingPosts را برای جمع بندی فعالیت‌های دریافت اطلاعات از سمت سرور ایجاد کنیم. این کلاس را ایجاد کرده و سورس زیر را در آن درج می‌کنیم:

public class APIGettingPosts {

    private Context context;

    public APIGettingPosts(Context context){
        this.context= context;
    }

    public void getPost(final OnPostsReceived onPostsReceived){
        JsonArrayRequest request = new JsonArrayRequest(
                Request.Method.GET,
                "http://192.168.1.2/getposts.php",
                null,
                new Response.Listener<JSONArray>() {
                    @Override
                    public void onResponse(JSONArray response) {
                        onPostsReceived.onReceived(ParsingPostJSON(response));
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.i("Get_Error: ", error.toString());
                onPostsReceived.onReceived(null);
            }
        });
        request.setRetryPolicy(new DefaultRetryPolicy(8000, 1, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));

        RequestQueue requestQueue= Volley.newRequestQueue(context);
        requestQueue.add(request);
    }

    private Posts ParsingPostJSON(JSONArray response){
        try {
            JSONArray postArray= response.getJSONArray(0);
            Posts posts= new Posts();
            posts.setTitle(postArray.getString(0));
            posts.setFullPost(postArray.getString(1));
            return posts;
        } catch (JSONException e) {
            e.printStackTrace();
            return null;
        }
    }

    public interface OnPostsReceived{
        void onReceived(Posts posts);
    }
}

از آن جا که Context باید از کلاس MainAcitivty فراخوانی شود، بنابراین در خطوط 3 تا 7 دستوراتی را اضافه کردیم تا هنگام فراخوانی این کلاس در کلاس MainActivity متغیر context از سمت MainActivity مقدار دهی شود.

اگر به سطر‌های 32 تا 42 دقت کنید می‌بینید یک متد به نام ParsingPostJSON ایجاد کردیم که قصد داریم در این متد کلیه اعمال پردازش JSON را انجام دهیم. خروجی این متد از نوع Posts و ورودی آن یک JSONArray است.

دو return برای این متد متصور است، در صورت موفقیت آمیز بودن عملیات همان متغیر از نوع Posts و با نام posts به خروجی فرستاده می‌شود و اگر عملیات موفقیت آمیز نبود null به خروجی باز می‌گردد.

در سطرهای 45 تا 47 یک interface به نام OnPostsReceived ایجاد کردیم. این متد بسیار مهم است و با استفاده از آن فرایند اتمام عملیات را در این کلاس و کلاس MainAcitivty مدیریت می‌کنیم.

در سطر 9 متد getPosts را ایجاد کردیم که خروجی خاصی ندارد و از نوع void است. ورودی این متد OnPostsReceived است.

در سطرهای 10 تا 29 سورس دریافت Json از سرور را از کلاس MainActivity به این جا منتقل کردیم.

در سطر 17 با دستور onPostsReceived.onReceived و ورودی ParsingPostJSON قصد داریم نتیجه پردازش رشته JSON را به onPostsReceived بفرستیم.

در سطر 39 ورودی onPostsReceived.onReceived را بدلیل شکسته شدن عملیات برابر null قرار می‌دهیم.

در لایه نمایش activity_main یک کلید با شناسه button ایجاد می‌کنیم.

کلاس MainActivity را مانند زیر ویرایش می‌کنیم:

public class MainActivity extends AppCompatActivity implements APIGettingPosts.OnPostsReceived{
    private APIGettingPosts apiGettingPosts;
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        apiGettingPosts= new APIGettingPosts(this);

        Button button = (Button) findViewById(R.id.button);

        button.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view) {
                apiGettingPosts.getPost(MainActivity.this);
            }
        });
    }

    @Override
    public void onReceived(Posts posts){
        if(posts != null){
            Toast.makeText(this, posts.getTitle(), Toast.LENGTH_SHORT).show();
            Toast.makeText(this, posts.getFullPost(), Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(this, "خطا در دریافت اطلاعات", Toast.LENGTH_LONG).show();
        }
    }
}

در سطر 1 می‌بینیم کلاس MainActivity متد OnPostsReceived کلاس APIGettingPosts را implement کرده است.

در سطرهای 20 تا 28 متد onReceived را Override کرده و ورودی آن را Posts تعیین می‌کنیم. وظیفه این متد این است که بررسی کند که مقدار posts دریافت شده از کلاس APIGettingPosts برابر null نباشد و پست اول را به نمایش در بیاید و اگر null بود خروجی "خطا در دریافت اطلاعات" نمایان خواهد شد.

در سطر 2 یک متغیر از کلاس APIGettingPosts ایجاد می‌کنیم.

در سطر 8 یک شی از کلاس APIGettingPosts ایجاد و در ورودی this را به عنوان context به سازنده کلاس APIGettingPosts ارسال کردیم.

در سطر‌های 10 تا 17 button موجود در لایه نمایش را صدا زده و با setOnClickListener آن را آماده اجرای عملیات می‌کنیم.

در سطر 15 متد getPosts را از کلاس apiGettingPosts با ورودی MainActivity.this درخواست دادیم. این MainActivity.this به متد onPostsReceived اشاره دارد، چون ورودی متد getPosts در کلاس APIGettingPosts برابر onPostsReceived است.

اگر برنامه فوق را اجرا کنید خواهید دید رشته مربوط به پست اول در Toast نمایش داده می‌شود:

آموزش ایجاد api اندروید از سمت سایت هیتوس

در بالا سورسی پیاده کردیم که تنها یک پست را که معادل آرایه اول بود برای ما نمایش می‌داد. در قسمت بعد قصد داریم یک API پیچیده با قابلیت‌های جالب را برای شما پیاده سازی کنیم.