ذخیره اطلاعات با SharedPreferences در اندروید

به نام خدا. در جلسه گذشته به بررسی دیتابیس در اندروید پرداختیم و با نحوه ذخیره اطلاعات در دیتابیس آشنا شدیم. اما در اندروید روش های دیگری هم برای ذخیره اطلاعات درنظر گرفته شده که در این جلسه به معرفی SharedPreferences می پردازیم. اینکه هرکدام از روش های ذخیره سازی در چه مواردی کاربرد دارد عمدتا به سلیقه توسعه دهنده بستگی دارد اما عموما در کاربردهای متفاوت، با توجه به ویژگی آن، یک گزینه نسبت به دیگری رایج تر بوده و ارجح است. به عنوان مثال برای ذخیره تنظیمات اپلیکیشن (فونت و سایز برنامه، حالت شب/روز، رنگ پس زمینه و…)، ثبت اطلاعات کاربر، نحوه نمایش و چیدمان لیست ها، فعال یا غیرفعال بودن دریافت نوتیفیکشن ها، و به طور کلی داده های با حجم کم، معمولا از SharedPreferences استفاده می شود. SharedPreferences به ما اجازه میدهد تا اطلاعات را با فرمت Key/Value (کلید/مقدار) ذخیره و نگهداری کنیم. این اطلاعات با فرمت xml ذخیره شده و تا زمانی که اپلیکیشن از روی سیستم عامل حذف نشده و یا کاربر به صورت دستی در قسمت تنظیمات برنامه ها، Data ی اپلیکیشن را حذف نکند، باقی می ماند.

متدهای SharedPreferences

برای دستیابی به Preference ها به سه متد دسترسی داریم:
– getPreferences()
– getSharedPreferences()
– getDefaultSharedPreferences()

در این مبحث من از متد getSharedPreferences() استفاده می کنم. قبل از ادامه توضیحات، یک پروژه جدید می سازم که نام آن را Preference گذاشته ام. در ابتدا ساده ترین حالت را در نظر گرفته و تنها یک فیلد ورودی متن (EditText) و یک Button به اکتیویتی activity_main.xml اضافه می کنم تا کاربر نام خود را وارد کرده و ذخیره نماید:

activity_main.xml

در قدم بعد، این دو Widget را در MainActivity.java تعریف می کنم.

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    android:paddingBottom="16dp"
    tools:context="ir.android_studio.preference.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <EditText
            android:id="@+id/edt_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:hint="Name"
            android:inputType="textPersonName" />

        <Button
            android:id="@+id/btn_save"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Save" />
    </LinearLayout>
</RelativeLayout>

MainActivity.java:

package ir.android_studio.preference;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {

    EditText edtName;
    Button btnSave;

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

        edtName = (EditText) findViewById(R.id.edt_name);
        btnSave = (Button) findViewById(R.id.btn_save);

    }
}

حالا از اینترفیس SharedPreferences یک نمونه می سازم. من نام shPref را برای این نمونه در نظر گرفتم:

SharedPreferences shPref;

در ادامه shPref را مساوی متد getSharedPreferences() قرار می دهم:

متد getSharedPreferences

ملاحظه می کنید این متد دو پارامتر ورودی می گیرد. اولی name و دومی mode. پارامتر نخست مربوط به نام فایلی است که برای ذخیره سازی داده ها استفاده می شود و اختیاری است. من MyPref وارد می کنم (یک متغیر از نوع String با نام MyPref و مقدار MyPrefers ساخته ام. برای موارد بعدی نیز متغیر تعریف می کنم تا از وارد کردن دستی نامها جلوگیری شود که در نهایت منجر به افزایش سرعت و کاهش خطا می شود). پارامتر دوم مربوط به mode (روش) عملیاتی است.

انواع MODE ها

