ContentProvider應(yīng)該知道的

ContentProvider 提供了一個(gè)接口用來(lái)發(fā)布數(shù)據(jù)尖淘,通過(guò)ContentResolver 來(lái)使用該數(shù)據(jù)叹哭。它們?cè)试S將使用數(shù)據(jù)的應(yīng)用程序組件和底層數(shù)據(jù)源分離開(kāi)來(lái)晰绎,并提供一種通用的機(jī)制來(lái)允許一個(gè)應(yīng)用程序共享它們的數(shù)據(jù)或者使用其它應(yīng)用程序供的數(shù)據(jù)彼硫。

一吃溅、Uri介紹

Uri代表要操作的數(shù)據(jù)溶诞,Uri主要包含了兩部分信息:

  • 需要操作的ContentProvider
  • 對(duì)ContentProvider中的什么數(shù)據(jù)進(jìn)行操作

URI主要分三個(gè)部分:scheme, authority and path。其中authority又分為hostport决侈。

  • 格式如下:scheme://host:port/path

ContentProvider(內(nèi)容提供者)的scheme已經(jīng)由Android所規(guī)定螺垢, scheme為:content://主機(jī)名(或叫Authority)用于唯一標(biāo)識(shí)這個(gè)ContentProvider,外部調(diào)用者可以根據(jù)這個(gè)標(biāo)識(shí)來(lái)找到它赖歌。路徑(path)可以用來(lái)表示我們要操作的數(shù)據(jù)枉圃,路徑的構(gòu)建應(yīng)根據(jù)業(yè)務(wù)而定。

如下以person 表為例

  1. 要操作person表中id為10的記錄庐冯,可以構(gòu)建這樣的路徑:/person/10
  2. 要操作person表中id為10的記錄的name字段讯蒲, person/10/name
  3. 要操作person表中的所有記錄,可以構(gòu)建這樣的路徑:/person
  4. 要操作xxx表中的記錄肄扎,可以構(gòu)建這樣的路徑:/xxx
    使用Uri類中的parse()方法把一個(gè)字符串轉(zhuǎn)換成Uri墨林,如下:
    Uri uri = Uri.parse("content://com.cfox.contentprovid.PersonProvider/person")
    當(dāng)然要操作的數(shù)據(jù)不一定來(lái)自數(shù)據(jù)庫(kù),也可以是文件犯祠、xml或網(wǎng)絡(luò)等其他存儲(chǔ)方.

二旭等、UriMatcher和ContentUris使用介紹

因?yàn)閁ri代表了要操作的數(shù)據(jù),所以我們經(jīng)常需要解析Uri衡载,并從Uri中獲取數(shù)據(jù)搔耕。Android系統(tǒng)提供了兩個(gè)用于操作Uri的工具類,分別為UriMatcherContentUris 痰娱。掌握它們的使用弃榨,會(huì)便于我們的開(kāi)發(fā)工作。

UriMatcher

UriMatcher類用于匹配Uri梨睁,它的用法如下:

  • 首先第一步把你需要匹配Uri路徑全部給注冊(cè)上
    如下:

        //常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼
        UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        //如果match()方法匹配content://com.cfox.contentprovid.PersonProvider/person路徑鲸睛,返回匹配碼為1
        sMatcher.addURI("com.cfox.contentprovid.PersonProvider", "person", 1);//添加需要匹配uri,如果匹配就會(huì)返回匹配碼
        //如果match()方法匹配content://com.cfox.contentprovid.PersonProvider/person/230路徑坡贺,返回匹配碼為2
        sMatcher.addURI("com.cfox.contentprovid.PersonProvider", "person/#", 2);//#號(hào)為通配符
        switch (sMatcher.match(Uri.parse("content://com.cfox.contentprovid.PersonProvider/person/10"))) { 
             case 1
             break;
             case 2
             break;
             default://不匹配
        }
    

