گوگل به منظور کمک به توسعه دهندگان اندروید و برای اینکه توسعه دهندگان برنامههای قوی تر، قابل آزمایش و قابل اطمینانتری را طراحی کنند، مجموعه ای از کتابخانههای “Android Architecture Components” را منتشر کرد، قابل توجهترین کلاس LiveData و کلاسهای مربوط به lifecycle-aware، کتابخانهی Room persistence و کتابخانهی paging است. در این مقاله ما LiveData در اندروید و مشکلی که میتوانید با آن حل کنید، را بررسی میکنیم. در ادامه همراه ما باشید.
LiveData در اندروید یک کلاسی هست، که دارندهی دیتای قابل مشاهده است. بر خلاف یک مشاهدهی منظم، LiveData از چرخهی حیات آگاه (lifecycle-aware) است، به این معنی که به چرخهی حیات سایر مؤلفههای (components) برنامه مانند “Activities” ، “Fragments” یا “Services” دقت کرده و احترام میگذارد. این آگاهی تضمین میکند که LiveData فقط app component observers را که در حالت فعال چرخه (active lifecycle state) هستند، بروز میکند.
چرخه حیات اکتیویتی را در این تصویر مشاهده نمایید (Activity LifeCycle)
چرخه حیات فرگمنت را در این تصویر مشاهده نمایید(Fragment LifeCycle)
LiveData از observer pattern پیروی میکند. LiveData با تغییر وضعیت LifeCycle، اشیاء Observer را مطلع میکند. میتوانید کد خود را برای به روزرسانی UI در اشیاء Observer تلفیق کنید. به جای بروز کردن UI هر بار که دادههای برنامه تغییر میکنند، Observer شما میتواند، هر زمان که تغییر ایجاد میکند، رابط کاربر را بروز کند.
اگر چرخه حیات Observer غیرفعال باشد، مانند Activity در back stack، هیچ رویدادی را از LiveData دریافت نمیکند.
مؤلفههای UI فقط دادههای مربوطه را مشاهده میکنند، و مشاهده، متوقف یا دوباره از سر گرفته نمیشوند. LiveData به طور خودکار تمام این موارد را مدیریت میکند، زیرا Observer از تغییرات وضعیت چرخهی حیات مربوطه آگاه است.
اگر چرخهی حیات یا همان LifeCycle غیرفعال شود، پس از فعال شدن دوباره آخرین اطلاعات را دریافت میکند. به عنوان مثال، Activity که در Background بود، آخرین اطلاعات را بلافاصله پس از بازگشت به Foreground دریافت میکند.
LiveData در اندروید تا حدودی شبیه RxJava است، به جز اینکه LiveData از چرخهی حیات آگاه یا lifecycle aware است.
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'android.arch.lifecycle:extensions:1.1.1
کد طرح activity_main.xml در زیر آورده شده است:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_input_add" />
</android.support.design.widget.CoordinatorLayout>
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:gravity="center_vertical">
<TextView
android:id="@+id/tvUrl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:autoLink="web"
android:padding="8dp"
android:textColor="@android:color/black"
android:textSize="20sp" />
<TextView
android:id="@+id/tvDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/tvUrl" />
<ImageButton
android:id="@+id/btnDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:src="@android:drawable/ic_menu_delete" />
</RelativeLayout>
</android.support.v7.widget.CardView>
کد کلاس DbSettings.java در زیر آورده شده است:
package com.sevenlearn.androidlivedata.db;
import android.provider.BaseColumns;
public class DbSettings {
public static final String DB_NAME = "favourites.db";
public static final int DB_VERSION = 1;
public class DBEntry implements BaseColumns {
public static final String TABLE = "fav";
public static final String COL_FAV_URL = "url";
public static final String COL_FAV_DATE = "date";
}
}
The code for FavouritesDbHelper.java class is given below:
package com.journaldev.androidlivedata.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class FavouritesDBHelper extends SQLiteOpenHelper {
public FavouritesDBHelper(Context context) {
super(context, DbSettings.DB_NAME, null, DbSettings.DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String createTable = "CREATE TABLE " + DbSettings.DBEntry.TABLE + " ( " +
DbSettings.DBEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
DbSettings.DBEntry.COL_FAV_URL + " TEXT NOT NULL, " +
DbSettings.DBEntry.COL_FAV_DATE + " INTEGER NOT NULL);";
db.execSQL(createTable);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + DbSettings.DBEntry.TABLE);
onCreate(db);
}
}
package com.sevenlearn.androidlivedata;
public class Favourites {
public long mId;
public String mUrl;
public long mDate;
public Favourites(long id, String name, long date) {
mId = id;
mUrl = name;
mDate = date;
}
public Favourites(Favourites favourites) {
mId = favourites.mId;
mUrl = favourites.mUrl;
mDate = favourites.mDate;
}
}
package com.sevenlearn.androidlivedata;
import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.MutableLiveData;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.journaldev.androidlivedata.db.DbSettings;
import com.journaldev.androidlivedata.db.FavouritesDBHelper;
import java.util.ArrayList;
import java.util.List;
public class FavouritesViewModel extends AndroidViewModel {
private FavouritesDBHelper mFavHelper;
private MutableLiveData<List<Favourites>> mFavs;
FavouritesViewModel(Application application) {
super(application);
mFavHelper = new FavouritesDBHelper(application);
}
public MutableLiveData<List<Favourites>> getFavs() {
if (mFavs == null) {
mFavs = new MutableLiveData<>();
loadFavs();
}
return mFavs;
}
private void loadFavs() {
List<Favourites> newFavs = new ArrayList<>();
SQLiteDatabase db = mFavHelper.getReadableDatabase();
Cursor cursor = db.query(DbSettings.DBEntry.TABLE,
new String[]{
DbSettings.DBEntry._ID,
DbSettings.DBEntry.COL_FAV_URL,
DbSettings.DBEntry.COL_FAV_DATE
},
null, null, null, null, null);
while (cursor.moveToNext()) {
int idxId = cursor.getColumnIndex(DbSettings.DBEntry._ID);
int idxUrl = cursor.getColumnIndex(DbSettings.DBEntry.COL_FAV_URL);
int idxDate = cursor.getColumnIndex(DbSettings.DBEntry.COL_FAV_DATE);
newFavs.add(new Favourites(cursor.getLong(idxId), cursor.getString(idxUrl), cursor.getLong(idxDate)));
}
cursor.close();
db.close();
mFavs.setValue(newFavs);
}
public void addFav(String url, long date) {
SQLiteDatabase db = mFavHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(DbSettings.DBEntry.COL_FAV_URL, url);
values.put(DbSettings.DBEntry.COL_FAV_DATE, date);
long id = db.insertWithOnConflict(DbSettings.DBEntry.TABLE,
null,
values,
SQLiteDatabase.CONFLICT_REPLACE);
db.close();
List<Favourites> favourites = mFavs.getValue();
ArrayList<Favourites> clonedFavs;
if (favourites == null) {
clonedFavs = new ArrayList<>();
} else {
clonedFavs = new ArrayList<>(favourites.size());
for (int i = 0; i < favourites.size(); i++) {
clonedFavs.add(new Favourites(favourites.get(i)));
}
}
Favourites fav = new Favourites(id, url, date);
clonedFavs.add(fav);
mFavs.setValue(clonedFavs);
}
public void removeFav(long id) {
SQLiteDatabase db = mFavHelper.getWritableDatabase();
db.delete(
DbSettings.DBEntry.TABLE,
DbSettings.DBEntry._ID + " = ?",
new String[]{Long.toString(id)}
);
db.close();
List<Favourites> favs = mFavs.getValue();
ArrayList<Favourites> clonedFavs = new ArrayList<>(favs.size());
for (int i = 0; i < favs.size(); i++) {
clonedFavs.add(new Favourites(favs.get(i)));
}
int index = -1;
for (int i = 0; i < clonedFavs.size(); i++) {
Favourites favourites = clonedFavs.get(i);
if (favourites.mId == id) {
index = i;
}
}
if (index != -1) {
clonedFavs.remove(index);
}
mFavs.setValue(clonedFavs);
}
}
package com.sevenlearn.androidlivedata;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;
import java.util.Date;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private FavAdapter mFavAdapter;
private FavouritesViewModel mFavViewModel;
private List<Favourites> mFav;
FloatingActionButton fab;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fab = findViewById(R.id.fab);
final RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
mFavViewModel = ViewModelProviders.of(this).get(FavouritesViewModel.class);
final Observer<List<Favourites>> favsObserver = new Observer<List<Favourites>>() {
@Override
public void onChanged(@Nullable final List<Favourites> updatedList) {
if (mFav == null) {
mFav = updatedList;
mFavAdapter = new FavAdapter();
recyclerView.setAdapter(mFavAdapter);
} else {
DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
@Override
public int getOldListSize() {
return mFav.size();
}
@Override
public int getNewListSize() {
return updatedList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mFav.get(oldItemPosition).mId ==
updatedList.get(newItemPosition).mId;
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
Favourites oldFav = mFav.get(oldItemPosition);
Favourites newFav = updatedList.get(newItemPosition);
return oldFav.equals(newFav);
}
});
result.dispatchUpdatesTo(mFavAdapter);
mFav = updatedList;
}
}
};
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final EditText inUrl = new EditText(MainActivity.this);
AlertDialog dialog = new AlertDialog.Builder(MainActivity.this)
.setTitle("New favourite")
.setMessage("Add a url link below")
.setView(inUrl)
.setPositiveButton("Add", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String url = String.valueOf(inUrl.getText());
long date = (new Date()).getTime();
mFavViewModel.addFav(url, date);
}
})
.setNegativeButton("Cancel", null)
.create();
dialog.show();
}
});
mFavViewModel.getFavs().observe(this, favsObserver);
}
public class FavAdapter extends RecyclerView.Adapter<FavAdapter.FavViewHolder> {
@Override
public FavViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_row, parent, false);
return new FavViewHolder(itemView);
}
@Override
public void onBindViewHolder(FavViewHolder holder, int position) {
Favourites favourites = mFav.get(position);
holder.mTxtUrl.setText(favourites.mUrl);
holder.mTxtDate.setText((new Date(favourites.mDate).toString()));
}
@Override
public int getItemCount() {
return mFav.size();
}
class FavViewHolder extends RecyclerView.ViewHolder {
TextView mTxtUrl;
TextView mTxtDate;
FavViewHolder(View itemView) {
super(itemView);
mTxtUrl = itemView.findViewById(R.id.tvUrl);
mTxtDate = itemView.findViewById(R.id.tvDate);
ImageButton btnDelete = itemView.findViewById(R.id.btnDelete);
btnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int pos = getAdapterPosition();
Favourites favourites = mFav.get(pos);
mFavViewModel.removeFav(favourites.mId);
}
});
}
}
}
}
در کد بالا، ما کلاس Adapter را برای RecyclerView در Activity تعریف کردهایم.
اگر به یادگیری بیشتر در زمینهی اندروید علاقه داری، با شرکت در دورهی آموزش برنامه نویسی اندروید در کمتر از یکسال به یک توسعهدهنده اندروید همه فن حریف تبدیل میشوی که آمادهی استخدام، دریافت پروژه و حتی پیادهسازی اپلیکیشن خودت هستی.
لینک رو با vpn باز کنید، محتوا دانلود میشه. این مشکل به این دلیل ایجاد شده که ما به دلیل حق کپی رایت باید سورس رو از سایت انگلیسی که منبع این مقاله هست، لینک میکردیم.