انواع مد ها را بررسی می کنیم:
– MODE_PRIVATE: مد پیش فرض که فایل محتوی داده ها تنها با فراخوانی اپلیکیشن در دسترس خواهد بود.
– MODE_WORLD_READABLE: با این مد سایر اپلیکیشن ها نیز می توانند داده ها را بخوانند که از حیث امنیت ممکن است مشکل ساز شود. (این مد از API 17 به بعد deprecate (منقضی) شده است)
– MODE_WORLD_WRITEABLE: این مد به سایر اپلیکیشن ها اجازه می دهد تا اطلاعات Preference برنامه ما را ویرایش کنند که مانند مورد قبل به لحاظ امنیتی مشکل ایجاد نموده و از API 17 به بعد deprecate شده است.
– MODE_MULTI_PROCESS: این مد تعدیل preferences را چک خواهد کرد حتی اگر Shared Preference قبلا اجرا شده باشد. (این مد از API 23 به بعد deprecate شده است)
– MODE_APPEND: به واسطه این مد، preferences های جدید با preferences های قبلی ترکیب خواهد شد.
ما از MODE_PRIVATE استفاده می کنیم:

shPref = getSharedPreferences("MyPref", Context.MODE_PRIVATE);

خواسته ما از برنامه این است که پس از وارد کردن نام و لمس کلید Save توسط کاربر، نام وارده ذخیره شده و با بستن و اجرای مجدد اپلیکیشن، اطلاعات وارد شده نمایش داده شود. ابتدا متد مربوط به دکمه btnSave را کامل کرده و در ادامه توضیح می دهم.

package ir.android_studio.preference;

import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    EditText edtName;
    Button btnSave;
    SharedPreferences shPref;
    public static final String MyPref = "MyPrefers";
    public static final String Name = "nameKey";

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

        edtName = (EditText) findViewById(R.id.edt_name);
        btnSave = (Button) findViewById(R.id.btn_save);
        shPref = getSharedPreferences(MyPref, Context.MODE_PRIVATE);

        btnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String n = edtName.getText().toString();
                SharedPreferences.Editor sEdit = shPref.edit();
                sEdit.putString(Name, n);
                sEdit.apply();

                Toast.makeText(MainActivity.this, "Saved", Toast.LENGTH_LONG).show();

            }
        });

    }
}

در خط اول متغیری از جنس String با نام n تعریف کردم که با دستور

edtName.getText().toString()

Text ورودی کاربر را از edtName گرفته و در خود ذخیره می کند (توسط getText رشته ورودی دریافت و به وسیله toString به رشته از جنس String تبدیل می شود).
حالا به یک ادیتور نیاز داریم تا به واسطه آن اطلاعات را ذخیره و یا ویرایش کنیم. یک نمونه از SharedPreferences.Editor با نام sEdit ساختم که با shPref.edit() آماده ذخیره و یا ویرایش اطلاعات است.
در مرحله بعد باید محتویات درون n را به Name منتقل کنیم. این کار توسط متد putString انجام می شود:

sEdit.putString(Name, n);

همانطور که از نام متد پیداست، برای ذخیره و یا ویرایش داده های از نوع String بکار می رود. به همین ترتیب برای مقادیر صحیح عددی از متد putInt، مقادیر boolean متد putBoolean، مقادیر شناور متد putFloat و مقادیر long متد putLong استفاده می شود.
در نهایت توسط متد commit() تغییرات اعمال شده ذخیره می شود:

sEdit.commit();

