Best practices for exposing multiple tables using content providers in Android

前端 未结 5 487
迷失自我
迷失自我 2020-12-04 05:19

I\'m building an app where I have a table for events and a table for venues. I want to be able to grant other applications access to this data. I have a few questions relate

5条回答
  •  一向
    一向 (楼主)
    2020-12-04 05:52

    I found best demo and explanation for ContentProvider and I think it has followed Android Standards.

    Contract Classes

     /**
       * 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";
    

    and Inner Classes:

     /**
       * 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);
          }
      }
    

    Now creating Database using SQLiteOpenHelper:

    public class MovieDBHelper extends SQLiteOpenHelper{
        /**
         * Defines the database version. This variable must be incremented in order for onUpdate to
         * be called when necessary.
         */
        private static final int DATABASE_VERSION = 1;
        /**
         * The name of the database on the device.
         */
        private static final String DATABASE_NAME = "movieList.db";
    
        /**
         * Default constructor.
         * @param context The application context using this database.
         */
        public MovieDBHelper(Context context){
            super(context, DATABASE_NAME, null, 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) {
    
        }
    
        /**
         * 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 + "));"
            );
        }
    }
    

    Content Provider:

    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;
    
        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;
        }
    
        @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);
            }
        }
    
        @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;
        }
    
        @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;
        }
    
        @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;
        }
    }
    

    I hope it will helps you.

    Demo on GitHub: https://github.com/androidessence/MovieDatabase

    Full Article : https://guides.codepath.com/android/creating-content-providers

    References:

    • http://code.tutsplus.com/tutorials/android-sdk_content-providers--mobile-5549

    • http://www.grokkingandroid.com/android-tutorial-writing-your-own-content-provider/

    • http://developer.android.com/guide/topics/providers/content-providers.html

    • https://thenewcircle.com/s/post/1375/android_content_provider_tutorial

    • http://www.grokkingandroid.com/android-tutorial-content-provider-basics/

    • http://androidessence.com/

    Note : I copied code just because if link of demo or article may be remove in future.

提交回复
热议问题