Android ContentProvider介绍

在Android中数据的存储一共有五种形式,分别是:Shared Preferences、网络存储、文件存储,外储存储、SQLite。但是我们知道一般这些存储都只是在单独的一个应用程序之中达到一个数据的共享。而使用ContentProvider共享数据的好处是统一了数据访问方式。ContentProvide对数据进行封装,不用关心数据存储的细节。下面我们通过代码来介绍这个ContentProvider的使用过程。

public class ItemContentProvier {

    /*Authority*/
    public static final String  AUTHORITY="com.itchq.contentprovider";

    /*Match Code*/
    public static final int ITEM=1;
    public static final int ITEM_POS=2;

    /*Default sort order*/
    public static final String DEFAULT_SORT_ORDER = "_id asc";

       /*MIME*/
    public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.itchq.contentprovider";
    public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.itchq.contentprovider";

    public static final String ARTICE_ITEM="articeitem";

    /*Content URI*/
    public static final Uri CONTENT_ARTICE=Uri.parse("content://"+AUTHORITY+"/"+ARTICE_ITEM);

    public interface Artice{

        public static final String ID="_id";

        public static final String TITLE="title";

        public static final String CONTENT_ID="contentid";
    }

}

上面这个类都是定义数据库一些常量,我们往往把它单独的提前出来使代码结构更清晰一点。

URI 通用资源标志符,这个Uri代表了要操作的数据,后面要访问这个数据库就可以通过这个URI来指定,用于唯一标识某个具体的ContentProvider