اندروید استودیو به من توصیه می کند به جای commit از متد apply استفاده کنم، بنابراین sEdit.apply() را جایگزین کردم (تفاوتهایی بین این دو وجود دارد که در صورت تمایل با جستجوی عبارت “commit() vs apply()” به جوابهای خوبی خواهید رسید. برای زیبایی کار یک پیغام از نوع Toast به دکمه اضافه کردم تا پس از لمس دکمه عبارت Saved به کاربر نمایش داده شود (در مبحث دیتابیس با Toast آشنا شدیم). تا اینجای کار اگر کد ما مشکلی نداشته باشد اطلاعات وارد شده باید به درستی ذخیره شود. اما هنوز نیاز به یک دستور شرطی داریم تا بعد از خروج کاربر از برنامه و ورود مجدد به آن، چک کند اگر مقادیری در Preference ذخیره شده در محل موردنظر نمایش داده شود. به کد زیر دقت کنید:

if (shPref.contains(Name)) {
    edtName.setText(shPref.getString(Name, null));
}

این کد می گوید اگر shPref حاوی Name است، توسط متد getString مقدار آنرا گرفته و توسط متد setText روی ویجت edtName نمایش بده.
مشابه متدهای set، متدهای get شامل getString، getInt، getBoolean، getFloat و getLong برای بازیابی داده های ذخیره شده در اختیار ما قرار گرفته است. این متدها نیز دو پارامتر ورودی دارند که اولی کلید مربوط به داده ذخیره شده و پارامتر دوم مقدار پیش فرض است. پیش فرض به این معنی که اگر در کلید مربوطه (به عنوان مثال Name) داده ای ذخیره نشده بود، مقداری که اینجا تعیین کرده ایم به کاربر نمایش داده شود. یک مثال میزنم:

edtName.setText(shPref.getString(Name, "Name not saved!"));

در حالت بالا اگر قبلا در Name چیزی ذخیره نشده باشد، پس از اجرای اپلیکیشن در ویجت edtName عبارت Name not saved! قرار می گیرد. اما چون من قبلا در لایه رابط کاربری مقدار Name را به عنوان hint برای edtName درنظر گرفته ام، از این پارامتر صرف نظر کرده و مقدار null قرار داده ام. البته به جای null رشته خالی یعنی “” هم می توان استفاده کرد:

edtName.setText(shPref.getString(Name, ""));

کد کامل MainActivity:

package ir.android_studio.preference;

import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    EditText edtName;
    Button btnSave;
    SharedPreferences shPref;
    public static final String MyPref = "MyPrefers";
    public static final String Name = "nameKey";

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

        edtName = (EditText) findViewById(R.id.edt_name);
        btnSave = (Button) findViewById(R.id.btn_save);
        shPref = getSharedPreferences(MyPref, Context.MODE_PRIVATE);

        btnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String n = edtName.getText().toString();
                SharedPreferences.Editor sEdit = shPref.edit();
                sEdit.putString(Name, n);
                sEdit.apply();

                Toast.makeText(MainActivity.this, "Saved", Toast.LENGTH_LONG).show();

            }
        });

        if (shPref.contains(Name)) {
            edtName.setText(shPref.getString(Name, null));
        }

    }
}

پروژه را اجرا می کنم:

اجرای پروژه SharedPreferences

یک نام وارد کرده، ذخیره می کنم:

ذخیره داده

از اپلیکیشن خارج می شوم:

خروج از اپلیکیشن

مجدد اجرا می کنم:

اجرای اپلیکیشن

برنامه به درستی عمل کرد و نام ذخیره شده در EditText نمایش داده شد. اگر نام فعلی را تغییر داده و مجدد ذخیره کنیم، جایگزین نام قبلی خواهد شد.
دو فیلد ورودی دیگر با عنوان نام خانوادگی و سن نیز به برنامه اضافه می کنم:

package ir.android_studio.preference;

import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    EditText edtName;
    EditText edtFamily;
    EditText edtAge;
    Button btnSave;
    SharedPreferences shPref;
    public static final String MyPref = "MyPrefers";
    public static final String Name = "nameKey";
    public static final String Family = "familyKey";
    public static final String Age = "ageKey";

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

        edtName = (EditText) findViewById(R.id.edt_name);
        edtFamily = (EditText) findViewById(R.id.edt_family);
        edtAge = (EditText) findViewById(R.id.edt_age);
        btnSave = (Button) findViewById(R.id.btn_save);
        shPref = getSharedPreferences(MyPref, Context.MODE_PRIVATE);

        btnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String n = edtName.getText().toString();
                String f = edtFamily.getText().toString();
                int a = Integer.parseInt(edtAge.getText().toString());

                SharedPreferences.Editor sEdit = shPref.edit();
                sEdit.putString(Name, n);
                sEdit.putString(Family, f);
                sEdit.putInt(Age, a);
                sEdit.apply();

                Toast.makeText(MainActivity.this, "Saved", Toast.LENGTH_LONG).show();

            }
        });

        if (shPref.contains(Name)) {
            edtName.setText(shPref.getString(Name, null));
        }

        if (shPref.contains(Family)) {
            edtFamily.setText(shPref.getString(Family, null));
        }

        if (shPref.contains(Age)) {
            edtAge.setText(String.valueOf(shPref.getInt(Age, 0)));
        }

    }
}

