How to customize an elegant content provider

How to customize an elegant content provider
Recently, during code review, we found that many people's provider definitions are not very good, and the writing is so rough that the code is not robust enough, and the readability is not strong

But since you have written the content provider, you need to call it for others. If the provider is full of holes, it's better not to write it,

Either don't let other apps crud your data, or let your app directly use db to operate data. Since you want to write a provider, you need to write a standard

Elegant ~ ~ put an instance of the provider here. There are a lot of comments telling you why you should write this. Like me, people who have the habit of code cleanliness can refer to the following.

	package com.example.providertest;
	
	import android.net.Uri;
	import android.provider.BaseColumns;
	
	/**
	* Constant class
	*/
	public final class StudentProfile {
	
	/**
	* Generally speaking, our authority is set to the package name + class name of our constant class
	*/
	public static final String AUTHORITY = "com.example.providertest.StudentProfile";
	
	/**
	* Note that this constructor is private so that it cannot be initialized
	*/
	private StudentProfile() {
	
	}
	
	/**
	* Implementing the BaseColumns interface allows us to write fewer lines of code
	* 
	*/
	public static final class Students implements BaseColumns {
	 /**
	  * This class also cannot be initialized
	  */
	 private Students() {
	
	 }
	
	 // Define our table name
	 public static final String TABLE_NAME = "students";
	
	 /**
	  * Let's start the definition of uri
	  */
	
	 // The scheme part of uri is fixed
	 private static final String SCHEME = "content://";
	
	 // Some students
	 private static final String PATH_STUDENTS = "/students";
	
	 // A student
	 private static final String PATH_STUDENTS_ID = "/students/";
	
	 /**
	  * path The first value here is the position we set to the first position
	  */
	 public static final int STUDENT_ID_PATH_POSITION = 1;
	
	 // The basic uri format of this table
	 public static final Uri CONTENT_URI = Uri.parse(SCHEME + AUTHORITY
	         + PATH_STUDENTS);
	 // The basic uri format of a data is usually called in the insert method of a custom provider
	 public static final Uri CONTENT_ID_URI_BASE = Uri.parse(SCHEME
	         + AUTHORITY + PATH_STUDENTS_ID);
	
	 /**
	  * Define our mime type. Pay attention to the writing of mime type
	  * 
	  * Generally, it is the package name and table name of the vnd. Application
	  */
	
	 // mime type of multiline
	 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.example.providertest.students";
	 // Single line mime type
	 public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.google.com.example.providertest.students";
	
	 /**
	  * Now that the provider provides the query method, we must set a default sorting method. Here, by default, we let the provider sort in descending order according to the creation time
	  */
	 public static final String DEFAULT_SORT_ORDER = "created DESC";
	
	 /**
	  * Here is the column definition of the table
	  */
	
	 // Student's name
	 public static final String COLUMN_NAME_NAME = "name";
	 // Age of students
	 public static final String COLUMN_NAME_AGE = "age";
	 // Student ID
	 public static final String COLUMN_NAME_NUMBER = "number";
	 // When this student was created
	 public static final String COLUMN_NAME_CREATE_DATE = "created";
	 // The time of modification after this student is put into storage
	 public static final String COLUMN_NAME_MODIFICATION_DATE = "modified";
	
	}

}
package com.example.providertest;

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;
import android.util.Log;