URI格式如下:
[content://][com.itchq.contentprovider][/artice][/1]

 |------A------|-----------------B-------------------|---C---|---D--|

A:前缀表明数据受控于一个内容提供者。它从不修改,也就是schema,ContentProvider(内容提供者)的scheme已经由Android所规定, scheme为:content://

B:称为Authority,它唯一地标识了一个特定的Content Provider,因此,这部分内容一般使用Content Provider所在的package来命名,使得它是唯一的,在上面的代码中使用    public static final String  AUTHORITY="com.itchq.contentprovider";定义这个变量,这个还有一个地方使用到,就是在AndroidMainfest.xml中要声明这个数据库的时候需要使用,如下代码:

         <provider
            android:name="com.itchq.contentprovider.itContentProvider"
            android:authorities="com.itchq.contentprovider"/>

上面这个android:authorities这个就是和我们的AUTHORITY里面定义的字符串是一样的,这个地方要注意点,如果不一样外部就不可以访问这个数据库了,我们所说的URI唯一性也是在这个地方来体现出来的,

C:称为资源路径,它表示所请求的资源的类型,这部分内容是可选的

D:资源ID,它表示所请求的是一个特定的资源,它通常是一个数字,对应前面我们所介绍的数据库表中的_id字段的内容,它唯一地标志了某一种资源下的一个特定的实例

我们在定义URI的时候一般情况行都是需要定义这个C,但是D就没有定义,简单点说C就是代表了数据库中某一个表(这个名字是随意定义不一定就是和我们的数据库表名字相同,这里只是一个比喻),而D则是表示操作这个表的哪一个字段,相当于我们的SQL语句中 where id=1这个条件

继续往下看:

       /*MIME*/
    public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.itchq.contentprovider";
    public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.itchq.contentprovider";

这个是MIME类型,每一个ContenProvider中都必须定义这个MIME,其实这个MIME类型一般来说就估计上面两种,被分为两部分前面是数据的大类别,后面定义具体的种类,比如上面的vnd.android.cursor.dir和vnd.android.cursor.item都是固定不会变的,后面的字段都是相同的而且随便定义,一般的数据库中都vnd.+包名。对于vnd.android.cursor.dir这个类型表示的访问多个资源的URI,就是你操作的是一整张表,比如添加新数据了,删除某一个表后者是查询表的全部记录等等,而vnd.android.cursor.item是表示访问单个资源的URI,比如更新表中某一个自定的记录,查询某一条指定的信息,就是相当SQL中的where条件语句。

Artice这个接口是我们这个数据库的表的字段,这里看出这个表有三个字段,分别是:_id,title,contentid。ITEM和ITEM_POS这两个变量分别定义URI匹配规则的匹配码。

比如如果我们访问的URI是content://com.itchq.contentprovider/articeitem,则匹配规则返回的匹配码为ITEM。如果访问的是content://com.itchq.contentprovider/articeitem/# # 是一个通配符表示如何数据,则匹配规则返回的匹配码为ITEM_POS,这两个常量我们后面还会有用到的

下面就是ContentProvider的定义了

package com.itchq.contentprovider;

import java.util.HashMap;

import com.itchq.contentprovider.ItemContentProvier.Artice;

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.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;

public class itContentProvider extends ContentProvider{

    private static final String DB_NAME="itchq.db";

    private static final String DB_TABLE_ARTICE="artice";

    private static final int DB_VERSION=1;

    private DBHelper dbHelper = null;  

    private static final String DB_CREATE_ARTICE = "create table " + DB_TABLE_ARTICE +
            " (" + Artice.ID + " integer primary key autoincrement, " +
            Artice.TITLE + " text not null, " +
            Artice.CONTENT_ID + " integer not null);";

    private static final UriMatcher uriMatcher;
    private static final HashMap<String, String> articleProjectionMap;
    static{
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM, ItemContentProvier.ITEM);
        uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM+"/#", ItemContentProvier.ITEM_POS);

        articleProjectionMap = new HashMap<String, String>();
        articleProjectionMap.put(Artice.ID, Artice.ID);
        articleProjectionMap.put(Artice.TITLE, Artice.TITLE);
        articleProjectionMap.put(Artice.CONTENT_ID,Artice.CONTENT_ID);
    }
    @Override
    public boolean onCreate() {
        // TODO Auto-generated method stub
        dbHelper = new DBHelper(getContext(), DB_NAME, null, DB_VERSION);
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        // TODO Auto-generated method stub
         SQLiteDatabase db = dbHelper.getReadableDatabase();  

         SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder();
         switch(uriMatcher.match(uri)){
         case ItemContentProvier.ITEM:{
             sqlBuilder.setTables(DB_TABLE_ARTICE);
             sqlBuilder.setProjectionMap(articleProjectionMap);
             break;
         }
         case ItemContentProvier.ITEM_POS:{
             String id = uri.getPathSegments().get(1);
             sqlBuilder.setTables(DB_TABLE_ARTICE);
             sqlBuilder.setProjectionMap(articleProjectionMap);
             sqlBuilder.appendWhere(Artice.ID + "=" + id);
             break;
         }
         default:
             throw new IllegalArgumentException("Error Uri: " + uri);
         }
        Cursor cursor = sqlBuilder.query(db, projection, selection, selectionArgs, null, null, TextUtils.isEmpty(sortOrder) ? ItemContentProvier.DEFAULT_SORT_ORDER : sortOrder);
        cursor.setNotificationUri(getContext().getContentResolver(), uri);
        return cursor;
    }

    @Override
    public String getType(Uri uri) {
        // TODO Auto-generated method stub
        switch (uriMatcher.match(uri)) {
        case ItemContentProvier.ITEM:{
            return ItemContentProvier.CONTENT_TYPE;
        }
        case ItemContentProvier.ITEM_POS:{
            return ItemContentProvier.CONTENT_ITEM_TYPE;
        }
        default:
            throw new IllegalArgumentException("Error Uri: " + uri);
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO Auto-generated method stub
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        long id = db.insert(DB_TABLE_ARTICE, Artice.ID, values);
        if (id < 0) {
            throw new SQLiteException("Unable to insert " + values + " for "
                    + uri);
        }
        Uri newUri = ContentUris.withAppendedId(uri, id);
        getContext().getContentResolver().notifyChange(newUri,null);
        return newUri;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // TODO Auto-generated method stub
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int count = 0;
        switch(uriMatcher.match(uri)){
        case ItemContentProvier.ITEM:{
             count = db.delete(DB_TABLE_ARTICE, selection, selectionArgs);
             break;
        }
        case ItemContentProvier.ITEM_POS:{
            String id = uri.getPathSegments().get(1);
            count = db.delete(DB_TABLE_ARTICE,Artice.ID + "=" + id
                     + (!TextUtils.isEmpty(selection) ? " and (" + selection + ‘)‘ : ""), selectionArgs);
             break;
        }
        default:
            throw new IllegalArgumentException("Error Uri: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return count;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        // TODO Auto-generated method stub
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int count = 0;
        switch(uriMatcher.match(uri)){
        case ItemContentProvier.ITEM:{
             count = db.update(DB_TABLE_ARTICE, values, selection, selectionArgs);
             break;
        }
        case ItemContentProvier.ITEM_POS:{
            String id = uri.getPathSegments().get(1);
            count = db.update(DB_TABLE_ARTICE, values, Artice.ID + "=" + id
                     + (!TextUtils.isEmpty(selection) ? " and (" + selection + ‘)‘ : ""), selectionArgs);
             break;
        }
        default:
            throw new IllegalArgumentException("Error Uri: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return count;
    }

    private static class DBHelper extends SQLiteOpenHelper{

        public DBHelper(Context context, String name, CursorFactory factory,
                int version) {
            super(context, name, factory, version);
            // TODO Auto-generated constructor stub
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            // TODO Auto-generated method stub
            db.execSQL(DB_CREATE_ARTICE);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // TODO Auto-generated method stub

        }

    }

}

继承ContentProvider类需要实现默认下面这些方法

public boolean onCreate() 在创建ContentProvider时调用

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 用于查询指定Uri的ContentProvider,返回一个Cursor

public int delete(Uri uri, String selection, String[] selectionArgs) 用于从指定Uri的ContentProvider中删除数据

public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) 用于更新指定Uri的ContentProvider中的数据

public Uri insert(Uri uri, ContentValues values) 用于添加新数据到指定Uri的ContentProvider中

public String getType(Uri uri) 用于返回指定的Uri中的数据的MIME类型

这个就是前面说的,如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头。即

public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.itchq.contentprovider";

如果要操作的数据属于非集合类型数据(简单点说就是指定where条件的),那么MIME类型字符串应该以vnd.android.cursor.item/开头。即

public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.itchq.contentprovider";

UriMatcher类用于匹配Uri 这个就是要设置Uri的匹配规则,我们可以根据不同的Uri来操作不同的数据,比如前面我们定义的那个URI

 /*Content URI*/
    public static final Uri CONTENT_ARTICE=Uri.parse("content://"+AUTHORITY+"/"+ARTICE_ITEM);

分解这个URI字符串是”content://com.itchq.contentprovider/articeitem“,那么我们操作可以是content://com.itchq.contentprovider/articeitem这个字符串也也可能是content://com.itchq.contentprovider/articeitem/#这个字符串,前面表示操作整个数据库表,后面指定是操作数据库某一个字段,那我们是如何知道这个匹配码呢?这个就需要UriMatcher类来设定

public void addURI (String authority, String path, int code) ;

authority就是上面定义的public static final String  AUTHORITY="com.itchq.contentprovider";这个变量,path就是就是指具体那个路径,一般就是URI规则中表示C的部分,比如上面URI规则中 public static final Uri CONTENT_ARTICE=Uri.parse("content://"+AUTHORITY+"/"+ARTICE_ITEM);那么这个path指的就是ARTICE_ITEM这个字符串变量,即public static final String ARTICE_ITEM="articeitem"; code就是后面那个匹配码,比如我们可以看一下update更新语句里面有这么一个switch函数:

       switch(uriMatcher.match(uri)){
        case ItemContentProvier.ITEM:{

        }
        case ItemContentProvier.ITEM_POS:{

        }
        default:
            throw new IllegalArgumentException("Error Uri: " + uri);
        }

上面的ItemContentProvier.ITEM 和 ItemContentProvier.ITEM_POS 就是已经定义好的匹配码,即code参数,我们通过这个ItemContentProvier.ITEM可以知道操作整个数据库表,ItemContentProvier.ITEM_POS操作的是数据库表中某一列,这个匹配一般都是一开始初始化的时候就需要全部给注册上的,

    static{
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM, ItemContentProvier.ITEM);
        uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM+"/#", ItemContentProvier.ITEM_POS);

这个就是注册上的,我们先看这个path参数有ItemContentProvier.ARTICE_ITEM和ItemContentProvier.ARTICE_ITEM/#,这两个就是分别表示操作的数据库表和数据库对应某一列,最后一个参数就是我们上面也说到的匹配码,构造函数的参数为UriMatcher.NO_MATCH,它表示当uriMatcher不能匹配指定的URI时,就返回代码UriMatcher.NO_MATCH常量值为-1

public int match (Uri uri)

与传入的Uri匹配,它会首先与找我们之前通过addURI方法添加进来的Uri匹配,这个URI匹配是这样的,前面部分固定是A部分:content://, B部分是authority,也就是我们addURI里面添加的第一个参数,C部分表示是路径,也就是我们在addRUI添加的第二个参数path,就是说最后匹配的URI就是这样的 content://authority/path。

authority和path都是我们在addURI参数就第一个和第二个的参数。如果匹配成功就返回之前我们设置的code值,否则返回一个UriMatcher.NO_MATCH常量值为-1

        articleProjectionMap = new HashMap<String, String>();
        articleProjectionMap.put(Artice.ID, Artice.ID);
        articleProjectionMap.put(Artice.TITLE, Artice.TITLE);
        articleProjectionMap.put(Artice.CONTENT_ID,Artice.CONTENT_ID); 

