Pattern A | Pattern B | Pattern C |
Service API used: Activity -> Service -> Content Provider. In this version, Activity works with the Android Servcie API. If necessary, send a REST request. Activity creates a Service, Service asynchronously sends requests to a REST server, and saves the results to Content Provider (sqlite). The activity receives a data readiness notification and reads the results from the Content Provider (sqlite). | Used ContentProvider API: Activity -> Content Provider -> Service. In this case, the Activity works with the API Content Provider, which acts as a facade for the service. This approach is based on the similarity of the Content Provider API and the REST API: GET REST is equivalent to a select query to the database, POST REST is equivalent to insert, PUT REST ~ update, DELETE REST ~ delete. Activity results are also loaded from sqlite. | The Content Provider API + SyncAdapter is used: Activity -> Content Provider -> Sync Adapter. A variation of the “B” approach, which uses its own Sync Adapter instead of the service. Activity gives the command Content Provider, which redirects it to the Sync Adapter. The Sync Adapter is called from the Sync Manager, but not immediately, but at a “convenient” moment for the system. Those. delays in the execution of commands are possible. |
![]() | ![]() | ![]() |
Content a little bit. ... Again, we're not forcing you to adopt these particular design patterns.In general, do not want - do not use. It is encouraging.
public class FinchVideoContentProvider extends RESTfulContentProvider { public static final String VIDEO = "video"; public static final String DATABASE_NAME = VIDEO + ".db"; static int DATABASE_VERSION = 2; public static final String VIDEOS_TABLE_NAME = "video"; private static final String FINCH_VIDEO_FILE_CACHE = "finch_video_file_cache"; private static final int VIDEOS = 1; private static final int VIDEO_ID = 2; private static final int THUMB_VIDEO_ID = 3; private static final int THUMB_ID = 4; private static UriMatcher sUriMatcher; // Statically construct a uri matcher that can detect URIs referencing // more than 1 video, a single video, or a single thumb nail image. static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(FinchVideo.AUTHORITY, FinchVideo.Videos.VIDEO, VIDEOS); // use of the hash character indicates matching of an id sUriMatcher.addURI(FinchVideo.AUTHORITY, FinchVideo.Videos.VIDEO + "/#", VIDEO_ID); sUriMatcher.addURI(FinchVideo.AUTHORITY, FinchVideo.Videos.THUMB + "/#", THUMB_VIDEO_ID); sUriMatcher.addURI(FinchVideo.AUTHORITY, FinchVideo.Videos.THUMB + "/*", THUMB_ID); } /** uri for querying video, expects appending keywords. */ private static final String QUERY_URI = "http://gdata.youtube.com/feeds/api/videos?" + "max-results=15&format=1&q="; private DatabaseHelper mOpenHelper; private SQLiteDatabase mDb; private static class DatabaseHelper extends SQLiteOpenHelper { private DatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) { super(context, name, factory, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { createTable(sqLiteDatabase); } private void createTable(SQLiteDatabase sqLiteDatabase) { String createvideoTable = "CREATE TABLE " + VIDEOS_TABLE_NAME + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + FinchVideo.Videos.TITLE + " TEXT, " + FinchVideo.Videos.DESCRIPTION + " TEXT, " + FinchVideo.Videos.THUMB_URI_NAME + " TEXT," + FinchVideo.Videos.THUMB_WIDTH_NAME + " TEXT," + FinchVideo.Videos.THUMB_HEIGHT_NAME + " TEXT," + FinchVideo.Videos.TIMESTAMP + " TEXT, " + FinchVideo.Videos.QUERY_TEXT_NAME + " TEXT, " + FinchVideo.Videos.MEDIA_ID_NAME + " TEXT UNIQUE," + FinchVideo.Videos.THUMB_CONTENT_URI_NAME + " TEXT UNIQUE," + FinchVideo.Videos._DATA + " TEXT UNIQUE" + ");"; sqLiteDatabase.execSQL(createvideoTable); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldv, int newv) { sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + VIDEOS_TABLE_NAME + ";"); createTable(sqLiteDatabase); } } public FinchVideoContentProvider() { } public FinchVideoContentProvider(Context context) { } @Override public boolean onCreate() { FileHandlerFactory fileHandlerFactory = new FileHandlerFactory(new File(getContext().getFilesDir(), FINCH_VIDEO_FILE_CACHE)); setFileHandlerFactory(fileHandlerFactory); mOpenHelper = new DatabaseHelper(getContext(), DATABASE_NAME, null); mDb = mOpenHelper.getWritableDatabase(); return true; } @Override public SQLiteDatabase getDatabase() { return mDb; } /** * Content provider query method that converts its parameters into a YouTube * RESTful search query. * * @param uri a reference to the query for videos, the query string can * contain, "q='key_words'". The keywords are sent to the google YouTube * API where they are used to search the YouTube video database. * @param projection * @param where not used in this provider. * @param whereArgs not used in this provider. * @param sortOrder not used in this provider. * @return a cursor containing the results of a YouTube search query. */ @Override public Cursor query(Uri uri, String[] projection, String where, String[] whereArgs, String sortOrder) { Cursor queryCursor; int match = sUriMatcher.match(uri); switch (match) { case VIDEOS: // the query is passed out of band of other information passed // to this method -- its not an argument. String queryText = uri. getQueryParameter(FinchVideo.Videos.QUERY_PARAM_NAME); if (queryText == null) { // A null cursor is an acceptable argument to the method, // CursorAdapter.changeCursor(Cursor c), which interprets // the value by canceling all adapter state so that the // component for which the cursor is adapting data will // display no content. return null; } String select = FinchVideo.Videos.QUERY_TEXT_NAME + " = '" + queryText + "'"; // quickly return already matching data queryCursor = mDb.query(VIDEOS_TABLE_NAME, projection, select, whereArgs, null, null, sortOrder); // make the cursor observe the requested query queryCursor.setNotificationUri( getContext().getContentResolver(), uri); /** * Always try to update results with the latest data from the * network. * * Spawning an asynchronous load task thread, guarantees that * the load has no chance to block any content provider method, * and therefore no chance to block the UI thread. * * While the request loads, we return the cursor with existing * data to the client. * * If the existing cursor is empty, the UI will render no * content until it receives URI notification. * * Content updates that arrive when the asynchronous network * request completes will appear in the already returned cursor, * since that cursor query will match that of * newly arrived items. */ if (!"".equals(queryText)) { asyncQueryRequest(queryText, QUERY_URI + encode(queryText)); } break; case VIDEO_ID: case THUMB_VIDEO_ID: long videoID = ContentUris.parseId(uri); queryCursor = mDb.query(VIDEOS_TABLE_NAME, projection, BaseColumns._ID + " = " + videoID, whereArgs, null, null, null); queryCursor.setNotificationUri( getContext().getContentResolver(), uri); break; case THUMB_ID: String uriString = uri.toString(); int lastSlash = uriString.lastIndexOf("/"); String mediaID = uriString.substring(lastSlash + 1); queryCursor = mDb.query(VIDEOS_TABLE_NAME, projection, FinchVideo.Videos.MEDIA_ID_NAME + " = " + mediaID, whereArgs, null, null, null); queryCursor.setNotificationUri( getContext().getContentResolver(), uri); break; default: throw new IllegalArgumentException("unsupported uri: " + QUERY_URI); } return queryCursor; } /** * Provides a handler that can parse YouTube gData RSS content. * * @param requestTag unique tag identifying this request. * @return a YouTubeHandler object. */ @Override protected ResponseHandler newResponseHandler(String requestTag) { return new YouTubeHandler(this, requestTag); } /** * Provides read only access to files that have been downloaded and stored * in the provider cache. Specifically, in this provider, clients can * access the files of downloaded thumbnail images. */ @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { // only support read only files if (!"r".equals(mode.toLowerCase())) { throw new FileNotFoundException("Unsupported mode, " + mode + ", for uri: " + uri); } return openFileHelper(uri, mode); } @Override public String getType(Uri uri) { switch (sUriMatcher.match(uri)) { case VIDEOS: return FinchVideo.Videos.CONTENT_TYPE; case VIDEO_ID: return FinchVideo.Videos.CONTENT_VIDEO_TYPE; case THUMB_ID: return FinchVideo.Videos.CONTENT_THUMB_TYPE; default: throw new IllegalArgumentException("Unknown video type: " + uri); } } @Override public Uri insert(Uri uri, ContentValues initialValues) { // Validate the requested uri if (sUriMatcher.match(uri) != VIDEOS) { throw new IllegalArgumentException("Unknown URI " + uri); } ContentValues values; if (initialValues != null) { values = new ContentValues(initialValues); } else { values = new ContentValues(); } SQLiteDatabase db = getDatabase(); return insert(uri, initialValues, db); } private void verifyValues(ContentValues values) { if (!values.containsKey(FinchVideo.Videos.TITLE)) { Resources r = Resources.getSystem(); values.put(FinchVideo.Videos.TITLE, r.getString(android.R.string.untitled)); } if (!values.containsKey(FinchVideo.Videos.DESCRIPTION)) { Resources r = Resources.getSystem(); values.put(FinchVideo.Videos.DESCRIPTION, r.getString(android.R.string.untitled)); } if (!values.containsKey(FinchVideo.Videos.THUMB_URI_NAME)) { throw new IllegalArgumentException("Thumb uri not specified: " + values); } if (!values.containsKey(FinchVideo.Videos.THUMB_WIDTH_NAME)) { throw new IllegalArgumentException("Thumb width not specified: " + values); } if (!values.containsKey(FinchVideo.Videos.THUMB_HEIGHT_NAME)) { throw new IllegalArgumentException("Thumb height not specified: " + values); } // Make sure that the fields are all set if (!values.containsKey(FinchVideo.Videos.TIMESTAMP)) { Long now = System.currentTimeMillis(); values.put(FinchVideo.Videos.TIMESTAMP, now); } if (!values.containsKey(FinchVideo.Videos.QUERY_TEXT_NAME)) { throw new IllegalArgumentException("Query Text not specified: " + values); } if (!values.containsKey(FinchVideo.Videos.MEDIA_ID_NAME)) { throw new IllegalArgumentException("Media ID not specified: " + values); } } /** * The delegate insert method, which also takes a database parameter. Note * that this method is a direct implementation of a content provider method. */ @Override public Uri insert(Uri uri, ContentValues values, SQLiteDatabase db) { verifyValues(values); // Validate the requested uri int m = sUriMatcher.match(uri); if (m != VIDEOS) { throw new IllegalArgumentException("Unknown URI " + uri); } // insert the values into a new database row String mediaID = (String) values.get(FinchVideo.Videos.MEDIA_ID_NAME); Long rowID = mediaExists(db, mediaID); if (rowID == null) { long time = System.currentTimeMillis(); values.put(FinchVideo.Videos.TIMESTAMP, time); long rowId = db.insert(VIDEOS_TABLE_NAME, FinchVideo.Videos.VIDEO, values); if (rowId >= 0) { Uri insertUri = ContentUris.withAppendedId( FinchVideo.Videos.CONTENT_URI, rowId); getContext().getContentResolver().notifyChange(insertUri, null); return insertUri; } throw new IllegalStateException("could not insert " + "content values: " + values); } return ContentUris.withAppendedId(FinchVideo.Videos.CONTENT_URI, rowID); } private Long mediaExists(SQLiteDatabase db, String mediaID) { Cursor cursor = null; Long rowID = null; try { cursor = db.query(VIDEOS_TABLE_NAME, null, FinchVideo.Videos.MEDIA_ID_NAME + " = '" + mediaID + "'", null, null, null, null); if (cursor.moveToFirst()) { rowID = cursor.getLong(FinchVideo.ID_COLUMN); } } finally { if (cursor != null) { cursor.close(); } } return rowID; } @Override public int delete(Uri uri, String where, String[] whereArgs) { int match = sUriMatcher.match(uri); int affected; SQLiteDatabase db = mOpenHelper.getWritableDatabase(); switch (match) { case VIDEOS: affected = db.delete(VIDEOS_TABLE_NAME, (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; case VIDEO_ID: long videoId = ContentUris.parseId(uri); affected = db.delete(VIDEOS_TABLE_NAME, BaseColumns._ID + "=" + videoId + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); getContext().getContentResolver().notifyChange(uri, null); break; default: throw new IllegalArgumentException("unknown video element: " + uri); } return affected; } @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { getContext().getContentResolver().notifyChange(uri, null); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case VIDEOS: count = db.update(VIDEOS_TABLE_NAME, values, where, whereArgs); break; case VIDEO_ID: String videoId = uri.getPathSegments().get(1); count = db.update(VIDEOS_TABLE_NAME, values, BaseColumns._ID + "=" + videoId + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } }
Source: https://habr.com/ru/post/240543/
All Articles