پروژه را مجدد اجرا می کنم:

اضافه شدن دو EditText به رابط کاربری

اطلاعات را وارد کرده و روی دکمه save کلیک می کنم که ذخیره اطلاعات با موفقیت انجام می شود.
مقدار int به تنهایی نمی تواند توسط setText به ویجت ارسال شود. راه حل ساده این است که یک رشته خالی به آن اضافه کنیم:

edtAge.setText(shPref.getInt(Age, 0) + "");

اما من روشی را انتخاب می کنم که اصولی باشد. به اینصورت که توسط String.valueOf() عدد صحیح به String تبدیل شده و سپس به edtAge فرستاده می شود. عدد صفر هم به عنوان مقدار پیش فرض تعیین شده.
برای سادگی و مختصر شدن کد بهتر بود سن را هم از نوع String تعریف کرده و فقط در رابط کاربری، فیلد موردنظر محدود به کاراکترهای عددی می شد. با این حال بهانه ای شد تا از متد putInt و موارد مربوط به تبدیل String به Integer نیز استفاده شود.
اما در Shared Preferences دو متد دیگر نیز در اختیار داریم:
– remove(“KeyName”): توسط این متد می توان دیتا یا دیتاهای مدنظر را حذف کرد.
– clear(): با اجرا شدن این متد کلیه اطلاعات ذخیره شده حذف می گردد.
دو دکمه جدید با نام های “Remove Name and Family” و “Reset” به برنامه اضافه می کنم. تصمیم دارم با کلیک دکمه Remove، نام و نام خانوادگی و با کلیک Reset تمامی اطلاعات حذف شود. متد مربوط به دکمه ها را به صورت زیر تکمیل کردم:

btnRemove.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

        SharedPreferences.Editor rEdit = shPref.edit();
        rEdit.remove(Name);
        rEdit.remove(Family);
        rEdit.apply();

        Toast.makeText(MainActivity.this, "Removed!", Toast.LENGTH_LONG).show();

    }
});

btnClear.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

        SharedPreferences.Editor cEdit = shPref.edit();
        cEdit.clear();
        cEdit.apply();

        Toast.makeText(MainActivity.this, "All data cleared!", Toast.LENGTH_LONG).show();

    }
});

مانند دکمه save ابتدا یک نمونه ساخته و سپس متدهای موردنیاز فراخوانی شده است. توجه داشته باشید در صورتی که متد apply() یا commit() اضافه نشود، تغییرات اعمال نخواهد شد.
پروژه را اجرا می کنم:

اجرای پروژه

اطلاعاتی که قبلا ذخیره شده بود نمایش داده می شود. انتظار دارم با زدن گزینه Remove و سپس بستن و اجرای مجدد برنامه، نام و نام خانوادگی حذف شده باشد:

متد remove()

متد remove()

اطلاعات فیلدهای نام و نام خانوادگی با موفقیت حذف شد. در مرحله بعد برای اطمینان از عملکرد صحیح برنامه، ابتدا نام و نام خانوادگی را مجدد ذخیره کرده و در نهایت روی Reset all کلیک می کنم:

متد clear()

متد clear() نیز به درستی عمل کرده و پس از خروج از برنامه و اجرای مجدد، اطلاعاتی که وارد کرده بودم نمایش داده نمی شود.

متد clear()

دسترسی به فایل Preference

مانند دیتابیس SQLite امکان دسترسی به فایل Preference نیز وجود دارد.
از منوی Tools > Android پنجره Android Device Monitor را باز می کنم. در سمت چپ (Device) نام اپلیکیشن را انتخاب کرده سپس در سمت راست در تب File Manager مسیر زیر را دنبال می کنم:

Data > data > [Application name] > shared_prefs

مشاهده فایل Preference از طریق Android Device Monitor