在上面的put函数中,第一个参数表示列的别名,第二个参数表示列的真实名称。在这个例子中,我们把列的别名和和真实名称都设置成一样的。

在query函数中,我们使用SQLiteQueryBuilder来辅助数据库查询操作,使用这 个类的好处是我们可以不把数据库表的字段暴露出来,而是提供别名给第三方应用程序使用,这样就可以把数据库表内部设计隐藏起来,方便后续扩展和维护。列别 名到真实列名的映射是由上面这个HashMap成员变量来实现的,当然这里没有修改,也可以不用定义。

    @Override
    public String getType(Uri uri) {
        // TODO Auto-generated method stub
        switch (uriMatcher.match(uri)) {
        case ItemContentProvier.ITEM:{
            return ItemContentProvier.CONTENT_TYPE;
        }
        case ItemContentProvier.ITEM_POS:{
            return ItemContentProvier.CONTENT_ITEM_TYPE;
        }
        default:
            throw new IllegalArgumentException("Error Uri: " + uri);
        }
    }

这个是返回当前Uri所数据的MIME类型,如果该Uri对应的数据可能包括多条记录,那么MIME类型字符串就是以 vnd.android.cursor.dir/开头,我们通过前面的addURI指定 ItemContentProvier.ITEM是返回全面的类型,如果Uri对应的数据只包含一条记录,那么MIME类型字符串就是以 vnd.android.cursor.item/开头,前面的addURI得知ItemContentProvier.ITEM_POS这个返回的是某一列信息。

还有一点是我们在itContentProvider类的内部中定义了一个DBHelper类,它继承于SQLiteOpenHelper类,它用是用辅助我 们操作数据库的。使用这个DBHelper类来辅助操作数据库的好处是只有当我们第一次对数据库时行操作时,系统才会执行打开数据库文件的操作,

剩下的更新查询删除等操作就不多说了,最后不要忘记在AndroidManifest.xml中进行定义

         <provider
            android:name="com.itchq.contentprovider.itContentProvider"
            android:authorities="com.itchq.contentprovider"
            android:multiprocess="false"
            />

在配置Content Provider的时候,最重要的就是要指定它的authorities属性了,只有配置了这个属性,第三方应用程序才能通过它来找到这个Content Provider。这要需要注意的,这里配置的authorities属性的值是和我们前面在ItemContentProvier.java文件中定义的AUTHORITY 常量的值是一致的。另外一个属性multiprocess是一个布尔值,它表示这个Content Provider是否可以在每个客户进程中创建一个实例,这样做的目的是为了减少进程间通信的开销。这里我们为了减少不必要的内存开销,把属性 multiprocess的值设置为false,使得系统只能有一个Content Provider实例存在,它运行在自己的进程中。下面看看在Activity中投入和操作这些数据库的

    private void add(){
        ContentValues values = new ContentValues();
        values.put(Artice.TITLE, "test");
        values.put(Artice.CONTENT_ID, 1);
        Uri uri = getContentResolver().insert(ItemContentProvier.CONTENT_ARTICE, values);
    }

    private void query(){
        String[] projection = new String[] {
                Artice.ID,
                Artice.TITLE,
                Artice.CONTENT_ID
        };
        Cursor cursor = getContentResolver().query(ItemContentProvier.CONTENT_ARTICE, projection, null, null, ItemContentProvier.DEFAULT_SORT_ORDER);
        if (cursor.moveToFirst()) {
            do{
                int id=cursor.getInt(cursor.getColumnIndex(Artice.ID));
                String title=cursor.getString(cursor.getColumnIndex(Artice.TITLE));
                int content_id=cursor.getInt(cursor.getColumnIndex(Artice.CONTENT_ID));

            }while(cursor.moveToNext());
        }
    }
