wb安卓Content Provider

ContentProvider

Android四大组件之一,ContentProvider是Android中提供的专门用于不同应用间数据交互和共享的组件。

ContentProvider实际上是对SQLiteOpenHelper的进一步封装,以一个或多个表的形式将数据呈现给外部应用,通过URI映射来选择需要操作数据库中的哪个表,并对表中的数据进行增删改查处理。ContentProvider其底层使用了Binder来完成APP进程之间的通信,同时使用了匿名共享内存来作为共享数据的载体。ContentProvider支持访问权限管理机制,以控制数据的访问者及访问方式,保证数据访问的安全性。

URI

URI(Uniform Resource Identifier)即统一资源标识符,是一个用于标识某一互联网资源名称的字符串

其结构为content:// com.android.contacts/ contacts/ contactId

  • schema:Android中固定为content://
  • anthority:用于唯一标识一个ContentProvider
  • path:ContentProvider中数据表的表名
  • id:数据表中数据的标识,可选字段

MIME类型

MIME(Multipurpose Internet Mail Extensions)即多用途互联网邮件扩展类型,是指定某种扩展名的文件用什么应用程序来打开的方式类型。当扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。

类型/子类型(Content-Type/subtype)扩展名
application/vnd.android.package-archive.apk
text/plain.txt
image/jpeg.jpeg
text/html./html
audio/x-pn-realaudio.rmvb
audio/mpeg.mp3
audio/mp4.mp4
image/png.png
application/json.json
aoolication/pdf.pdf

UriMatcher类(URI匹配器)

UriMatcher类是一个工具类,帮助匹配ContentProvider中的Uri。只提供了两个方法——addURI和match方法。

	private final static String AUTHORITY = "com.android.peter.provider";
	private final static int STUDENT_URI_CODE = 0;
	private final static UriMatcher sUriMatcher;
	
	static{
		sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		//把Uri和Uri_Code相关联
		sUriMatcher.addURI(AUTHORITY,"student",STUDENT_URI_CODE);
	}
	//通过match方法能够根据传递的uri匹配到对应的Uri_Code
	int uriType = sUriMatcher.match(uri);
	

ContentUris类