فایل با همان نامی که تعیین کرده بودم ایجاد شده است. توسط گزینه Pull a file from a device فایل MyPrefers.xml را روی رایانه ذخیره و باز می کنم:

مشاهده فایل Preference از طریق Android Device Monitor

مشاهده می کنید تمامی اطلاعات درون این فایل ذخیره شده است.

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    android:paddingBottom="16dp"
    tools:context="ir.android_studio.preference.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <EditText
            android:id="@+id/edt_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:hint="Name"
            android:inputType="textPersonName" />

        <EditText
            android:id="@+id/edt_family"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:hint="Family"
            android:inputType="textPersonName" />

        <EditText
            android:id="@+id/edt_age"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:hint="Age"
            android:inputType="number" />

        <Button
            android:id="@+id/btn_save"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Save" />

        <Button
            android:id="@+id/btn_remove"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Remove" />

        <Button
            android:id="@+id/btn_clear"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Reset All" />
    </LinearLayout>
</RelativeLayout>

MainActivity.java

package ir.android_studio.preference;

import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    EditText edtName;
    EditText edtFamily;
    EditText edtAge;
    Button btnSave;
    Button btnRemove;
    Button btnClear;
    SharedPreferences shPref;
    public static final String MyPref = "MyPrefers";
    public static final String Name = "nameKey";
    public static final String Family = "familyKey";
    public static final String Age = "ageKey";

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

        edtName = (EditText) findViewById(R.id.edt_name);
        edtFamily = (EditText) findViewById(R.id.edt_family);
        edtAge = (EditText) findViewById(R.id.edt_age);
        btnSave = (Button) findViewById(R.id.btn_save);
        btnRemove = (Button) findViewById(R.id.btn_remove);
        btnClear = (Button) findViewById(R.id.btn_clear);
        shPref = getSharedPreferences(MyPref, Context.MODE_PRIVATE);

        btnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String n = edtName.getText().toString();
                String f = edtFamily.getText().toString();
                int a = Integer.parseInt(edtAge.getText().toString());

                SharedPreferences.Editor sEdit = shPref.edit();
                sEdit.putString(Name, n);
                sEdit.putString(Family, f);
                sEdit.putInt(Age, a);
                sEdit.apply();

                Toast.makeText(MainActivity.this, "Saved", Toast.LENGTH_LONG).show();

            }
        });

        btnRemove.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                SharedPreferences.Editor rEdit = shPref.edit();
                rEdit.remove(Name);
                rEdit.remove(Family);
                rEdit.apply();

                Toast.makeText(MainActivity.this, "Removed!", Toast.LENGTH_LONG).show();

            }
        });

        btnClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                SharedPreferences.Editor cEdit = shPref.edit();
                cEdit.clear();
                cEdit.apply();

                Toast.makeText(MainActivity.this, "All data cleared!", Toast.LENGTH_LONG).show();

            }
        });

        if (shPref.contains(Name)) {
            edtName.setText(shPref.getString(Name, null));
        }

        if (shPref.contains(Family)) {
            edtFamily.setText(shPref.getString(Family, null));
        }

        if (shPref.contains(Age)) {
            edtAge.setText(String.valueOf(shPref.getInt(Age, 0)));
        }

    }
}

دانلود فایل آموزشی با فرمت PDF به همراه سورس پروژه
تعداد صفحات : ۲۶
حجم : ۱٫۵ مگابایت
قیمت : رایگان

