إختبر قواعد البيانات داخل الأندرويد

يتحدث المقال عن واحدة من أهم المكتبات المستخدمة لعمل إختبارات لقواعد البيانات داخل نظام الأندرويد

Abdulrahman Hasan Aghaمنذ أسبوعين

بسم الله الرحمن الرحيم

 

منذ ظهور مكتبة Room كمكتبة تسهل على مطوري الأندرويد التعامل مع قواعد البيانات الداخلية من ناحية الإنشاء والحذف والإضافة، بدأ الكثير من المطورين العمل على إضافة بعد المميزات على هذه المكتبة. واحدة من أهم المشاكل التي كان يعاني منها المطورين هي مشكلة إختبار قواعد البيانات والقدرة على معرفة محتوياتها ورؤيتها بشكل فعلي كجداول وعلاقات. الكثير من المواقع أو الـ plugins كانت توفر هذه الميزة ولكن يجب عليك أولاً رفع ملف قاعدة البيانات على الموقع واستخراجه من الهاتف ومن ثم العمل عليه. هذه الطريقة عانت من التعقيد ولم تستخدم بشكل كبير بين المطورين. ( منها مكتبة Stetho المطورة من قبل Facebook )

الكثير من المكتبات الأخرى التي تستخدم طريقة Object-Databases مثل مكتبة Realm أو حتى ObjectBox تمتلك Browser خاص بها يمكنك من معرفة محتويات قاعدة البيانات بسهولة. ولكن ماذا عن Room؟ سنتحدث اليوم عن واحدة من أفضل المكتبات التي تميزت بهذا المجال. كانت تستخدم من قبل ظهور مكتبة Room حتى، أي مع الـ Pure SQLite ولكن بعد ظهور Room تحسنت المكتبة بشكل كبير. تمكنك المكتبة من إضافة وحذف أو التعديل على الـ Records داخل قاعدة البيانات. تمكنك أيضاً من تتبع المعلومات المحفوظة داخل الـ SharedPreferences والتأكد ما إذا كانت المعلومات موجودة أو لا. 

 

المكتبة من تطوير Amit Shekhar واحد من أفضل مطوري المكتبات في مجال الأندرويد. بإمكانك مراجعة حسابه في Github من هنا

ملاحظة : لن نتكلم بشكل كبير عن مكتبة Room وطريقة إنشاء قواعد البيانات من خلالها. لمعرفة طريقة عمل المكتبة يمكنك زيارة موضوعي السابق من هنا

بإمكانك أيضاً مراجعة دورة الأستاذ محمد على عالم البرمجة من هنا

 

لنبدأ .. 

المشروع سيكون عبارة عن RecyclerView ستقوم بعرض محتويات قاعدة البيانات التي سنقوم بإنشائها.

 

بعد إنشاء بروجكت جديد سنقوم بإضافة كل الـ Dependencies اللازمة

    // recyclerview
    implementation 'com.android.support:recyclerview-v7:+'

    //room db
    implementation "android.arch.persistence.room:runtime:1.1.1"
    annotationProcessor "android.arch.persistence.room:compiler:1.1.1"
    
    // android debug database library
    debugImplementation 'com.amitshekhar.android:debug-db:1.0.4'

 

نقوم الآن بعمل Setup لقاعدة البيانات.

أولاً سنقوم بإنشاء الـ Model Class. سيقوم بتخزين الـ ID, Name, and Major للطلاب.

import android.arch.persistence.room.Entity;
import android.arch.persistence.room.PrimaryKey;

@Entity
public class Student {

    @PrimaryKey(autoGenerate = true)
    private int id;
    private String name;
    private String major;

    public Student() {

    }

    public Student(int id, String name, String major) {
        this.id = id;
        this.name = name;
        this.major = major;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getMajor() {
        return major;
    }

    public void setMajor(String major) {
        this.major = major;
    }
}

 

ثانياً نقوم بإنشاء Data access object interface DAO بالإضافة إلى كلاس الـ Database.

import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Delete;
import android.arch.persistence.room.Insert;
import android.arch.persistence.room.OnConflictStrategy;
import android.arch.persistence.room.Query;

import com.example.asus.databasedebug.pojo.Student;

import java.util.List;


@Dao
public interface StudentDAO {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert (Student student);

    @Delete
    void delete (Student student);

    @Query("SELECT * FROM Student")
    List<Student> getAllStudents();

}

 

import android.arch.persistence.room.Database;
import android.arch.persistence.room.RoomDatabase;

import com.example.asus.databasedebug.pojo.Student;

@Database(entities = Student.class, version = 1)
public abstract class StudentDB extends RoomDatabase {

    public abstract StudentDAO studentDAO();

}

 

نقوم بإنشاء كلاس الـ Repository أو الـ Source of truth والمسؤول عن إنشاء قاعدة البيانات بالإضافة إلى إجراء كل العمليات المعرفة مسبقاً داخل الـ DAO.

import android.arch.persistence.room.Room;
import android.os.AsyncTask;

import com.example.asus.databasedebug.application.DatabaseDebugApplication;
import com.example.asus.databasedebug.pojo.Student;

import java.util.List;


// Singleton
public class StudentRepository {

    // singleton object
    private static StudentRepository repository = new StudentRepository();

    // database object
    private static StudentDB studentDB;

    // constructor
    private StudentRepository(){
        initDB();
    }

    public static StudentRepository getInstance(){
        return repository;
    }

    private void initDB() {
        studentDB = Room.databaseBuilder(DatabaseDebugApplication.getContext(),
                StudentDB.class, "StudentDatabase").build();
    }