public class StudentProfileProvider extends ContentProvider {

// Use tag to log
private static final String TAG = "StudentProfileProvider";

// Database name
private static final String DATABASE_NAME = "students_info.db";

// Database version number
private static final int DATABASE_VERSION = 1;

/**
 * A UriMatcher instance
 */
private static final UriMatcher sUriMatcher;

// The return value of a successful match here represents a successful match of multiple lines
private static final int STUDENTS = 1;

// The return value of a successful match here represents a successful match of multiple rows
private static final int STUDENTS_ID = 2;

/**
 * Note that this hash table is mainly used for the setProjectionMap method of SQLiteQueryBuilder class
 * 
 * I put the initialization of his value in the static code block, which is actually mainly for multi table query
 * 
 * For example, when you want to query multiple tables, you have two tables, one table A and one table B. when you join, you must rename A column of A table
 * 
 * For example, if you want to rename the name1 column name of table A to a.name1, you can add A key value pair, and the key is name1
 * 
 * value Just a.name1. Of course, if you don't want to rename or just query a single table, you just need key and value
 * 
 * Just write the same values for
 * 
 */
private static HashMap<String, String> sStudentsProjectionMap;

// Define database helper
private DatabaseHelper mOpenHelper;

// Static code block execution
static {

    // First construct the URI matcher
    sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    sUriMatcher.addURI(StudentProfile.AUTHORITY, "students", STUDENTS);

    // #For any number * generally for any text
    sUriMatcher.addURI(StudentProfile.AUTHORITY, "students/#", STUDENTS_ID);

    // Because we have a single table query here, we can write the values of key and value as fixed values
    sStudentsProjectionMap = new HashMap<String, String>();

    sStudentsProjectionMap.put(StudentProfile.Students._ID,
            StudentProfile.Students._ID);

    sStudentsProjectionMap.put(StudentProfile.Students.COLUMN_NAME_AGE,
            StudentProfile.Students.COLUMN_NAME_AGE);

    sStudentsProjectionMap.put(
            StudentProfile.Students.COLUMN_NAME_CREATE_DATE,
            StudentProfile.Students.COLUMN_NAME_CREATE_DATE);

    sStudentsProjectionMap.put(
            StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE,
            StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE);

    sStudentsProjectionMap.put(StudentProfile.Students.COLUMN_NAME_NAME,
            StudentProfile.Students.COLUMN_NAME_NAME);

    sStudentsProjectionMap.put(StudentProfile.Students.COLUMN_NAME_NUMBER,
            StudentProfile.Students.COLUMN_NAME_NUMBER);
}

@Override
public boolean onCreate() {
    // TODO Auto-generated method stub
    mOpenHelper = new DatabaseHelper(getContext());
    return true;
}

/**
 * For the custom content provider, CRUD's methods should be written in such a way that the code is beautiful and fault-tolerant
 * 
 */

@Override
public Cursor query(Uri uri, String[] projection, String selection,
        String[] selectionArgs, String sortOrder) {
    SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
    qb.setTables(StudentProfile.Students.TABLE_NAME);

    // First match uri.
    switch (sUriMatcher.match(uri)) {
    // Multi line query
    case STUDENTS:
        qb.setProjectionMap(sStudentsProjectionMap);
        break;
    // Single line query
    case STUDENTS_ID:
        qb.setProjectionMap(sStudentsProjectionMap);
        qb.appendWhere(StudentProfile.Students._ID
                + "="
                + uri.getPathSegments().get(
                        StudentProfile.Students.STUDENT_ID_PATH_POSITION));
        break;
    default:
        throw new IllegalArgumentException("Unknown uri" + uri);
    }

    // If the order by value is not passed, we will use the default
    String orderBy;
    if (TextUtils.isEmpty(sortOrder)) {
        orderBy = StudentProfile.Students.DEFAULT_SORT_ORDER;
    } else {
        // If it's transmitted, use the transmitted value
        orderBy = sortOrder;
    }

    // Start database operation
    SQLiteDatabase db = mOpenHelper.getReadableDatabase();

    Cursor c = qb.query(db, projection, selection, selectionArgs, null,
            null, orderBy);

    // This place needs to explain the function of this statement. Many people forget to define the provider in the query method
    // When writing this sentence, some people don't know what it is. In fact, this sentence adds an observer to our cursor
    // If you are interested, you can look at the source code of this function in the sdk, which is very simple. So its actual function is to return the cursor
    // When it is used in an adapter like SimpleCursorAdapter, once the provider data corresponding to the uri changes
    // Then the data in this adapter will change and refresh itself. That's what this sentence does. You can write your own code if you are interested
    // Verify that if you delete this sentence, the data in the adapter will not be automatically updated when the uri is updated
    c.setNotificationUri(getContext().getContentResolver(), uri);
    return c;
}

/**
 * The return value of this place must be the same as that of the data field when you configure the activity in the manifest. Otherwise, an error will be reported
 */
@Override
public String getType(Uri uri) {
    switch (sUriMatcher.match(uri)) {
    case STUDENTS:
        return StudentProfile.Students.CONTENT_TYPE;
    case STUDENTS_ID:
        return StudentProfile.Students.CONTENT_ITEM_TYPE;
    default:
        // Pay attention to this place. Remember to throw exception information when there is a mismatch, so that when the call fails, you will know what's wrong
        throw new IllegalArgumentException("Unknown uri" + uri);
    }

}

@Override
public Uri insert(Uri uri, ContentValues initialValues) {

    if (sUriMatcher.match(uri) != STUDENTS) {
        throw new IllegalArgumentException("Unknown URI " + uri);
    }

    ContentValues values;

    if (initialValues != null) {
        values = new ContentValues(initialValues);
    } else {
        values = new ContentValues();
    }

    // The following lines of code actually tell us that for some tables, the default field values can be written in insert
    // Do not let the caller do the repetitive work manually. We should allow the caller to write the minimum value of the field to complete the db insert
    // operation
    Long now = Long.valueOf(System.currentTimeMillis());

    if (values.containsKey(StudentProfile.Students.COLUMN_NAME_CREATE_DATE) == false) {
        values.put(StudentProfile.Students.COLUMN_NAME_CREATE_DATE, now);
    }
    if (values
            .containsKey(StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE) == false) {
        values.put(StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE,
                now);
    }

    SQLiteDatabase db = mOpenHelper.getWritableDatabase();

    long rowId = db.insert(StudentProfile.Students.TABLE_NAME,
            StudentProfile.Students.COLUMN_NAME_NAME, values);

    if (rowId > 0) {
        Uri stuUri = ContentUris.withAppendedId(
                StudentProfile.Students.CONTENT_ID_URI_BASE, rowId);
        // Used to inform all observers that the data has changed
        getContext().getContentResolver().notifyChange(stuUri, null);
        return stuUri;
    }

    // If the insertion fails, it is better to throw an exception to notify the caller
    throw new SQLException("Failed to insert row into " + uri);

}

@Override
public int delete(Uri uri, String where, String[] whereArgs) {
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    String finalWhere;

    int count;

    switch (sUriMatcher.match(uri)) {

    case STUDENTS:
        count = db.delete(StudentProfile.Students.TABLE_NAME, where,
                whereArgs);
        break;

    case STUDENTS_ID:
        finalWhere = StudentProfile.Students._ID
                + " = "
                + uri.getPathSegments().get(
                        StudentProfile.Students.STUDENT_ID_PATH_POSITION);

        if (where != null) {
            finalWhere = finalWhere + " AND " + where;
        }

        count = db.delete(StudentProfile.Students.TABLE_NAME, finalWhere,
                whereArgs);
        break;

    default:
        throw new IllegalArgumentException("Unknown URI " + uri);
    }

    getContext().getContentResolver().notifyChange(uri, null);

    return count;
}

@Override
public int update(Uri uri, ContentValues values, String where,
        String[] whereArgs) {
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    int count;
    String finalWhere;

    switch (sUriMatcher.match(uri)) {

    case STUDENTS:

        count = db.update(StudentProfile.Students.TABLE_NAME, values,
                where, whereArgs);
        break;

    case STUDENTS_ID:

        finalWhere = StudentProfile.Students._ID
                + " = "
                + uri.getPathSegments().get(
                        StudentProfile.Students.STUDENT_ID_PATH_POSITION);

        if (where != null) {
            finalWhere = finalWhere + " AND " + where;
        }

        count = db.update(StudentProfile.Students.TABLE_NAME, values,
                finalWhere, whereArgs);
        break;
    default:
        throw new IllegalArgumentException("Unknown URI " + uri);
    }

    getContext().getContentResolver().notifyChange(uri, null);

    return count;
}

// Custom helper
static class DatabaseHelper extends SQLiteOpenHelper {

    DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // TODO Auto-generated method stub
        db.execSQL("CREATE TABLE " + StudentProfile.Students.TABLE_NAME
                + " (" + StudentProfile.Students._ID
                + " INTEGER PRIMARY KEY,"
                + StudentProfile.Students.COLUMN_NAME_NAME + " TEXT,"
                + StudentProfile.Students.COLUMN_NAME_NUMBER + " TEXT,"
                + StudentProfile.Students.COLUMN_NAME_AGE + " INTEGER,"
                + StudentProfile.Students.COLUMN_NAME_CREATE_DATE
                + " INTEGER,"
                + StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE
                + " INTEGER" + ");");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // TODO Auto-generated method stub
        // When upgrading the database, the code here will not be written. Look at the business logic. It is generally recommended that you type more logs in this place
    }

}
}

Published 31 original articles, won 0 praise and 200 visitors
Private letter follow

Tags: Android Database SQLite Google

Posted on Sun, 12 Jan 2020 07:34:50 -0800 by SulleyMonstersInc