注冊(cè)完需要匹配的Uri后官辈,就可以使用sMatcher.match(uri)方法對(duì)輸入的Uri進(jìn)行匹配。如果匹配就返回匹配碼遍坟,匹配碼是調(diào)用 addURI()方法傳入的第三個(gè)參數(shù)拳亿,假設(shè)匹配content://com.cfox.contentprovid.PersonProvider路徑,返回的匹配碼為1愿伴。

ContentUris使用介紹

ContentUris類用于操作Uri路徑后面的ID部分肺魁,它有兩個(gè)比較實(shí)用的方法:

  • withAppendedId(uri, id)用于為路徑加上ID部分:

    Uri uri = Uri.parse("content://com.cfox.contentprovid.PersonProvider/person")
    Uri resultUri = ContentUris.withAppendedId(uri, 10); 
    //生成后的Uri為:content://com.cfox.contentprovid.PersonProvider/person/10
    
  • parseId(uri)方法用于從路徑中獲取ID部分:

    Uri uri = Uri.parse("content://com.cfox.contentprovid.PersonProvider/person/10")
    long personid = ContentUris.parseId(uri);//獲取的結(jié)果為:10
    

三、ContentProvider(內(nèi)容提供者)共享數(shù)據(jù)

ContentProvider在android中的作用是對(duì)外共享數(shù)據(jù)隔节,可以通過(guò)ContentProvider把應(yīng)用中的數(shù)據(jù)共享給其他應(yīng)用訪問(wèn)鹅经,其他應(yīng)用可以通過(guò)ContentProvider對(duì)你應(yīng)用中的數(shù)據(jù)進(jìn)行增刪改查胡桨。關(guān)于數(shù)據(jù)共享,以前我們學(xué)習(xí)過(guò)文件操作模式瞬雹,知道通過(guò)指定文件的操作模式為Context.MODE_WORLD_READABLE或Context.MODE_WORLD_WRITEABLE同樣也可以對(duì)外共享數(shù)據(jù)昧谊。那么,這里為何要使用ContentProvider對(duì)外共享數(shù)據(jù)呢酗捌?是這樣的呢诬,如果采用文件操作模式對(duì)外共享數(shù)據(jù),數(shù)據(jù)的訪問(wèn)方式會(huì)因數(shù)據(jù)存儲(chǔ)的方式而不同胖缤,導(dǎo)致數(shù)據(jù)的訪問(wèn)方式無(wú)法統(tǒng)一尚镰,如:采用xml文件對(duì)外共享數(shù) 據(jù),需要進(jìn)行xml解析才能讀取數(shù)據(jù)哪廓;采用sharedpreferences共享數(shù)據(jù)狗唉,需要使用sharedpreferences API讀取數(shù)據(jù)。使用ContentProvider對(duì)外共享數(shù)據(jù)的好處是統(tǒng)一了數(shù)據(jù)的訪問(wèn)方式涡真。

ContentProvider對(duì)外共享數(shù)

第一步需要繼承ContentProvider并重寫下面方法:

public class BaseContentProvider extends ContentProvider {

    @Override
    public boolean onCreate() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getType(Uri uri) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }
}

ContentProvider類主要方法的作用:

  1. public boolean onCreate():該方法在ContentProvider創(chuàng)建后就會(huì)被調(diào)用分俯,應(yīng)用程序主線程上的所有已注冊(cè)內(nèi)容提供程序都會(huì)調(diào)用此方法。
  2. public Uri insert(Uri uri, ContentValues values):該方法用于供外部應(yīng)用往ContentProvider添加數(shù)據(jù)哆料。
  3. public int delete(Uri uri, String selection, String[] selectionArgs):該方法用于供外部應(yīng)用從ContentProvider刪除數(shù)據(jù)缸剪。
  4. public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):該方法用于供外部應(yīng)用更新ContentProvider中的數(shù)據(jù)。
  5. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):該方法用于供外部應(yīng)用從ContentProvider中獲取數(shù)據(jù)东亦。
  6. public String getType(Uri uri):該方法用于返回當(dāng)前Url所代表數(shù)據(jù)的MIME類型杏节。

