ارائه دهنده محتوا (Content Provider) در اندروید
یک ارائه دهنده محتوا یا Content Provider وظیفه ارائه اطلاعات از یک برنامه به برنامه های دیگر، هنگام درخواست آنها را به عهده دارد. چنین درخواست هایی به وسیله متد های موجود در کلاس ContentResolver مدیریت می شود. یک ارائه دهنده محتوا، می تواند از راههای مختلفی برای ذخیره سازی اطلاعات خود استفاده کند این به این معنی است که این اطلاعات می تواند در دیتابیس یا فایل یا حتی روی شبکه ذخیره شود.
بعضی اوقات نیاز دارید که اطلاعات را بین اپلیکیشن ها به اشتراک بگذارید. در اینگونه مواقع است که ارائه دهنده های محتوا کاربرد زیادی دارند.
ارائه دهنده محتوا به شما این اجازه را می دهد که محتوای خود را در یک جا جمع کنید و تعداد زیادی اپلیکیشن مختلف داشته باشید که زمانی که به این محتوا نیاز داشتند، به آن دسترسی پیدا کنند. یک ارائه دهنده محتوا بسیار شبیه به یک دیتابیس رفتار می کند، یعنی شما می توانید به آن درخواست دریافت یا ویرایش اطلاعات آن را بدهید و آن مانند متد های insert(), update(), delete(), و query() اطلاعات را اضافه یا حذف یا اراده می دهد. در اغلب موارد، این اطلاعات در دیتابیس SQLite ذخیره شده است.
یک ارائه دهنده محتوا با ارث بری از کلاس ContentProvider پیاده سازی می شود و باید یک سری API استاندارد را پیاده سازی کند تا سایر اپلیکیشن ها قادر به دسترسی به آن بشوند.
public class My Application extends ContentProvider { }
URI های محتوا
برای درخواست کردن اطلاعات از یک ارائه دهنده محتوا، باید رشته درخواست (query string) را به شکل یک URI که دارای فرمت زیر می باشد تعریف کنید.
<prefix>://<authority>/<data_type>/<id>
در جدول زیر توضیحات بخش های مختلف ساختار URI را می بینید:
شماره | بخش و توضیح |
---|---|
۱ | prefix
این قسمت همیشه content:// می باشد. |
۲ | authority
این قسمت نام ارائه دهنده محتوا را مشخص میکند. برای مثال contacts، browser و … . برای ارائه دهنده های محتوای غیر سیستمی یا به اصطلاع ارائه دهنده های محتوای شخص ثالث (third-party) ، این بخش می تواند یک نام کامل مثلا com.app2app.statusprovider باشد. |
۳ | data_type
این بخش نوع داده ای را که این ارائه دهنده محتوا ارائه می دهد را مشخص میکند. برای مثال اگر شما بخواهید همه مخاطبین را از ارائه دهنده محتوای مخاطبین بگیرید، data_type یا نوع داده ای باید people و URI باید شبیه این باشد. content://contacts/people |
۴ | id
این بخش نشان دهنده یک رکورد مشخص است. برای مثال اگر شما به دنبال مخاطب شماره ۵ در ارائه دهنده محتوای مخاطبین هستید، URI باید شبیه این باشد. content://contacts/people/5 |
ایجاد ارائه دهنده محتوا
ایجاد ارائه دهنده محتوا شامل تعدادی گام ساده می شود.
- قبل از هر چیز باید یک کلاس Content Provider که از کلاس ContentProvider مشتق شده باشد ایجاد کنید.
- در گام دوم باید آدرس URI ارائه دهنده محتوای خود، که برای دسترسی به محتوا ارائه شده است، را تعریف کنید.
- در ادامه شما باید دیتابیس خود برای نگهداری محتوا را ایجاد کنید. معمولا اندروید از دیتابیس SQLite استفاده می کند و در فریمورک ارائه دهنده محتوا نیاز باید متد onCreate() را override یا سربارگذاری کنید که از متد SQLite Open Helper برای ایجاد یا باز کردن دیتابیس ارائه دهنده استفاده می کند. زمانی که اپلیکیشن اجرا می شود، متد onCreate() هر کدام از ارائه دهنده های محتوا در ترد اصلی اپلیکیشن فراخوانی خواهد شد.
- در گام بعد شما باید درخواست های ارائه دهنده محتوا را پیاده سازی کنید تا عملیات مختلف دیتابیس اجرا شود.
- در نهایت ارائه دهنده محتوا را در با استفاده از تگ <provider> در فایل منیفست ثبت کنید.
در زیر لیست متد هایی که باید در کلاس ارائه دهنده محتوا پیاده سازی کنید تا ارائه دهنده محتوا کار کند را می بینید.
- onCreate(): این متد زمانی فراخوانی می شود که ارائه دهنده محتو اشروع به کار می کند.
- query(): این متد درخواست را از کلاینت دریافت می کند. نتیجه در قالب یک شی Cursor برگردانده می شود.
- insert(): این متد یک رکورد جدید در ارائه دهنده محتوا درج می کند.
- delete(): این متد یک رکورد که در ارائه دهنده محتوا وجود دارد را حذف میکند.
- update(): این متد یک رکورد که در ارائه دهنده محتوا وجود دارد را ویرایش میکند.
- getType(): این متد MIME type اطلاعات موجود در یک URI را بر می گرداند.
مثال
این مثال توضیح می دهد که چطور ارائه دهنده محتوای خود را بسازید. پس اجازه دهید گام های زیر را دنبال کنیم. این گام ها تقریبا شبیه به همان گام هایی است که در آموزش Hello world برای ایجاد اولین اپلیکیشن توضیح دادم.
گام | توضیح |
---|---|
۱ | در اندروید استدیو یک اپلیکیشن اندروید با blank Activity ایجاد کنید و نام آن را My Application و پکیج را com.example.MyApplication قرار دهید. |
۲ | اکتیویتی اصلی را ویرایش کنید و متد های onClickAddName() و onClickRetrieveStudents() را به آن اضافه کنید. |
۳ | برای تعریف ارائه دهنده واقعی و متد های وابسته آن ، در پکیج com.example.MyApplication ، یک فایل جدید ایجاد کنید و نام آن را StudentsProvider.java بگذارید. |
۴ | با استفاده از تگ <provider…/> ارائه دهنده محتوای خود را در فایل AndroidManifest.xml ثبت کنید. |
۵ | ویرایش فایل res/layout/activity_main.xml و اضافه کردن یک GUI کوچک برای اضافه کردن رکورد های Student |
۶ | نیازی به تغییر هیچ چیزی در فایل res/values/strings.xml نیست. اندروید استدیو مقادیر این فایل را مدیریت می کند. |
۷ | اپلیکیشن را روی گوشی اجرا کنید و تغییرات ایجاد شده در اپلیکیشن را بررسی کنید. |
در زیر محتوای ویرایش شده فایل اکتیویتی اصلی src/com.example.MyApplication/MainActivity.java را می بینید. این فایل می تواند شامل هر کدام از متد های اساسی چرخه اجرا باشد. ما دو متد جدید onClickAddName() و onClickRetrieveStudents() را برای مدیریت کردن تعامل کاربر با اپلیکیشن ایجاد کرده ایم.
package com.example.MyApplication; import android.net.Uri; import android.os.Bundle; import android.app.Activity; import android.content.ContentValues; import android.content.CursorLoader; import android.database.Cursor; import android.view.Menu; import android.view.View; import android.widget.EditText; import android.widget.Toast; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onClickAddName(View view) { // Add a new student record ContentValues values = new ContentValues(); values.put(StudentsProvider.NAME, ((EditText)findViewById(R.id.editText2)).getText().toString()); values.put(StudentsProvider.GRADE, ((EditText)findViewById(R.id.editText3)).getText().toString()); Uri uri = getContentResolver().insert( StudentsProvider.CONTENT_URI, values); Toast.makeText(getBaseContext(), uri.toString(), Toast.LENGTH_LONG).show(); } public void onClickRetrieveStudents(View view) { // Retrieve student records String URL = "content://com.example.MyApplication.StudentsProvider"; Uri students = Uri.parse(URL); Cursor c = managedQuery(students, null, null, null, "name"); if (c.moveToFirst()) { do{ Toast.makeText(this, c.getString(c.getColumnIndex(StudentsProvider._ID)) + ", " + c.getString(c.getColumnIndex( StudentsProvider.NAME)) + ", " + c.getString(c.getColumnIndex( StudentsProvider.GRADE)), Toast.LENGTH_SHORT).show(); } while (c.moveToNext()); } } }
در پکیج com.example.MyApplication فایلی با نام StudentsProvider.java ایجاد کنید و کد های زیر را در آن قرار دهید.
package com.example.MyApplication; import java.util.HashMap; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.text.TextUtils; public class StudentsProvider extends ContentProvider { static final String PROVIDER_NAME = "com.example.MyApplication.StudentsProvider"; static final String URL = "content://" + PROVIDER_NAME + "/students"; static final Uri CONTENT_URI = Uri.parse(URL); static final String _ID = "_id"; static final String NAME = "name"; static final String GRADE = "grade"; private static HashMap<String, String> STUDENTS_PROJECTION_MAP; static final int STUDENTS = 1; static final int STUDENT_ID = 2; static final UriMatcher uriMatcher; static{ uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(PROVIDER_NAME, "students", STUDENTS); uriMatcher.addURI(PROVIDER_NAME, "students/#", STUDENT_ID); } /** * Database specific constant declarations */ private SQLiteDatabase db; static final String DATABASE_NAME = "College"; static final String STUDENTS_TABLE_NAME = "students"; static final int DATABASE_VERSION = 1; static final String CREATE_DB_TABLE = " CREATE TABLE " + STUDENTS_TABLE_NAME + " (_id INTEGER PRIMARY KEY AUTOINCREMENT, " + " name TEXT NOT NULL, " + " grade TEXT NOT NULL);"; /** * Helper class that actually creates and manages * the provider's underlying data repository. */ private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context){ super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_DB_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + STUDENTS_TABLE_NAME); onCreate(db); } } @Override public boolean onCreate() { Context context = getContext(); DatabaseHelper dbHelper = new DatabaseHelper(context); /** * Create a write able database which will trigger its * creation if it doesn't already exist. */ db = dbHelper.getWritableDatabase(); return (db == null)? false:true; } @Override public Uri insert(Uri uri, ContentValues values) { /** * Add a new student record */ long rowID = db.insert( STUDENTS_TABLE_NAME, "", values); /** * If record is added successfully */ if (rowID > 0) { Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID); getContext().getContentResolver().notifyChange(_uri, null); return _uri; } throw new SQLException("Failed to add a record into " + uri); } @Override public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(STUDENTS_TABLE_NAME); switch (uriMatcher.match(uri)) { case STUDENTS: qb.setProjectionMap(STUDENTS_PROJECTION_MAP); break; case STUDENT_ID: qb.appendWhere( _ID + "=" + uri.getPathSegments().get(1)); break; default: } if (sortOrder == null || sortOrder == ""){ /** * By default sort on student names */ sortOrder = NAME; } Cursor c = qb.query(db, projection, selection, selectionArgs,null, null, sortOrder); /** * register to watch a content URI for changes */ c.setNotificationUri(getContext().getContentResolver(), uri); return c; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; switch (uriMatcher.match(uri)){ case STUDENTS: count = db.delete(STUDENTS_TABLE_NAME, selection, selectionArgs); break; case STUDENT_ID: String id = uri.getPathSegments().get(1); count = db.delete( STUDENTS_TABLE_NAME, _ID + " = " + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int count = 0; switch (uriMatcher.match(uri)) { case STUDENTS: count = db.update(STUDENTS_TABLE_NAME, values, selection, selectionArgs); break; case STUDENT_ID: count = db.update(STUDENTS_TABLE_NAME, values, _ID + " = " + uri.getPathSegments().get(1) + (!TextUtils.isEmpty(selection) ? " AND (" +selection + ')' : ""), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri ); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)){ /** * Get all student records */ case STUDENTS: return "vnd.android.cursor.dir/vnd.example.students"; /** * Get a particular student */ case STUDENT_ID: return "vnd.android.cursor.item/vnd.example.students"; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } } }
در زیر کدهای فایل AndroidManifest.xml را می بینید. در اینجا ما تگ <provider…/> را برای ثبت کردن ارائه دهنده محتوای خود اضافه کرده ایم.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.MyApplication"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <provider android:name="StudentsProvider" android:authorities="com.example.MyApplication.StudentsProvider"/> </application> </manifest>
کدهای زیر محتوای فایل res/layout/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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.MyApplication.MainActivity"> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Content provider" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:textSize="30dp" /> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Tutorials point " android:textColor="#ff87ff09" android:textSize="30dp" android:layout_below="@+id/textView1" android:layout_centerHorizontal="true" /> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imageButton" android:src="@drawable/abc" android:layout_below="@+id/textView2" android:layout_centerHorizontal="true" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button2" android:text="Add Name" android:layout_below="@+id/editText3" android:layout_alignRight="@+id/textView2" android:layout_alignEnd="@+id/textView2" android:layout_alignLeft="@+id/textView2" android:layout_alignStart="@+id/textView2" android:onClick="onClickAddName"/> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/editText" android:layout_below="@+id/imageButton" android:layout_alignRight="@+id/imageButton" android:layout_alignEnd="@+id/imageButton" /> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/editText2" android:layout_alignTop="@+id/editText" android:layout_alignLeft="@+id/textView1" android:layout_alignStart="@+id/textView1" android:layout_alignRight="@+id/textView1" android:layout_alignEnd="@+id/textView1" android:hint="Name" android:textColorHint="@android:color/holo_blue_light" /> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/editText3" android:layout_below="@+id/editText" android:layout_alignLeft="@+id/editText2" android:layout_alignStart="@+id/editText2" android:layout_alignRight="@+id/editText2" android:layout_alignEnd="@+id/editText2" android:hint="Grade" android:textColorHint="@android:color/holo_blue_bright" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Retrive student" android:id="@+id/button" android:layout_below="@+id/button2" android:layout_alignRight="@+id/editText3" android:layout_alignEnd="@+id/editText3" android:layout_alignLeft="@+id/button2" android:layout_alignStart="@+id/button2" android:onClick="onClickRetrieveStudents"/> </RelativeLayout>
مطمئن شوید که در فایل res/values/strings.xml حداقل یک رشته با نام app_name داشته باشید. به شکل زیر:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">My Application</string> </resources>;
اجازه بدهید اپلیکیشن تغییر داده شده My Application را اجرا کنیم. برای اجرای اپلیکیشن، در اندروید استدیو یکی از فایل های اکتیویتی پروژه را باز کنید و بر روی آیکن Run که در تولبار قرار دارد کلیک کنید. اندروید استدیو اپلیکیشن را بر روی گوشی نصب کرده، و اگر همه ی کارها را درست انجام داده باشید برنامه در صفحه گوشی اپلیکیشن را مانند شکل زیر خواهید دید.
حالا نام و مقطع تحصیلی(Grade) دانش آموز را وارد کنید و روی دکمه Add Name کلیک کنید. با این کار یک رکورد Student به دیتابیس اضافه می شود و یک پیام که حاوی URI ارائه دهنده محتوا به همراه شماره رکورد ثبت شده در دیتابیس می باشد، در پایین صفحه نمایش داده خواهد شد. این کار با استفاده از متد insert() انجام خواهد شد. حالا با تکرار این کار چند دانش آموز دیگر به دیتابیس ارائه دهنده محتوا اضافه کنید.
وقتی که رکورد ها را در دیتابیس اضافه کردید، زمان آن می شود که از ارائه دهنده محتوا بخواهیم که رکورد ها به به ما بدهد. برای این کار روی دکمه Retrieve Students کلیک کنید که با این کار همانطور که در متد query() تعریف کردیم تمام اطلاعات دریافت و یکی کی نمایش داده خواهد شد.
شما می توانید عملیات آپدیت و حذف را هم به اپلیکیشن اضافه کنید. برای این کار باید متد های آنها در فایل MainActivity.java بنویسید و همانطور که دکمه ها را برای عملیات اضافه کردن و خواندن در رابط گرافیکی قرار دادید، دکمه های مربوط به آپدیت و حذف را هم به رابط گرافیکی اضافه کنید.
به همین طریق شما می توانید از ارائه دهنده های موجود در اندروید مثل Address Book استفاده کنید یا می توانید از مفهوم ارائه دهنده محتوا در توسعه اپلیکیشن های دیتابیس محور استفاده کنید و همانطور که در مثال بالا توضیح داده شد ، همه انواع مختلف عملیات دیتابیسی مثل خواندن ، نوشتن ، آپدیت کردن و حذف کردن را همانطور که در مثال بالا توضیح داده شد در آن به کار بگیرید.