ContentUris类代码很短,只包含了withAppendedld、parseld、appendld三个静态方法。以接下来示列中要用到的uri(“content://com.android.peter.provider/student”)为例,依次执行withAppendedld和parseld方法。

	Uri uri = Uri.parse("content://com.android.peter.provider/student");
	Uri withAppendedIdUri = ContentUris.withAppendedId(uri,1);
	Log.d(TAG," withAppendedId ~ uri = " + withAppendedIdUri.toString());
	long parseId = ContentUris.parseId(withAppendedIdUri);
	Log.d(TAG," parseId ~ uri = " + parseId);

输出的log如下:

04-10 17:56:03.668 15652-15652/com.android.peter.contentproviderdemo D/MainActivity:  withAppendedId ~ uri = content://com.android.peter.provider/student/1
04-10 17:56:03.669 15652-15652/com.android.peter.contentproviderdemo D/MainActivity:  parseId ~ uri = 1

从log中可以看出,调用withAppendedid方法会在原始的uri后面添加了一个值为1的id,调用parseld方法可以取出这个id。

appendld方法用于通过Uri.Builder方式生成的Uri使用。

	Uri.Builder ub = new Uri.Builder();
	ub.authority("com.android.peter.provider").appendPath("student");
	Log.d(TAG,"ub = " + ub.toString());
	Uri.Builder appendIdUri = ContentUris.appendId(ub,1);
	Log.d(TAG,"appendIdUri = " + appendIdUri.toString());

输出log如下:

04-10 18:10:48.283 19995-19995/com.android.peter.contentproviderdemo D/MainActivity: ub = //com.android.peter.provider/student
04-10 18:10:48.284 19995-19995/com.android.peter.contentproviderdemo D/MainActivity: appendIdUri = //com.android.peter.provider/student/1

ContentProvider的使用

ContentProvider 的使用可以简单的归纳为以下几步:
  1. 创建自己的数据列表;
  2. 自定义ContentProvider实现相关的抽象方法;
  3. 在AndroidManifest中声明provider以及定义相关访问权限;
  4. 通过ContentResolver根据URI进行增删改查;
创建自己的数据列表

首先,你应该根据项目需要选择合适的数据类型设计你的数据库列表,并转化成对应的可执行的SQL语句。然后,派生抽象类SQLiteOpenHelper创建其子类DBOpenHelper并实现构造方法以及重载onCreate和onUpgrade方法。最后,在onCreate方法中执行你设计好的SQL语句。

public class DBOpenHelper extends SQLiteOpenHelper{
    private final static String TAG = "DBOpenHelper";
    
    private final static String DATABASE_NAME = "com_android_peter_provider.db)";
    private final static String DATABASE_STUDENT_TABLE_NAME = "student";
    private final static int DATABASE_VERSION = 1;
    
    private Content mContext;
    
     /**
     * student table
     * @id primary key
     * @name student's name. e.g:peter.
     * @gender student's gender. e.g: 0 male; 1 female.
     * @number student's number. e.g: 201804081702.
     * @score student's score. more than 0 and less than 100. e.g:90.
     * */
    
    private final static String CREATE_STUDENT_TABLE = "CREATE TABLE IF NOT EXISTS " + DATABASE_SUTDENT_TABLE_NAME + "(id INTEGER PRIMARY KEY," + "name TEXT VARCHAR(20) NOT NULL," + "gender BIT DEFAULT(1)," + "number TEXT VARCHAR(12) NOT NULL," + "score INTEGER CHECK(score >= 0 and score <= 100))";
    
    public DBOpenHelper(Content context){
        super(context,DATABASE_NAME,null,DATABASE_VERSION);
        mContext = context;
    }
    
    public DBOpenHelper(Content context,String name,SQLIteDatabase.CursorFactory factory,int version){
        super(context,DATABASE_NAME,factory,DATABASE_VERSION);
        mContext = context;
    }
    
    public DBOpenHelper(Content context,String name,SQLIteDatabase.CursorFactory factory,int version,DatabaseErrorHandler errorHandler){
        super(context,DATABASE_NAME,factory,DATABASE_VERSION,errorHandler);
        mContext = context;
    }
    
    @Override
    public void onCreate(SQLiteDatabase db){
        Log.d(TAG,"onCreate");
        db.execSQL(CREATE_STUDENT_TABLE);
    }
    
    @Override
    public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion){
        Log.d(TAG,"onUpgrade o = " + oldVersion + " , n= " + newVersion);
    }
}

创建的数据库位于"/data/data/包名/databases/"目录中。

自定义ContentProvider类实现相关的抽象方法

创建一个自定义的ContentProvider需要实现以下几个方法:

方法功能
onCreate()初始化
query(@NonNull Uri,@Nullable String[] projection,@Nullable String selection,@Nullable String[] selectionArgs,@Nullable String sortOrder)查询数据
insert(@NonNull Uri uri,@Nullable ContentValues values)插入数据
update(@NonNull Uri uri,@Nullable ContentValues values ,@Nullable String selection,@Nullable String[] selectionArgs)更新数据
delete(@NonNull Uri uri,@Nullable String selection,@Nullable String[] selectionArgs)删除数据
getType(@NonNull Uri uri)获得数据的MIME类型

具体实现如下:

public class StudentContentProvider extends ContentProvider{
    private final static String TAG = "StudentProvider";
    
    private final static String AUTHORITY = "com.android.peter.provider";
    private final static int STUDENT_URI_CODE = 0;
    
    private Context mContext;
    private SQLiteDatabase mDataBase;
    private final static UriMatcher sUriMatcher;
    
    static{
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(AUTHORITY,"student",STUDENT_URI_CODE);
    }
    
    @Override
    public boolean onCreate(){
        mContext = getContext();
        mDataBase = new DBOpenHelper(mContext).getWritableDatabase();
        
        return true;
    }
    
    @Nullable
    @Override
        public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
            int uriType = sUriMatcher.match(uri);
            Cursor cursor;
            switch(uriType){
                case STUDENT_URI_CODE:
                    cursor = mDataBase.query(DBOpenHelper.DATABASE_STUDENT_TABLE_MAME,projection,selection,selectionArgs,null,null,sortOrder,null);
                    break;
                default:
                    throw new IllegalArgumentException("UnSupport Uri : " + uri);
            }
            return cursor;
        }
    
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }
    
     @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values){
        int uriType = sUriMatcher.match(uri);
        long row;
        
        switch(uriType){
            case STUDENT_URI_CODE:
                row = mDataBase.insert(DBOpenHelper.DATABASE_STUDENT_TABLE_NAME,null, values);
                break;
            default:
                throw new IllegalArgumentException("UnSupport Uri : " + uri);
        }
        
        if(row > -1){
            mContext.getContentResolver().notifyChange(uri,null);
            return ContentUris.withAppendedid(uri,row);
        }
        
        return null;
    }
    
     @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        int uriType = sUriMatcher.match(uri);
        int rowDelete;
        
        switch (uriType) {
            case STUDENT_URI_CODE:
                rowDelete = mDataBase.delete(DBOpenHelper.DATABASE_STUDENT_TABLE_NAME,selection,selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("UnSupport Uri : " + uri);
        }
        
        if(rowDelete > 0){
             mContext.getContentResolver().notifyChange(uri,null);
        }
        
        return rowDelete;
    }

      @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        int uriType = sUriMatcher.match(uri);
        int rowUpdate;
        switch (uriType) {
            case STUDENT_URI_CODE:
                rowUpdate = mDataBase.update(DBOpenHelper.DATABASE_STUDENT_TABLE_NAME,values,selection,selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("UnSupport Uri : " + uri);
        }

        if(rowUpdate > 0) {
            mContext.getContentResolver().notifyChange(uri,null);
        }

        return rowUpdate;
    }
}

注意:

  1. onCreate()由系统回调并运行在主线程里,其他五个方法由外界回调并允行在Binder线程池中。所以,不要在onCreate方法中做耗时操作。
  2. 增删改查操作存在多线程并发访问问题,因此方法内部要做好线程同步。
  3. SQliteDatabase的insert方法的返回值是插入数据所在的行号,update和delete方法的返回值代表此次操作影响到的行数。
在AndroidManifest中声明provider以及定义相关访问权限

在注册ContentProvider的时候通过android:process属性设置provider运行在单独的进程里,模拟进程间通信。

 <!-- student provider 访问权限声明 -->
    <permission
        android:name="com.android.peter.provider.READ_PERMISSION"
        android:label="Student provider read permission"
        android:protectionLevel="normal"
        />
    <permission
        android:name="com.android.peter.provider.WRITE_PERMISSION"
        android:label="Student provider read permission"
        android:protectionLevel="normal"
        />

    <!-- 声明ContentProvider -->
    <application
         ...
         <provider
            android:name=".StudentContentProvider"
            android:authorities="com.android.peter.provider"
            android:readPermission="com.android.peter.provider.READ_PERMISSION"
            android:writePermission="com.android.peter.provider.WRITE_PERMISSION"
            android:process=":provider"
            android:exported="true"/>
        ...
    </application>

为了方便起见,权限声明时protectionLevel设置的是最低风险权限(normal),关于其他等级权限和说明如下:

权限等级说明
normal低风险权限,只要申请了就可以使用,安装时不需要用户确认
dangerous高风险权限,安装时需要用户确认授权才可使用
signature只有当申请权限应用于声明此权限应用的数字签名相同时才能将权限授给它
signatureOrSystem签名相同或者申请权限的应用为系统应用才能将权限授给它
通过ContentResolver根据URI进行增删改查

在Activity中分别定义insertValue、queryValue、updateValue、deleteValue对自定义的provider进行测试。其中,database中id=1的数据为预存数据,为了方便查看数据的变化;Student类为student数据的封装类。

public class ClientActivity extends AppCompatActivity {
     private final static String TAG = "ClientActivity";
    
    /**
     * student table
     * @id primary key
     * @name student's name. e.g:peter.
     * @gender student's gender. e.g: 0 male; 1 female.
     * @number student's number. e.g: 201804081702.
     * @score student's score. more than 0 and less than 100. e.g:90.
     * */
         
     private final static String AUTHORITY = "com.android.peter.provider";
    private final static Uri STUDENT_URI = Uri.parse("content://" + AUTHORITY + "/student");

    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);

        mContext = this;
    }
    
     @Override
    protected void onResume() {
        super.onResume();
    	Log.d(TAG,"------------insert---------");
        insertValue();
        queryValue();
        Log.d(TAG,"------------update---------");
        updateValue();
        queryValue();
         Log.d(TAG,"------------delete---------");
        deleteValue();
        queryValue();
    }
    
     @Override
    protected void onPause() {
        super.onPause();
    }

     @Override
    protected void onDestroy() {
        super.onDestroy();
    }
    
     private void insertValue() {
         ContentValues contentValues = new ContentValues();
         contentValues.put("id",0);
         contentValues.put("name","peter");
         contentValues.put("gender",0);
         contentValues.put("number","201818240252");
         contentValues.put("score","100");
         
         mContext.getContentResolver().insert(STUDENT_URI,contentValues);
     }
    
     private void queryValue() {
     	Cursor cursor = getContentResolver().query(STUDENT_URI, new String[]{"id", "name","gender","number","score"},null,null,null);
         while(cursor.moveToNext()){
             Student student = new Student();
             student.id = cursor.getInt(cursor.getColumnIndex("id"));
             student.name = cursor.getString(cursor.getColumnIndex("name"));
            student.gender = cursor.getInt(cursor.getColumnIndex("gender"));
            student.number = cursor.getString(cursor.getColumnIndex("number"));
            student.score = cursor.getInt(cursor.getColumnIndex("score"));
             Log.d(TAG,"student = " + student.toString());
         }
     }
    
     private void updateValue() {
         ContentValues contentValues = new ContentValues();
         contentValues.put("id",0);
         contentValues.put("name","update");
         contentValues.put("gender",1);
         contentValues.put("number","201818240252");
         contentValues.put("score","90");
         
         getContenResolver().update(STUDENT_URI,contentValues,"id = ?",new String[] {"0"});
     }
    
      private void deleteValue() {
        getContentResolver().delete(STUDENT_URI,"name = ?",new String[]{"update"});
    }
    
    //student数据封装类
public class Student {
    private final static String TAG = "Student";

    public Integer id;
    public String name;
    public Integer gender;
    public String number;
    public Integer score;

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender=" + gender +
                ", number='" + number + '\'' +
                ", score=" + score +
                '}';
    }
}

为了方便查看数据,insert、update、delete操作之后都会调用query重新查询打印log。增加一条id=0的数据,然后更新这条数据,最后删除这条数据,log如下:

04-11 10:51:13.405 31349-31349/com.android.peter.contentproviderclient D/ClientActivity: ------------insert---------
04-11 10:51:13.778 31349-31349/com.android.peter.contentproviderclient D/ClientActivity: student = Student{id=0, name='peter', gender=0, number='201818240252', score=100}
04-11 10:51:13.779 31349-31349/com.android.peter.contentproviderclient D/ClientActivity: student = Student{id=1, name='lemon', gender=1, number='201818240251', score=100}

04-11 10:51:13.779 31349-31349/com.android.peter.contentproviderclient D/ClientActivity: ------------update---------
04-11 10:51:13.796 31349-31349/com.android.peter.contentproviderclient D/ClientActivity: student = Student{id=0, name='update', gender=1, number='201818240250', score=90}
04-11 10:51:13.796 31349-31349/com.android.peter.contentproviderclient D/ClientActivity: student = Student{id=1, name='lemon', gender=1, number='201818240251', score=100}

04-11 10:51:13.796 31349-31349/com.android.peter.contentproviderclient D/ClientActivity: ------------delete---------
04-11 10:51:13.810 31349-31349/com.android.peter.contentproviderclient D/ClientActivity: student = Student{id=1, name='lemon', gender=1, number='201818240251', score=100}

如果是访问其它应用定义的provider只需要在AndroidManifest中声明相应权限即可使用,使用方法跟上面Activity中的示例是一样的。

<uses-permission android:name="com.android.peter.provider.READ_PERMISSION"/>
<uses-permission android:name="com.android.peter.provider.WRITE_PERMISSION"/>
ContentObserver

Android中提供的用来监听ContentProvider变化的抽象类,可以通过ContentResolver的registerContentObserver和unregisterContentObserver方法来注册和注销ContentObserver监听器。当被监听的ContentProvider发生变化时,就会回调对应的ContentObserver的onChange方法。具体用法如下:

private final static int CONTENT_PROVIDER_CHANGED = 20180412;
private Handler mHandler;
private ContentObserver mContentObserver;
//更新UI线程
private class ObserverHandler extends Handler{
    @Override
    public void handlerMessage(Message msg){
        super.handlerMessage(msg);
        Log.d(TAG,"handleMessage msg = " + msg);
    }
}
    
@Override    
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_client);
    
    	mContext = this;
    	//实例化ContentObserver
    	mHandler = new ObserverHandler(mHandler){
            @Override
            public void onChange(boolean selfChange,Uri uri){
                Log.d(TAG,"onChange selfChange = " + selfChange + " ,uri = " + uri.toString());
                mHandler.obtainMessage(CONTENT_PROVIDER_CHANGED).sendToTarget();
            }
        };
    
    	//注册监听
    mContext.getContentResolver().registerConntentObserver(STUDENT_URI,ture,mContentObserver);
	}
    
@Override
protected void onDestory(){
        super.onDestroy();
        //注销监听
        if(mContentObserver != null){
            mContext.getContentResolver().unregisterContentObserver(mContentObserver);
        }
    }

在使用完记得一定要注销ContentObserver以免引起内存泄漏。

内容大部分来自网络
自己收集的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 平台上进行 AMR-WB 编码,可以使用 Android 系统提供的 MediaCodec API 实现。以下是一个简单的 AMR-WB 编码示例: ```java public class AMRWBEncoder { private MediaCodec mediaCodec; private ByteBuffer[] inputBuffers; private ByteBuffer[] outputBuffers; private MediaCodec.BufferInfo bufferInfo; private int sampleRate; private int bitRate; private int frameSize; private int channelCount; public AMRWBEncoder(int sampleRate, int bitRate) throws IOException { this.sampleRate = sampleRate; this.bitRate = bitRate; this.frameSize = sampleRate / 100; this.channelCount = 1; // 单声道 this.mediaCodec = MediaCodec.createEncoderByType("audio/amr-wb"); MediaFormat mediaFormat = MediaFormat.createAudioFormat("audio/amr-wb", sampleRate, channelCount); mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate); mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, frameSize * channelCount * 2); mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); mediaCodec.start(); inputBuffers = mediaCodec.getInputBuffers(); outputBuffers = mediaCodec.getOutputBuffers(); bufferInfo = new MediaCodec.BufferInfo(); } public byte[] encode(byte[] input) { int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1); if (inputBufferIndex >= 0) { ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; inputBuffer.clear(); inputBuffer.put(input); mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, 0, 0); } int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0); if (outputBufferIndex >= 0) { ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; byte[] output = new byte[bufferInfo.size]; outputBuffer.get(output); mediaCodec.releaseOutputBuffer(outputBufferIndex, false); return output; } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { outputBuffers = mediaCodec.getOutputBuffers(); } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { // TODO: 输出 format 变化,这里省略 } return null; } public void release() { mediaCodec.stop(); mediaCodec.release(); } } ``` 使用时,首先创建一个 AMRWBEncoder 对象,然后调用其 encode() 方法对音频数据进行编码,最后调用 release() 方法释放资源。例如: ```java AMRWBEncoder encoder = new AMRWBEncoder(16000, 16000); byte[] input = // 音频数据,PCM 格式 byte[] output = encoder.encode(input); encoder.release(); ``` 需要注意的是,这里的输入音频数据必须是 PCM 格式,如果是其他格式需要先进行格式转换。另外,AMR-WB 编码是有损的,输出的音频质量与压缩比有关,需要根据实际需求选择合适的比特率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值