如果操作的數(shù)據(jù)屬于集合類型,那么MIME類型字符串應(yīng)該以vnd.android.cursor.dir/開(kāi)頭典阵。

例如:要得到所有person記錄的Uri為content://com.cfox.contentprovid.PersonProvider/person
     那么返回的MIME類型字符串應(yīng)該為:"vnd.android.cursor.dir/person"

如果要操作的數(shù)據(jù)屬于非集合類型數(shù)據(jù)奋渔,那么MIME類型字符串應(yīng)該以vnd.android.cursor.item/開(kāi)頭

例如:得到id為10的person記錄,Uri為content://com.cfox.contentprovid.PersonProvider/person/10
     那么返回的MIME類型字符串為:"vnd.android.cursor.item/person"
public String getType(Uri uri) {
        UriMatcher uriMatcher = getUriMatcher();
        

        int match = uriMatcher.match(uri);
        switch (match) {
        case ITEMS:
            return "vnd.android.cursor.dir/" + person;
        case ITEMS_ID:
            return "vnd.android.cursor.item/" + person;
        default:
            throw new IllegalArgumentException("Unknown URI: " + uri);
        }
    }

第二步需要在AndroidManifest.xml使用對(duì)該ContentProvider進(jìn)行配置壮啊,為了能讓其他應(yīng)用找到該ContentProvider 嫉鲸,ContentProvider采用了authorities(主機(jī)名/域名)對(duì)它進(jìn)行唯一標(biāo)識(shí),你可以把ContentProvider看作是一個(gè)網(wǎng) 站(想想他巨,網(wǎng)站也是提供數(shù)據(jù)者)充坑,authorities 就是他的域名:

<provider
    android:exported="true"
    android:name="com.example.contentprovideranddb.ContentProvider.PersonProvider"
    android:authorities="com.cfox.contentprovid.PersonProvider" />

四、使用ContentResolver操作ContentProvider中的數(shù)據(jù)

當(dāng)外部應(yīng)用需要對(duì)ContentProvider中的數(shù)據(jù)進(jìn)行添加染突、刪除、修改和查詢操作時(shí)辈灼,可以使用ContentResolver 類來(lái)完成份企,要獲取ContentResolver 對(duì)象,可以使用Activity提供的getContentResolver()方法巡莹。 ContentResolver 類提供了與ContentProvider類相同簽名的四個(gè)方法:

public Uri insert(Uri uri, ContentValues values):該方法用于往ContentProvider添加數(shù)據(jù)司志。
public int delete(Uri uri, String selection, String[] selectionArgs):該方法用于從ContentProvider刪除數(shù)據(jù)甜紫。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):該方法用于更新ContentProvider中的數(shù)據(jù)。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):該方法用于從ContentProvider中獲取數(shù)據(jù)骂远。

這些方法的第一個(gè)參數(shù)為Uri囚霸,代表要操作的ContentProvider和對(duì)其中的什么數(shù)據(jù)進(jìn)行操作, 例如 Uri是:Uri.parse("content://com.cfox.contentprovid.PersonProvider/person /10")激才,那么將會(huì)對(duì)主機(jī)名為com.cfox.contentprovid.PersonProviderContentProvider進(jìn)行操作拓型,操作的數(shù) 據(jù)為person表中id為10的記錄。 使用ContentResolver對(duì)ContentProvider中的數(shù)據(jù)進(jìn)行添加瘸恼、刪除劣挫、修改和查詢操作。

五东帅、監(jiān)聽(tīng)ContentProvider中數(shù)據(jù)的變化

如果ContentProvider的訪問(wèn)者需要知道ContentProvider中的數(shù)據(jù)發(fā)生變化压固,可以在ContentProvider發(fā)生數(shù)據(jù)變化時(shí)調(diào)用getContentResolver().notifyChange(uri, null)來(lái)通知注冊(cè)在此URI上的訪問(wèn)者,例子如下:

public class BaseContentProvider extends ContentProvider {

    略.........

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        
    //添加數(shù)據(jù)變化通知
    getContext().getContentResolver().notifyChange(uri, null);
        return 返回一個(gè)Uri;
    }
    略........

}

如果ContentProvider的訪問(wèn)者需要得到數(shù)據(jù)變化通知靠闭,必須使用ContentObserver對(duì)數(shù)據(jù)(數(shù)據(jù)采用uri描述)進(jìn)行監(jiān)聽(tīng)帐我,當(dāng)監(jiān)聽(tīng)到數(shù)據(jù)變化通知時(shí),系統(tǒng)就會(huì)調(diào)用ContentObserveronChange()方法:

public class MainActivity extends Activity {
    private Handler mHandler = new Handler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.getContentResolver().registerContentObserver(PersonProvider.uri, false, new MyObserver(mHandler));
    }
    
    public class MyObserver extends ContentObserver{

        public MyObserver(Handler handler) {
            super(handler);
        }
        
        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            //數(shù)據(jù)改變時(shí)調(diào)用
            System.out.println("changing ------>");
        }
    }
}

六愧膀、ContentProvider 進(jìn)行批量操作

  1. 批量插入
    ContentProvider 中有特定的方法進(jìn)行批量插入bulkInsert焚刚, 在ContentProvider 中從寫這個(gè)方法就可以實(shí)現(xiàn)批量插入的功能。下面來(lái)看一下扇调, 在實(shí)際開(kāi)發(fā)中的一種應(yīng)用矿咕。

    @Override
    public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
        int result;
        synchronized (this) {
            SQLiteDatabase db = mDBHelper.getWritableDatabase();
            try {
                db.beginTransaction();
                result = super.bulkInsert(uri, values);
                db.setTransactionSuccessful();
            }finally {
                db.endTransaction();
            }
        }
        return result;
    }
    

    從上面代碼中可以看出,是為了使用事務(wù)批量插入數(shù)據(jù)提高插入效率狼钮。代碼很簡(jiǎn)單碳柱,不詳細(xì)介紹,這里說(shuō)一下super.bulkInsert(uri, values); 這個(gè)行代碼熬芜, 下面看一下ContentProvider 類中是怎么實(shí)現(xiàn)的莲镣。

    public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
        int numValues = values.length;
        for (int i = 0; i < numValues; i++) {
            insert(uri, values[i]);
        }
        return numValues;
    }
    

    看到insert 相信很多人,就明白了涎拉,這里是遍歷ContentValues 中的每一個(gè)value 瑞侮, 最后還是通過(guò)insert 方法進(jìn)行操作。

    調(diào)用方法
    通過(guò)ContentResolver 中bulkInsert 方法進(jìn)行批量操作:

    ContentResolver mcr = getContentResolver();
    ----
    ContentValues value1 = new ContentValues();
    ContentValues value2 = new ContentValues();
    ContentValues value3 = new ContentValues();
    ContentValues[] contentValues = {value1, value2, value3};
    int num = mcr.bulkInsert(mUri, contentValues);
    
  2. 批量操作
    在ContentProvider中提供了一個(gè)功能更加豐富的批量操作方法applyBatch鼓拧,使用這個(gè)可以進(jìn)行增半火、刪、 改季俩、 批量操作钮糖。還是先從一個(gè)使用例子開(kāi)始。

    @Override
    public ContentProviderResult[] applyBatch(@NonNull ArrayList<ContentProviderOperation> operations) throws OperationApplicationException {
        ContentProviderResult[] results;
        synchronized (this) {
            SQLiteDatabase db = mDBHelper.getWritableDatabase();
            try {
                db.beginTransaction();
                results = super.applyBatch(operations);
                db.setTransactionSuccessful();
            }finally {
                db.endTransaction();
            }
        }
        return results;
    }
    

    看了上面代碼和批量插入有些類似酌住,在這些代碼中要注意兩段代碼店归,ContentProviderOperationsuper.applyBatch(operations);

    下面先來(lái)看看如何使用:

    private static final String AUTHORITY = "com.cfox.contentprovid.PersonProvider";
    ----
    ContentResolver mcr = getContentResolver();
    ---- 
    ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
    ContentValues values = new ContentValues();
    operations.add(ContentProviderOperation.newInsert(mUri).withValues(values).build());
    
    ContentValues values = new ContentValues();
    operations.add(ContentProviderOperation.newUpdate(mUri)
            .withSelection("name = ?" , new String[]{"xxx"})
            .withValues(values).build());
            
    operations.add(ContentProviderOperation.newDelete(mUri)
            .withSelection("name = ?" , new String[]{"xxx"}).build());
            
    try {
        mcr.applyBatch(AUTHORITY, operations);
    } catch (OperationApplicationException e) {
        e.printStackTrace();
    } catch (RemoteException e) {
        e.printStackTrace();
    }
    

    上面代碼不講解阎抒,下面通過(guò)源碼介紹一下在ContentProvider中是怎么進(jìn)行批量操作的。
    先看一下ContentProviderOperation 中的 newInsert消痛、newUpdate且叁、newDelete 三個(gè)方法實(shí)現(xiàn):

    public static Builder newInsert(Uri uri) {
        return new Builder(TYPE_INSERT, uri);
    }
    
    public static Builder newUpdate(Uri uri) {
        return new Builder(TYPE_UPDATE, uri);
    }
    
    public static Builder newDelete(Uri uri) {
        return new Builder(TYPE_DELETE, uri);
    }
    

    三個(gè)方法都是創(chuàng)建了一個(gè)Builder實(shí)例,返回的也是Builder對(duì)象而不是ContentProviderOperation秩伞,不同的是傳入的type不同逞带。那么在什么時(shí)候創(chuàng)建ContentProviderOperation呢?在我們調(diào)用Builder實(shí)例中的build()方法是創(chuàng)建稠歉。下面來(lái)看一下具體代碼:

    public static class Builder {
        private final int mType;
    .... //省略部分代碼
        private Builder(int type, Uri uri) {
            if (uri == null) {
                throw new IllegalArgumentException("uri must not be null");
            }
            mType = type;
            mUri = uri;
        }
            
    .... //省略部分代碼
    }
    

    build的時(shí)候?qū)?code>mType設(shè)置給ContentProviderOperation中的mType.所以才有下面使用mType 判斷是什么操作掰担。

    在看一下ContentProvider 中applyBatch 方法實(shí)現(xiàn):

    public @NonNull ContentProviderResult[] applyBatch(
            @NonNull ArrayList<ContentProviderOperation> operations)
                    throws OperationApplicationException {
        final int numOperations = operations.size();
        final ContentProviderResult[] results = new ContentProviderResult[numOperations];
        for (int i = 0; i < numOperations; i++) {
            results[i] = operations.get(i).apply(this, results, i);
        }
        return results;
    }
    

    上面代碼就是遍歷出list中的ContentProviderOperation然后調(diào)用apply方法,但是怒炸,到這里要注意apply方法中有一個(gè)參數(shù)是this带饱。繼續(xù)查看ContentProviderOperationapply方法實(shí)現(xiàn):

    public ContentProviderResult apply(ContentProvider provider, ContentProviderResult[] backRefs,
            int numBackRefs) throws OperationApplicationException {
        ContentValues values = resolveValueBackReferences(backRefs, numBackRefs);
        String[] selectionArgs =
                resolveSelectionArgsBackReferences(backRefs, numBackRefs);
    
        if (mType == TYPE_INSERT) {
            Uri newUri = provider.insert(mUri, values);
            if (newUri == null) {
                throw new OperationApplicationException("insert failed");
            }
            return new ContentProviderResult(newUri);
        }
    
        int numRows;
        if (mType == TYPE_DELETE) {
            numRows = provider.delete(mUri, mSelection, selectionArgs);
        } else if (mType == TYPE_UPDATE) {
            numRows = provider.update(mUri, values, mSelection, selectionArgs);
        } else if (mType == TYPE_ASSERT) {
            .... //省略部分代碼, 如果感興趣阅羹,自己閱讀研究
        } else {
            Log.e(TAG, this.toString());
            throw new IllegalStateException("bad type, " + mType);
        }
    
        if (mExpectedCount != null && mExpectedCount != numRows) {
            Log.e(TAG, this.toString());
            throw new OperationApplicationException("wrong number of rows: " + numRows);
        }
    
        return new ContentProviderResult(numRows);
    }
    

    看到這里勺疼,就會(huì)恍然大悟,原來(lái)是在這里通過(guò)mType這個(gè)屬性捏鱼,判斷增执庐、刪、改的导梆,讓后在通過(guò)傳入的ContentProvider調(diào)用繼承ContentProvider實(shí)現(xiàn)的insert轨淌、updatedelete 方法看尼。

七递鹉、ContentProvider call 方法

先看一個(gè)使用例子:
ContentProvider 實(shí)現(xiàn):

@Override
public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
    extras.putString("data", "this is data");
    return extras;
}

在activity 中調(diào)用call 實(shí)現(xiàn):

ContentResolver mcr = getContentResolver();
---- 
Bundle bundleArg = new Bundle();
bundleArg.putString("good", "hello");
Bundle bundle = mcr.call(mUri, "callMethod","call,,,,,,",bundleArg);
Log.d(TAG, "callMethod: data:" + bundle.get("data"));

調(diào)用和被調(diào)用參數(shù)都是相同的,注意:所有參數(shù)不可為NULL藏斩, 可以使用這個(gè)方法使用Bundle進(jìn)行數(shù)據(jù)交互躏结。

八、ContentProvider 文件共享

ContentProvider中用于共享大的文件的時(shí)候比較多狰域。文件會(huì)以流的形式傳遞媳拴。
下面是一個(gè)簡(jiǎn)單的使用例子:
ContentProvider 中示例:

@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
    String basePath = Environment.getExternalStorageDirectory().getPath() + "/aaa.pdf";
    File file = new File(basePath);
    ParcelFileDescriptor descriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
    return descriptor;
}

activity 中調(diào)用示例:

Uri mUri = "";
----
ContentResolver mcr = getContentResolver();
---- 
String basePath = Environment.getExternalStorageDirectory().getPath() + "/bb.pdf";
File file = new File(basePath);
if (!file.isFile()) {
    try {
        boolean b = file.createNewFile();
        Log.d(TAG, "AssetFile: b:" + b);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
try {
    AssetFileDescriptor descriptor = mcr.openAssetFileDescriptor(mUri, "r");

    InputStream in = descriptor.createInputStream();
    OutputStream out = new FileOutputStream(file);
    byte[] buffer = new byte[1024];
    int len;
    while ((len = in.read(buffer)) != -1) {
        out.write(buffer, 0 , len);
    }
    out.flush();
    in.close();
    out.close();
}catch (Exception e) {
    e.printStackTrace();
}finally {
    Log.d(TAG, "AssetFile: successs....");
}

上面代碼不解釋了,寫個(gè)demo體會(huì)一下兆览,很簡(jiǎn)單的屈溉, 這篇文章只是引路,告訴你ContentProvider 中有這些東西拓颓,具體在項(xiàng)目中如何使用语婴,還要結(jié)合實(shí)際場(chǎng)景。

九驶睦、幾點(diǎn)注意

  1. 不要在UI線程中調(diào)用砰左, 容易出現(xiàn)ANR。
  2. ContentProvideronCreate在運(yùn)行進(jìn)程的主線程中執(zhí)行场航。如果``ContentProvider和應(yīng)用運(yùn)行在同一進(jìn)程缠导,所以盡量不要在onCreate中執(zhí)行耗時(shí)的操作或者不要做任何事情,如果在onCreate`中執(zhí)行耗時(shí)的代碼溉痢,會(huì)影響應(yīng)用啟動(dòng)速度僻造。
  3. 如果ContentProvider和調(diào)用者在同一個(gè)進(jìn)程,則ContentProvider和調(diào)用者在同一個(gè)線程孩饼。
  4. 如果ContentProvider和調(diào)用者不在同一個(gè)進(jìn)程髓削,onCreate在進(jìn)程的主線程中執(zhí)行, insert镀娶、update立膛、deletequery 梯码、call等在同一個(gè)線程中執(zhí)行(非主線程)宝泵。

附:

在android4.2 在使用到 ContentProvider 的時(shí)候遇到的問(wèn)題,報(bào)錯(cuò)Permission Denial: opening provider uid=10033) .... that is not exported from uid 10036
AndroidManifest.xmlandroid:exported 屬性轩娶,這個(gè)屬性用于指示該服務(wù)是否能被其他程序應(yīng)用組件調(diào)用或跟他交互儿奶; 取值為(true | false),如果設(shè)置成true鳄抒,則能夠被調(diào)用或交互闯捎,否則不能;設(shè)置為false時(shí)许溅,只有同一個(gè)應(yīng)用程序的組件或帶有相同用戶ID的應(yīng)用程序才能啟動(dòng)或綁定該服務(wù)瓤鼻。

<provider
    android:exported="true"
    android:name="com.example.contentprovideranddb.ContentProvider.PersonProvider"
    android:authorities="com.cfox.contentprovid.PersonProvider" />
  • 重要:它的默認(rèn)值是依賴于該服務(wù)所包含的過(guò)濾器
    1. 如果沒(méi)有過(guò)濾器則意味著該服務(wù)只能通過(guò)指定明確的類名來(lái)調(diào)用,也就是說(shuō)該服務(wù)只能在應(yīng)用程序內(nèi)部使用(因?yàn)槠渌獠渴褂谜卟粫?huì)知道該服務(wù)的類名)闹司,此時(shí)它的默認(rèn)值是false
    2. 如果至少包含了一個(gè)過(guò)濾器娱仔,則意味著該服務(wù)可以給外部的其他應(yīng)用提供服務(wù),因此默認(rèn)值是true游桩。

本文Uri 部分參考 :http://blog.sina.com.cn/s/blog_9f233c070101euqx.html

注:以上內(nèi)容牲迫,如有不對(duì)之處請(qǐng)及時(shí)指出,相互交流學(xué)習(xí)借卧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盹憎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子铐刘,更是在濱河造成了極大的恐慌陪每,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異檩禾,居然都是意外死亡挂签,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門盼产,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)饵婆,“玉大人,你說(shuō)我怎么就攤上這事戏售∏群耍” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵灌灾,是天一觀的道長(zhǎng)搓译。 經(jīng)常有香客問(wèn)我,道長(zhǎng)锋喜,這世上最難降的妖魔是什么些己? 我笑而不...
    開(kāi)封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮跑芳,結(jié)果婚禮上轴总,老公的妹妹穿的比我還像新娘。我一直安慰自己博个,他們只是感情好怀樟,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著盆佣,像睡著了一般往堡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上共耍,一...
    開(kāi)封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天虑灰,我揣著相機(jī)與錄音,去河邊找鬼痹兜。 笑死穆咐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的字旭。 我是一名探鬼主播对湃,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼遗淳!你這毒婦竟也來(lái)了拍柒?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤屈暗,失蹤者是張志新(化名)和其女友劉穎拆讯,沒(méi)想到半個(gè)月后脂男,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡种呐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年宰翅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陕贮。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡堕油,死狀恐怖潘飘,靈堂內(nèi)的尸體忽然破棺而出肮之,到底是詐尸還是另有隱情,我是刑警寧澤卜录,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布戈擒,位于F島的核電站,受9級(jí)特大地震影響艰毒,放射性物質(zhì)發(fā)生泄漏筐高。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一丑瞧、第九天 我趴在偏房一處隱蔽的房頂上張望柑土。 院中可真熱鬧,春花似錦绊汹、人聲如沸稽屏。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)狐榔。三九已至,卻和暖如春获雕,著一層夾襖步出監(jiān)牢的瞬間薄腻,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工届案, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留庵楷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓楣颠,卻偏偏與公主長(zhǎng)得像尽纽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子球碉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容