سوالاتی که مشمول موارد زیر باشد تایید و پاسخ داده نخواهد شد :
۱ : جزء موارد پاسخ داده شده در مطلب "مشکلات و پرسش های رایج" باشد
۲ : سوال قبلا توسط افراد در دیدگاهها مطرح و پاسخ داده شده باشد
۳ : سوال خارج از مبحث آموزشی موجود در این صفحه باشد
  • pjk گفت:

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

    1. سید مهدی مطهری (مدیر) گفت:

      ممنون بزرگوار

  • باقر برغمدی گفت:

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

  • kiyan kamgar گفت:

    سلام،
    خیلی ممنون بابت آموزشهاتون. یه سوال داشتم میخواستم بدونم تا اینجا چند درصد از مباحثی که باید یاد میگرفتیم رو یاد گرفتیم؟

    1. سید مهدی مطهری (مدیر) گفت:

      خواهش میکنم. حدودا ۳۰ درصد

  • Ahmad gharacheh گفت:

    با سلام و عرض خسته نباشید…
    یک انتقاد درخصوص پکی ۱۸۰۰۰ تومانی که امروز دانلود کردم از سایتتون…
    اول اینکه آموزش اواش متعلق به آقای بهزاد مرادیه که توی سایت نردبان بود که الان فیلتر شده و تو سایتای دیگه رایگان هست که تو لب تابم داشتم این آموزشو و شما بابتش چول گرفتید و این دور از شان یک سایت معتبر است اگر کر می کنید هستید.
    دوم اینکه محتوای اموزش اندرویدتون بسیار بسیار کمه که بالای ۵۰%ش رو اکثر مبتدیا میدونند و اینکه بازه ی زمانی آموزشایی که می زارید خیلی زیاده و فکر کنم تا دو سال دیکه ۲۰ % سایتای خارجی مطلب نزارید.
    ۱۸۰۰۰ تومان واسه همچین آموزشی دور از انصافه…
    امیدوارم رویه رو اصلاح کنید که افراد یشتری جذب شن.
    موفق و موید باشید…

    1. Admin گفت:

      با عرض سلام و تشکر از وقتی که گذاشتید بزرگوار
      در خصوص پکیج سایت لطفا صفحه مربوطه رو مجدد مطالعه بفرمایید (ان شا الله که قبل از خرید مطالعه فرمودید)
      بند اول صفحه پکیج :
      «به طور کلی مطالب وب سایت اندروید استودیو به دو قسمت رایگان و غیر رایگان تقسیم می شوند که در انتهای هر مطلب آموزشی فایل PDF برای دانلود در دسترس است. با این حال اگر مایل هستید آموزشهایی که تا این لحظه منتشر شده را یکجا دریافت کنید، نسبت به دانلود و یا دریافت پستی اقدام نمایید.»
      در پاراگراف بالا به وضوح اشاره شده که آموزش های موجود در سایت شامل دو دسته رایگان و غیر رایگان هستن. در خصوص جاوا که فرمودید کافیه فصل اول رو (منوی سمت راست سایت) باز کنید. فصل اول آموزشهای جاوا هست که به صورت رایگان قابل دانلوده و داخل متن هم اشاره شده که آموزشها مربوط به وب سایت نردبان هست:
      https://android-studio.ir/%d8%a2%d9%85%d9%88%d8%b2%d8%b4-%d8%b2%d8%a8%d8%a7%d9%86-%d8%ac%d8%a7%d9%88%d8%a7

      در خصوص کم بودن محتوا هم بله در قسمت معرفی سایت هم عنوان شده که آموزشها به مرور در حال تکمیل هست و ادعایی مبنی بر کامل بودن پکیج نشده و صراحتا عنوان شده برای افرادی هست که قصد دارن آموزشهای سایت (اعم از رایگان و غیر رایگان) رو یکجا و سریع دریافت کنن. مبلغ پکیج بابت ۴ مبحث آموزشی هست که به ترتیب مبالغ ۳، ۴، ۵ و ۶ هزار تومن هستن و مابغی رایگان هست که قبل از خرید می تونستید محتوای سایت رو بررسی کنید و در صورتی که مباحث برای شما سطح پایین بود اقدام به خرید نمیکردید.
      این که فرمودید بازه زمانی انتشار آموزشها زیاده بله قبول دارم و تمام سعی بر این هست که این فاصله کاهش پیدا بکنه.
      در نهایت مجدد از انتقادتون نسبت به وب سایت تشکر میکنم و خوشحالم که بعد از حدود یک سال و نیم از فعالیت سایت علاوه بر پیامهای ابراز لطف کاربرا بالاخره یک نفر هم انتقاد کرد

  • قالب وردپرس گفت:

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

پاسخ دهید

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

کد امنیتی *