ItemContentProvier.CONTENT_ARTICE就是我们前面定义的那个URI这个介绍一个ContentUris类,这个类是它用于在Uri后面追加一个ID或者解析出传入的Uri所带上的ID值,就是我们说的URI规则中最后D部分|------A------|-----------------B-------------------|---C---|---D--|常用的两个方法如下:public static Uri withAppendedId (Uri contentUri, long id)  用于为路径加上ID部分Uri resultUri = ContentUris.withAppendedId(ItemContentProvier.CONTENT_ARTICE, 10);那么这个Uri就是content://com.itchq.contentprovider/articeitem/10,那么我们在前面使用UriMatch.addURI匹配码就是uriMatcher.addURI(ItemContentProvier.AUTHORITY, ItemContentProvier.ARTICE_ITEM+"/#", ItemContentProvier.ITEM_POS);这个,那么这个时候返回的匹配码就是 ItemContentProvier.ITEM_POS。

public static long parseId (Uri contentUri)从路径中获取ID部分Uri uri = Uri.parse("content://com.itchq.contentprovider/articeitem/10"long personid = ContentUris.parseId(uri);//获取的结果为:10,前面的操作中有用到这个
时间: 2024-08-29 15:37:41

Android ContentProvider介绍的相关文章

Android 四大组件 (四) ContentProvider介绍

ContentProvider虽然与Activity.Service.BroadcastReceiver齐名为Android四大组件.如果你不是特别开发一款与其他APP有数据交互的应用,它的使用频率远没有另外三者高.因为已经介绍了前三大组件,为了完善Android四大组件介绍了解,这次补充下ContentProvider介绍.哈哈~一边在看世界杯,巴西打瑞士.目前巴西1:0.我把所有金币全压巴西了~题外话说完,下面简单介绍下ContentProvider. 一.  ContentProvider

Android入门介绍

Android入门介绍 3G.4G 第三代移动通信技术(3rd - Generation),速率一般在几百Kbps,较之前的2G和2.5G在数据传输速度上有很大提升. 第四代移动通信技术(4th - Generation),速度可达到100Mbps以上,几乎可以满足人们的所有传输数据的需求. 目前主流的3G技术标准有三种: WCDMA:全球80%以上的3G网络都是采用此种制式.中国联通运营.186 CDMA2000:目前日韩及北美使用较多.中国电信运营. 189 TD-SCDMA:中国自主知识产

android AsyncTask介绍

本文转自:http://www.cnblogs.com/devinzhang/archive/2012/02/13/2350070.html 写的很好,向你们推荐 android AsyncTask介绍 AsyncTask和Handler对比 1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主

Android bluetooth介绍(一):基本概念及硬件接口

关键词:蓝牙硬件接口 UART  PCM  blueZ 版本:基于android4.2之前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)欢迎指正错误,共同学习.共同进步!! 一.基本概念补充 1.AP:ApplicationProcessor应用处理器 采用ARM架构的CPU,通常负责运行OS和一些特定设置和载入开机预设.比如一个没有电话功能的平板电脑,只跑android或

Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程

关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本:基于android4.2之前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)欢迎指正错误,共同学习.共同进步!!一.Android Bluetooth Architecture蓝牙代码架构部分(google 官方蓝牙框架) Android的蓝牙系统,自下而上包括以下一些

Android Configuration介绍 (未完成)

博客很空,想赶紧填一篇东西,选的这个题目看了下中文网络中还不是很常见,但是由于我也不了解全部的configuration,需要验证思路,写起来也很慢,先发个未完成的占座. 所谓Configuration指的是Configuration.java这个类所代表的配置信息,它的位置在($ANDROID_ROOT)/frameworks/base/core/java/android/content/res/Configuration.java 本文分三部分: 一. 逐一讲解成员变量,了解功能和每一个数值

Android ContentProvider、ContentResolver和ContentObserver的使用

1.ContentProvider.ContentResolver和ContentObserver ContentProvider是Android的四大组件之一,可见它在Android中的作用非同小可.它主要的作用是:实现各个应用程序之间的(跨应用)数据共享,比如联系人应用中就使用了ContentProvider,你在自己的应用中可以读取和修改联系人的数据,不过需要获得相应的权限.其实它也只是一个中间人,真正的数据源是文件或者SQLite等. 一个应用实现ContentProvider来提供内容

Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析

关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_get_properties.AUDIO.DBUS版本号:基于android4.2之前版本号 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载.请注明作者.请尊重版权谢谢)欢迎指正错误.共同学习.共同进步!! 參考

Android bluetooth介绍(四): a2dp connect流程分析

关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_get_properties.AUDIO.DBUS版本:基于android4.2之前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)欢迎指正错误,共同学习.共同进步!! Andr