وظیفه تولید کننده های محتوا، ارسال و دریافت داده بین اپلیکیشن ما و اپلیکیشن های دیگر، ویجت ها و غیره است.
- نفیسه افقی 4 سال قبل سوال کرد
- آخرین ویرایش 4 سال قبل
- شما باید برای ارسال دیدگاه وارد شوید
<< اگر با مفهوم content provider یا تولید کننده محتوا آشنا نیستید، قویاً توصیه می کنم که این قسمت از آموزش اندروید را مطالعه کنید. >>
تولید کننده های محتوا ، 4 کار اصلی را روی داده انجام می دهند: CRUD (یعنی: create, read, update, delete: ساختن، خواندن، بروزرسانی و حذف). این داده می تواند ردیفی در پایگاه داده باشد یا عکسی روی گوشی شما و یا حتی ویدئویی در اینترنت.
کلاس های Contract
قبل از اینکه کلاس ContentProvider خودتان را بسازید، باید یک کلاس Contract بسازید که شکل و شمایل پایگاه داده شما را تعیین می کند. فرض کنید یک پایگاه داده برای فیلم داریم. داخل کلاس MovieContract
، باید متغیرهای زیر را تعریف کنیم:
- یک content authority: که منحصر به پایگاه داده مان است.
- یک URI پایه.
- و path name به ازای هر جدول.
/**
* The Content Authority is a name for the entire content provider, similar to the relationship
* between a domain name and its website. A convenient string to use for content authority is
* the package name for the app, since it is guaranteed to be unique on the device.
*/
public static final String CONTENT_AUTHORITY = "com.androidessence.moviedatabase";
/**
* The content authority is used to create the base of all URIs which apps will use to
* contact this content provider.
*/
private static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
/**
* A list of possible paths that will be appended to the base URI for each of the different
* tables.
*/
public static final String PATH_MOVIE = "movie";
public static final String PATH_GENRE = "genre";
برای هر جدول، باید کلاسی بسازید که از BaseColumns ارث ببرد. این کلاس دارای رشته ای بنام ID_
است (ID_
، آیدی هر جدول را مشخص کرده و بطور خودکار زیاد می شود). همچنین، برای هر جدول باید این متغیرها را داشته باشید:
- یک URI
- نوع داده بازگشتی (می تواند مجموعه ای از سطرها یا یک سطر از جدول باشد)
- تابع ساختن URI برای هر سطر از جدول.
در زیر مثالی برای جداول MovieTable و GenreTable را می بینید:
/**
* Create one class for each table that handles all information regarding the table schema and
* the URIs related to it.
*/
public static final class MovieEntry implements BaseColumns {
// Content URI represents the base location for the table
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_MOVIE).build();
// These are special type prefixes that specify if a URI returns a list or a specific item
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/" + CONTENT_URI + "/" + PATH_MOVIE;
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/" + CONTENT_URI + "/" + PATH_MOVIE;
// Define the table schema
public static final String TABLE_NAME = "movieTable";
public static final String COLUMN_NAME = "movieName";
public static final String COLUMN_RELEASE_DATE = "movieReleaseDate";
public static final String COLUMN_GENRE = "movieGenre";
// Define a function to build a URI to find a specific movie by it's identifier
public static Uri buildMovieUri(long id){
return ContentUris.withAppendedId(CONTENT_URI, id);
}
}
public static final class GenreEntry implements BaseColumns{
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_GENRE).build();
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/" + CONTENT_URI + "/" + PATH_GENRE;
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/" + CONTENT_URI + "/" + PATH_GENRE;
public static final String TABLE_NAME = "genreTable";
public static final String COLUMN_NAME = "genreName";
public static Uri buildGenreUri(long id){
return ContentUris.withAppendedId(CONTENT_URI, id);
}
}
کلاس SQLiteOpenHelper
حال که ظاهر پایگاه داده مان را تعریف کردیم، باید آن را بسازیم. برای همین منظور، از کلاس SQLiteOpenHelper استفاده می کنیم. با نوشتن تابع سازنده کلاس، که آن هم تابع سازنده کلاس والد را فراخوانی می کند، شروع می کنیم. پارامترهای آن، نام پایگاه و نسخه پایگاه داده است (هر گاه پایگاه داده بروز شود، نسخه آن هم افزایش می یابد).
public class MovieDBHelper extends SQLiteOpenHelper{
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "movieList.db";
public MovieDBHelper(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
در زیر پیاده سازی دو تابع onCreate
و onUpgrade
را می بینید که اولی وقتی پایگاه داده ساخته شود، و دومی هرگاه ورژن پایگاه داده (DATABASE_VERSION
) افزایش یابد، فراخوانی می شوند.
*در این مثال، ما برای بروزرسانی پایگاه داده کار خاصی انجام نمی دهیم.
/**
* Called when the database is first created.
* @param db The database being created, which all SQL statements will be executed on.
*/
@Override
public void onCreate(SQLiteDatabase db) {
addGenreTable(db);
addMovieTable(db);
}
/**
* Called whenever DATABASE_VERSION is incremented. This is used whenever schema changes need
* to be made or new tables are added.
* @param db The database being updated.
* @param oldVersion The previous version of the database. Used to determine whether or not
* certain updates should be run.
* @param newVersion The new version of the database.
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
در نهایت، کدهای SQL لازم برای ساختن جداول پایگاه داده را اضافه می کنیم.
/**
* Inserts the genre table into the database.
* @param db The SQLiteDatabase the table is being inserted into.
*/
private void addGenreTable(SQLiteDatabase db){
db.execSQL(
"CREATE TABLE " + MovieContract.GenreEntry.TABLE_NAME + " (" +
MovieContract.GenreEntry._ID + " INTEGER PRIMARY KEY, " +
MovieContract.GenreEntry.COLUMN_NAME + " TEXT UNIQUE NOT NULL);"
);
}
/**
* Inserts the movie table into the database.
* @param db The SQLiteDatabase the table is being inserted into.
*/
private void addMovieTable(SQLiteDatabase db){
db.execSQL(
"CREATE TABLE " + MovieContract.MovieEntry.TABLE_NAME + " (" +
MovieContract.MovieEntry._ID + " INTEGER PRIMARY KEY, " +
MovieContract.MovieEntry.COLUMN_NAME + " TEXT NOT NULL, " +
MovieContract.MovieEntry.COLUMN_RELEASE_DATE + " TEXT NOT NULL, " +
MovieContract.MovieEntry.COLUMN_GENRE + " INTEGER NOT NULL, " +
"FOREIGN KEY (" + MovieContract.MovieEntry.COLUMN_GENRE + ") " +
"REFERENCES " + MovieContract.GenreEntry.TABLE_NAME + " (" + MovieContract.GenreEntry._ID + "));"
);
}
کلاس ContentProvider
حالا که همه چیز را برای ساختن Provider خودمان مهیا کردیم، ببینیم چطور باید کلاس ContentProvider را بنویسیم. مرحله به مرحله جلو می رویم:
اول، باید برای هر URI یا کوئری که می خواهیم بنویسیم، یک integer تعریف کنیم. در این مثال، به ازای هر کوئری، دو عدد تعریف می کنیم (یکی برای تمام سطرها و یکی برای یک سطر خاص).
public class MovieProvider extends ContentProvider {
// Use an int for each URI we will run, this represents the different queries
private static final int GENRE = 100;
private static final int GENRE_ID = 101;
private static final int MOVIE = 200;
private static final int MOVIE_ID = 201;
}
سپس، می توانیم متغیرهای دیگر را تعریف کنیم. مانند: SQLiteOpenHelper (که برای دسترسی مستقیم به پایگاه داده استفاده می کنیم) و یک URIMatcher (که یک URI را می گیرد و آن را با عددی که برایش تعریف کردیم، تطبیق می دهد).
private static final UriMatcher sUriMatcher = buildUriMatcher();
private MovieDBHelper mOpenHelper;
@Override
public boolean onCreate() {
mOpenHelper = new MovieDBHelper(getContext());
return true;
}
/**
* Builds a UriMatcher that is used to determine witch database request is being made.
*/
public static UriMatcher buildUriMatcher(){
String content = MovieContract.CONTENT_AUTHORITY;
// All paths to the UriMatcher have a corresponding code to return
// when a match is found (the ints above).
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
matcher.addURI(content, MovieContract.PATH_GENRE, GENRE);
matcher.addURI(content, MovieContract.PATH_GENRE + "/#", GENRE_ID);
matcher.addURI(content, MovieContract.PATH_MOVIE, MOVIE);
matcher.addURI(content, MovieContract.PATH_MOVIE + "/#", MOVIE_ID);
return matcher;
}
*چیزی که باید اینجا توجه کنید این است که : اگر URI ای را ببینیم که با یک path مشخص خاتمه می یابد، با URI آن جدول تطبیق می دهیم ولی اگر ببینیم که یک id هم در انتهای خود دارد، این یعنی ما به دنبال یک سطر با آن id هستیم.
تابع getType
این تابع برای پیدا کردن نوع داده های خروجی استفاده می شود (خروجی می تواند مجموعه ای از آیتم ها و یک تنها یک آیتم باشد).
@Override
public String getType(Uri uri) {
switch(sUriMatcher.match(uri)){
case GENRE:
return MovieContract.GenreEntry.CONTENT_TYPE;
case GENRE_ID:
return MovieContract.GenreEntry.CONTENT_ITEM_TYPE;
case MOVIE:
return MovieContract.MovieEntry.CONTENT_TYPE;
case MOVIE_ID:
return MovieContract.MovieEntry.CONTENT_ITEM_TYPE;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
}
تابع query
این تابع 5 پارامتر در ورودی می گیرد:
- URI :URI یا جدولی که کوئری بر روی آن زده می شود.
- projection: آرایه ای از رشته ها که ستون هایی است که باید به عنوان خروجی برگردانده شوند.
- selection: رشته ای که شرط یا معیار را مشخص می کند.
- selectionArgs: آرگومان های معیار بالا که سطرها را بر اساس آن ها می سنجیم.
- sortOrder: رشته ای از ستون ها و ترتیبی که باید نتیجه را بر اساس آن مرتب کنیم.
برای اینکه از پایگاه داده کوئری بگیریم، شماره URI را مقایسه کرده و جدول مورد نظر برای کوئری را پیدا می کنیم.
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Cursor retCursor;
switch(sUriMatcher.match(uri)){
case GENRE:
retCursor = db.query(
MovieContract.GenreEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
break;
case GENRE_ID:
long _id = ContentUris.parseId(uri);
retCursor = db.query(
MovieContract.GenreEntry.TABLE_NAME,
projection,
MovieContract.GenreEntry._ID + " = ?",
new String[]{String.valueOf(_id)},
null,
null,
sortOrder
);
break;
case MOVIE:
retCursor = db.query(
MovieContract.MovieEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
break;
case MOVIE_ID:
_id = ContentUris.parseId(uri);
retCursor = db.query(
MovieContract.MovieEntry.TABLE_NAME,
projection,
MovieContract.MovieEntry._ID + " = ?",
new String[]{String.valueOf(_id)},
null,
null,
sortOrder
);
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
// Set the notification URI for the cursor to the one passed into the function. This
// causes the cursor to register a content observer to watch for changes that happen to
// this URI and any of it's descendants. By descendants, we mean any URI that begins
// with this path.
retCursor.setNotificationUri(getContext().getContentResolver(), uri);
return retCursor;
}
تابع insert
این تابع، شیئی از نوع ContentValues
را می گیرد (از نوع کلید و محتوا، یعنی به ترتیب: نام ستون و محتوایی که باید اضافه شود). همانند تابع query و توابع update و delete که در ادامه توضیح می دهیم، اینجا نیز با مقایسه URI، جدول درست را پیدا می کنیم.
@Override
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long _id;
Uri returnUri;
switch(sUriMatcher.match(uri)){
case GENRE:
_id = db.insert(MovieContract.GenreEntry.TABLE_NAME, null, values);
if(_id > 0){
returnUri = MovieContract.GenreEntry.buildGenreUri(_id);
} else{
throw new UnsupportedOperationException("Unable to insert rows into: " + uri);
}
break;
case MOVIE:
_id = db.insert(MovieContract.MovieEntry.TABLE_NAME, null, values);
if(_id > 0){
returnUri = MovieContract.MovieEntry.buildMovieUri(_id);
} else{
throw new UnsupportedOperationException("Unable to insert rows into: " + uri);
}
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
// Use this on the URI passed into the function to notify any observers that the uri has
// changed.
getContext().getContentResolver().notifyChange(uri, null);
return returnUri;
}
توابع update و delete
این توابع پارامترهایی را به این صورت می گیرند:
- رشته انتخابی
- آرگومان هایی برای تعیین سطرهای مورد نظر برای بروزرسانی یا حذف.
*با این تفاوت که تابع update یک شیئ از نوع ContentProvider نیز می خواهد (برای ستون هایی که در آن سطر باید بروزرسانی شوند).
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int rows; // Number of rows effected
switch(sUriMatcher.match(uri)){
case GENRE:
rows = db.delete(MovieContract.GenreEntry.TABLE_NAME, selection, selectionArgs);
break;
case MOVIE:
rows = db.delete(MovieContract.MovieEntry.TABLE_NAME, selection, selectionArgs);
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
// Because null could delete all rows:
if(selection == null || rows != 0){
getContext().getContentResolver().notifyChange(uri, null);
}
return rows;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int rows;
switch(sUriMatcher.match(uri)){
case GENRE:
rows = db.update(MovieContract.GenreEntry.TABLE_NAME, values, selection, selectionArgs);
break;
case MOVIE:
rows = db.update(MovieContract.MovieEntry.TABLE_NAME, values, selection, selectionArgs);
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
if(rows != 0){
getContext().getContentResolver().notifyChange(uri, null);
}
return rows;
}
استفاده از ContentProvider
برای استفاده از ContentPovider (حتی از داخل برنامه خودتان) باید حتماً فایل AndroidManifest.xml را بروزرسانی کنید. در نود applicaion، کد زیر را وارد کنید:
<provider
android:name=".MovieProvider"
android:authorities="com.androidessence.moviedatabase"
android:exported="false"
android:protectionLevel="signature"
android:syncable="true"/>
- نفیسه افقی 4 سال قبل پاسخ داد
- آخرین ویرایش 4 سال قبل
- شما باید برای ارسال دیدگاه وارد شوید