    public List<Student> getStudents(){
        try {
            return new DataBaseOperation().execute().get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // Async
    class DataBaseOperation extends AsyncTask<Void,Void,List<Student>> {

        @Override
        protected List<Student> doInBackground(Void... voids) {
            return studentDB.studentDAO().getAllStudents();
        }
    }
}

 

لا تنسى توفير الـ Application Context لكلاس الـ Repository حتى يتمكن من إنشاء قاعدة البيانات. ( لا تنسى إضافته للـ Manifest )

import android.app.Application;
import android.content.Context;

public class DatabaseDebugApplication extends Application {

    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }

    public static Context getContext(){
        return context;
    }
}

 

ننتقل الآن إلى الـ View. ( لن نقوم بإنشاء ViewModel كحلقة وصل بين الـ View وبين الـ Repository إختصاراً للوقت .. ولكن لا يعتبر Good practice )

الـ MainActivity.XML

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context=".ui.MainActivity">

    <android.support.v4.widget.SwipeRefreshLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/swipe_to_refresh">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="5dp" />

    </android.support.v4.widget.SwipeRefreshLayout>

</RelativeLayout>

 

الشكل النهائي

 

 

بالنسبة لتصميم الـ RecyclerView cell.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="wrap_content">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/name"
        android:layout_margin="10dp"
        android:textColor="#000"
        android:text="Student Name"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/major"
        android:layout_margin="10dp"
        android:layout_alignParentEnd="true"
        android:text="Student Major"/>

</RelativeLayout>

 

الشكل النهائي

 

 

أخيراً كلاس الـ MainActivity and RecyclerView adapter

import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import com.example.asus.databasedebug.R;
import com.example.asus.databasedebug.database.StudentRepository;

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private StudentAdapter adapter;
    private SwipeRefreshLayout swipeRefreshLayout;

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

        // Initialize views
        mRecyclerView = findViewById(R.id.recyclerview);
        swipeRefreshLayout = findViewById(R.id.swipe_to_refresh);

        // Initialize adapter
        adapter = new StudentAdapter(StudentRepository.getInstance().getStudents());


        // Configure recyclerview
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        mRecyclerView.setAdapter(adapter);

        // Swipe listener
        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                adapter.setList(StudentRepository.getInstance().getStudents());
                adapter.notifyDataSetChanged();

                swipeRefreshLayout.setRefreshing(false);
            }
        });

    }
}

 

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

import com.example.asus.databasedebug.R;
import com.example.asus.databasedebug.pojo.Student;

import java.util.List;

public class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.ViewHolder> {

    private List<Student> studentList;

    public StudentAdapter(List<Student> list){
        studentList = list ;
    }

    public void setList(List<Student> list){
        studentList = list;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        return new ViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_student, null));
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder viewHolder, int index) {
        viewHolder.name.setText(studentList.get(index).getName());
        viewHolder.major.setText(studentList.get(index).getMajor());
    }

    @Override
    public int getItemCount() {
        return ( studentList == null ) ? 0 : studentList.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder{

        TextView name, major;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            name = itemView.findViewById(R.id.name);
            major = itemView.findViewById(R.id.major);
        }
    }
}

 

إنتهينا بشكل كبير من الجانب النظري. ننتقل للجزء الرئيسي. إذا كنت تريد تشغيل التطبيق على الـ Emulator ستضطر إلى تشغيل هذا الأمر داخل مجلد التطبيق من نافذة الـ Terminal بالأسفل. ( تأكد من إضافة الـ platform-tools كـ PATH حتى يعمل الأمر بشكل صحيح )

adb forward tcp:8080 tcp:8080

 

إذا كنت تريد تشغيل التطبيق على جوالك الخاص، تأكد بأنك متصل على نفس الشبكة المتصل بها الكمبيوتر. 

بعد تشغيل التطبيق قم بالبحث داخل الـ Logcat عن D/DebugDB .. ستلاحظ وجود رابط قم بالدخول عليه.

 

بمجرد الدخول للرابط سنلاحظ ظهور هذه الصفحة

 

سنلاحظ وجود عدة خيارات. بإمكانك تصفح قواعد البيانات الموجودة داخل الجهاز والمنشأة من قبل التطبيق تحت خانة الـ Databases على اليسار. ( StudentDatabase ) بالإضافة إلى الـ SharedPreference.

بالضغط على جدول الـ Student سنستطيع رؤية كل البيانات المخزنة داخل هذا الجدول. ( نلاحظ وجود عدة جداول لم نقم بإنشائها ولكن مكتبة Room قامت بإنشائها بشكل تلقائي )

نستطيع الآن إضافة البيانات لتعبئة الجداول ( تعتبر طريقة ممتازة لعمل Populate لقاعدة البيانات بشكل سريع وبدون كتابة سطر برمجي واحد.

 

 

نقوم الآن بتحديث التطبيق بعمل Swipe للأسفل وسنلاحظ بأن البيانات أصبحت تعرض بشكل صحيح داخل التطبيق.

 

بإمكانك أيضاً تشغيل Query معينة للتأكد بأنها تعمل بشكل صحيح إذا طبقت داخل التطبيق.

 

بإمكانك أيضاً التعديل وحذف البيانات بكل سهولة بالتحديد على الـ Row ومن ثم الضغط على Edit أو Delete.

 

لمعرفة المزيد عن هذه المكتبة بإمكانك زيارة الـ Repository الخاصة بها على Github من هنا

 

بالتوفيق :)

 

كلمات دليلية: android database debug room
3
إعجاب
383
مشاهدات
0
مشاركة
3
متابع
متميز
محتوى رهيب

التعليقات (1)

كمال:

ممتاز عمل منظم 

تشكر على العمل أكثر منرائع

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !