Android 开发 Content Provider 使用 demo

 在我们Android开发中难免会用到Content Provider,主要是为了实现进程间访问数据,数据库是Android开发中最基本的数据保存方式,但由于数据库的私有性,我们无法对外提供或获取信息,当两个应用需要实现数据共享时,此时就需要本篇文章的主题——ContentProvider

一.提供数据端-contentprovider

1.首先新建一个自己的content provider类继承自ContentProvider,重写onCreate insert query update delete getType方法,在里面做一些自己的对数据操作方法供外部调用,另外需要用UriMatcher 类对访问数据路径Uri进行初始话,指明authorities,Uri路径类似content://authorities/path。

public class CourseContentProvider extends ContentProvider {

    private final String TAG = this.getClass().getName();
    private Context mContext;
    private DBHelper mDbHelper = null;
    private SQLiteDatabase db = null;


    public static final String AUTOHORITY = "com.lmy.provider";
    public static final String COURSE_PATH = "course";
    private static final int PROVIDE_COURSE = 1;
    private static final UriMatcher mMatcher;

    static {
        mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        mMatcher.addURI(AUTOHORITY, COURSE_PATH, PROVIDE_COURSE);
        // 若URI资源路径 = content://com.lmy.provider/course ,则返回注册码 PROVIDE_COURSE
    }


    /**
     * 初始化ContentProvider
     */
    @Override
    public boolean onCreate() {
        Log.d(TAG,"onCerate,current thread:"+Thread.currentThread().getName());
        return true;
    }

    /**
     * 添加数据
     */

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.d(TAG,"insert,current thread:"+Thread.currentThread().getName());
        return uri;
    }

    /**
     * 查询数据
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        Log.d(TAG,"query,current thread:"+Thread.currentThread().getName());
        return null;
    }

    /**
     * 更新数据
     */
    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        Log.d(TAG,"update,current thread:"+Thread.currentThread().getName());
        return 0;
    }

    /**
     * 删除数据
     */
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        Log.d(TAG,"delete,current thread:"+Thread.currentThread().getName());
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        Log.d(TAG,"getType,current thread:"+Thread.currentThread().getName());
        return null;
    }

    /**
     * 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
     */
    private String getTableName(Uri uri) {
        String tableName = null;
        switch (mMatcher.match(uri)) {
            case 1:
                tableName = DBHelper.USER_TABLE_NAME;
                break;
            case 2:
                tableName = DBHelper.JOB_TABLE_NAME;
                break;
        }
        return tableName;
    }
}

2.就是在AndroidManifest里面声明content provider,代码如下

      <provider
            android:name="com.example.contentproviderdemo.CourseContentProvider"
            android:authorities="com.lmy.provider"
            android:exported="true"
            android:readPermission="com.example.contentproviderdemo.READ"
            android:writePermission="com.example.contentproviderdemo.WRITE" />
参数简介

 

这里需要注意的就是自定义权限,如果这里没有添加readPermission writePermission,那么第三方应用在调用时也不需要申请此权限就可以访问数据库进行读写;如果这里添加了这两个权限,那么首先我们要明确这两个是属于自定义权限,需要在此处(也是就content provider 端)将这两个权限添加到系统中,这样第三方应用在申请这两个权限时系统才知道是要申请哪个权限,然后就是第三方应用需要像平时那样申请权限的方式将这两个自定义权限申请一下,这里需要注意一个坑,这种自定义权限系统只会在安装您的应用时,才会隐式允许此请求,也就是说当你第一次编译安装apk时没有去申请这两个权限,那么后来你添加了之后再编译是不生效的,只能删了apk重新再编译安装apk才可以生效(困了我一下午的坑)。

3.在系统中注册自定义权限

   <permission
        android:name="com.example.contentproviderdemo.READ"
        android:label="provider pomission"
        android:protectionLevel="normal" />
    <permission
        android:name="com.example.contentproviderdemo.WRITE"
        android:label="provider pomission"
        android:protectionLevel="normal" />

二.访问数据端-contentresolver

1.首先在AndroidManifest中申请provider的自定义权限,代码如下:

   <uses-permission android:name="com.example.contentproviderdemo.READ"/>
    <uses-permission android:name="com.example.contentproviderdemo.WRITE"/>

2.在使用的地方通过  ContentResolver  类访问 provider 提供的数据库数据,此处的Uri路径要保证跟前面provider中的一致,贴代码:

public class MainActivity extends AppCompatActivity {

    private ContentResolver mContentResolver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContentResolver = this.getContentResolver();
    }

    public void onClickTest(View view) {
        Uri uri = Uri.parse("content://com.lmy.provider/course");
        mContentResolver.query(uri, null, null, null, null);
//        mContentResolver.delete(uri,null,null);
//        mContentResolver.insert(uri, null);
    }
}

最后测试可以走到query中,对应输出日志如下,可以看到onCreate 和 query不在一个进程中,一个在main线程,一个在Binder线程:

2021-03-04 19:05:30.769 31059-31059/com.example.contentproviderdemo D/com.example.contentproviderdemo.CourseContentProvider: onCerate,current thread:main
2021-03-04 19:06:07.391 31059-31077/com.example.contentproviderdemo D/com.example.contentproviderdemo.CourseContentProvider: query,current thread:Binder:31059_1

三.总结

    到此文章已经讲解完毕,这里只是简单的进行了不同进程间访问数据的一个调用demo,仅仅是已输出日志形式来看的,具体操作数据库那块没有添加,感兴趣的小伙伴可以用 SQLiteDatabase 进行操作,大家有什么问题欢迎在下面留言评论,我们下期见!

 

 

 

钉钉的CalDAV服务器是一个标准的CalDAV服务器,因此你可以使用任何支持CalDAV协议的第三方库来访问它。下面是一个使用Android系统自带的SyncAdapter框架实现的CalDAV同步Demo。 1. 添加依赖 在build.gradle文件中添加以下依赖: ``` dependencies { implementation "com.github.aflx:sardine-android:5.7.0" } ``` 这里使用了Sardine-Android库,它是一个支持WebDAV和CalDAV协议的Android库,可以方便地与钉钉的CalDAV服务器进行交互。 2. 创建SyncAdapter 创建一个继承自AbstractThreadedSyncAdapter的SyncAdapter类,并实现其中的onPerformSync()方法,用于执行CalDAV同步任务。 ```java public class CalDAVSyncAdapter extends AbstractThreadedSyncAdapter { private static final String TAG = "CalDAVSyncAdapter"; private final Sardine mSardine; public CalDAVSyncAdapter(Context context, boolean autoInitialize) { super(context, autoInitialize); mSardine = new OkHttpSardine(); } @Override public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { try { // TODO: 执行CalDAV同步任务 } catch (Exception e) { Log.e(TAG, "CalDAV sync failed", e); syncResult.stats.numIoExceptions++; } } } ``` 3. 注册SyncAdapter 在AndroidManifest.xml文件中注册SyncAdapter,并指定对应的账户类型和CalDAV服务器地址。 ```xml <application> <provider android:name="android.content.ContentProvider" android:authorities="com.android.calendar" android:exported="false" android:syncable="true" /> <service android:name=".CalDAVSyncAdapterService" android:exported="true"> <intent-filter> <action android:name="android.content.SyncAdapter" /> </intent-filter> <meta-data android:name="android.content.SyncAdapter" android:resource="@xml/caldav_sync_adapter" /> <meta-data android:name="android.provider.CONTACTS_STRUCTURE" android:resource="@xml/contacts" /> </service> </application> <uses-permission android:name="android.permission.READ_CALENDAR" /> <uses-permission android:name="android.permission.WRITE_CALENDAR" /> <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" /> <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" android:accountType="com.android.exchange" android:icon="@drawable/icon_exchange" android:smallIcon="@drawable/icon_exchange" android:label="@string/app_name" /> <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" android:contentAuthority="com.android.calendar" android:accountType="com.android.exchange" android:userVisible="false" android:supportsUploading="true" android:allowParallelSyncs="false" android:isAlwaysSyncable="true" /> ``` 在res/xml目录下创建caldav_sync_adapter.xml文件,指定SyncAdapter的参数。 ```xml <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" android:contentAuthority="com.android.calendar" android:accountType="com.android.exchange" android:userVisible="false" android:supportsUploading="true" android:allowParallelSyncs="false" android:isAlwaysSyncable="true" /> ``` 4. 执行CalDAV同步任务 在SyncAdapter的onPerformSync()方法中,使用Sardine库实现CalDAV同步任务。以下是一个简单的例子,可以获取钉钉CalDAV服务器上的所有日历事件。 ```java @Override public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { try { // 创建Sardine对象 mSardine.setCredentials("username", "password"); // 获取所有日历事件 List<DavResource> resources = mSardine.getResources("https://calendar.dingtalk.com/caldav/username/events/"); // 解析日历事件 for (DavResource resource : resources) { CalendarBuilder builder = new CalendarBuilder(); Calendar calendar = builder.build(resource.getInputStream()); Log.d(TAG, "Event: " + calendar.toString()); } } catch (Exception e) { Log.e(TAG, "CalDAV sync failed", e); syncResult.stats.numIoExceptions++; } } ``` 注意:在使用Sardine库访问CalDAV服务器时,需要使用完整的CalDAV资源地址,例如"https://calendar.dingtalk.com/caldav/username/events/",其中username为钉钉账号的用户名。另外,钉钉的CalDAV服务器使用的是HTTPS协议,需要添加相应的证书验证。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值