我是Android新手。我想练习有关Content Provide的知识,但是我的应用程序以某种方式崩溃并告诉我有未知的URL内容。我尝试搜索解决方案,但仍然无法解决问题...
logcat消息:
2020-05-25 20:10:24.547 15120-15120/com.example.punchinandout E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.punchinandout, PID: 15120
java.lang.IllegalArgumentException: Unknown URL content://com.example.punchinandout.data/time
at android.content.ContentResolver.insert(ContentResolver.java:1535)
at com.example.punchinandout.MainActivity$1.onClick(MainActivity.java:65)
at android.view.View.performClick(View.java:6256)
at android.view.View$PerformClick.run(View.java:24701)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
我在Mainifest中的提供者:
<provider
android:authorities="com.example.punchinandout"
android:name=".data.TimeProvider"
android:exported="false"/>
我的内容提供商类:
package com.example.punchinandout.data;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;
import com.example.punchinandout.data.TimeContract.TimeEntry;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class TimeProvider extends ContentProvider {
private static final String TAG = TimeProvider.class.toString();
private static final int TIME = 100;
private static final int TIME_ID = 101;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//Determine which content URI we have.
static{
sUriMatcher.addURI(TimeContract.CONTENT_AUTHORITY, TimeContract.CONTENT_PATH, TIME);
sUriMatcher.addURI(TimeContract.CONTENT_AUTHORITY, TimeContract.CONTENT_PATH, TIME_ID);
}
private TimeHelper mDbHelper;
@Override
public boolean onCreate() {
mDbHelper = new TimeHelper(getContext());
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
SQLiteDatabase database = mDbHelper.getReadableDatabase();
Cursor cursor;
int match = sUriMatcher.match(uri);
switch (match){
case TIME:
cursor = database.query(TimeEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
break;
case TIME_ID:
selection = TimeEntry._ID + "=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
cursor = database.query(TimeEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
break;
default:
throw new IllegalArgumentException("Cannot query with unknown URI: " + uri);
}
cursor.setNotificationUri(getContext().getContentResolver(), uri); //Set notification URI on cursor, so that the cursor will update when the URI is updated.
return cursor;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
int match = sUriMatcher.match(uri);
switch (match){
case TIME:
return insertTimeRecord(uri, values);
default:
throw new IllegalArgumentException("Insertion is no support for " + uri);
}
}
private Uri insertTimeRecord(Uri uri, ContentValues values){
//Sanity check
if (values.containsKey(TimeEntry.COLUMN_RECORD)){
String time_record = values.getAsString(TimeEntry.COLUMN_RECORD);
if (time_record == null){
throw new IllegalArgumentException("Time record cannot be empty.");
}
}
//If there are no values to update, then don't try to update the database.
if (values.size() == 0){
return null;
}
SQLiteDatabase database = mDbHelper.getWritableDatabase();
long newRowId = database.insert(TimeEntry.TABLE_NAME, null, values);
//Check if the insertion is success.
if (newRowId == -1){
Log.e(TAG, "Insertion fail with " + uri);
return null;
}
getContext().getContentResolver().notifyChange(uri, null); //Notify all listener the data has changed
return ContentUris.withAppendedId(uri, newRowId);
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
int match = sUriMatcher.match(uri);
switch (match){
case TIME:
return updateTimeRecord(uri, values, selection, selectionArgs);
case TIME_ID:
selection = TimeEntry._ID + "=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
return updateTimeRecord(uri, values, selection, selectionArgs);
default:
throw new IllegalArgumentException("Update fail with " + uri);
}
}
private int updateTimeRecord(Uri uri, ContentValues values, String selection, String[] selectionArgs){
//Sanity check
if (values.containsKey(TimeEntry.COLUMN_RECORD)){
String time_record = values.getAsString(TimeEntry.COLUMN_RECORD);
if (time_record == null){
return 0; //If the data is not inputted, update nothing in database.
}
}
//If there are no values to update, then don't try to update the database.
if (values.size() == 0){
return 0;
}
SQLiteDatabase database = mDbHelper.getWritableDatabase();
int rowUpdated = database.update(TimeEntry.TABLE_NAME, values, selection, selectionArgs);
if (rowUpdated != 0){
getContext().getContentResolver().notifyChange(uri, null); //Notify all listener the data has changed
}
return rowUpdated;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
int match = sUriMatcher.match(uri);
switch (match){
case TIME:
return deleteTimeRecord(uri, selection, selectionArgs);
case TIME_ID:
selection = TimeEntry._ID + "=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
return deleteTimeRecord(uri, selection, selectionArgs);
default:
throw new IllegalArgumentException("Delete fail with " + uri);
}
}
private int deleteTimeRecord(Uri uri, String selection, String[] selectionArgs){
SQLiteDatabase database = mDbHelper.getWritableDatabase();
int rowDelected = database.delete(TimeEntry.TABLE_NAME, selection, selectionArgs);
if (rowDelected != 0){
getContext().getContentResolver().notifyChange(uri, null); //Notify all listener the data has changed
}
return rowDelected;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
int match = sUriMatcher.match(uri);
switch (match){
case TIME:
return TimeEntry.CONTENT_LIST_TYPE;
case TIME_ID:
return TimeEntry.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknown URI " + uri + " with match " + match);
}
}
}
我的合同类别:
package com.example.punchinandout.data;
import android.content.ContentResolver;
import android.net.Uri;
import android.provider.BaseColumns;
public class TimeContract {
private TimeContract(){};
public static final String CONTENT_AUTHORITY = "com.example.punchinandout.data";
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
public static final String CONTENT_PATH = "time";
public static final class TimeEntry implements BaseColumns {
public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH);
//MIME type of all records.
public static final String CONTENT_LIST_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + CONTENT_PATH;
//MIME type of single record.
public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + CONTENT_PATH;
public static final String TABLE_NAME = "time";
public static final String _ID = BaseColumns._ID;
public static final String COLUMN_RECORD = "record";
}
}
有人可以帮我吗? :(
我已经解决了问题!
我在Mainifest中的提供者是:
<provider
android:authorities="com.example.punchinandout"
android:name=".data.TimeProvider"
android:exported="false"/>
然后我变成:
<provider
android:authorities="com.example.punchinandout.data"
android:name=".data.TimeProvider"
android:exported="false"/>
让权限与合同类中的权限相同,并且问题已解决!
很抱歉第一次没有将Mainifest放在问题中,因为我之前都不知道应用程序中的问题在哪里。