Android开发 Day11(内容提供者)

1. ContentPorvider

ContentProvider为App存取内部数据提供统一的外部接口,让不同的应用之间得以分享数据。

Client App 将用户的输入内容,通过ContentProvider跨进程通信传递给Server App。

代码示例

Server端

database类:

public class UserDBHelper extends SQLiteOpenHelper {

    private static final String DB_NAME = "user.db";
    public static final String TABLE_NAME = "user_info";
    private static final int DB_VERSION = 1;
    private static UserDBHelper mHelper = null;

    private UserDBHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    // 利用单例模式获取数据库帮助器的唯一实例
    public static UserDBHelper getInstance(Context context) {
        if (mHelper == null) {
            mHelper = new UserDBHelper(context);
        }
        return mHelper;
    }

    // 创建数据库,执行建表语句
    @Override
    public void onCreate(SQLiteDatabase db) {
        String sql = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
                "_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
                " name VARCHAR NOT NULL," +
                " age INTEGER NOT NULL," +
                " height LONG NOT NULL," +
                " weight FLOAT NOT NULL," +
                " married INTEGER NOT NULL);";
        db.execSQL(sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

Provider类

public class UserInfoProvider extends ContentProvider {
    private static final UriMatcher URI_MATCH=new UriMatcher(UriMatcher.NO_MATCH);
    private static final int USERS=1;
    private static final int USER=2;
    static {
         URI_MATCH.addURI(UserInfoContent.AUTHORITY,"/user",USERS);
         URI_MATCH.addURI(UserInfoContent.AUTHORITY,"/user/#",USER);

    }
    private UserDBHelper dbHelper;
    public UserInfoProvider() {
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int count = 0;
        switch (URI_MATCH.match(uri)){
            //删除单行
            case USERS:
                SQLiteDatabase db1 = dbHelper.getWritableDatabase();
                count = db1.delete(UserDBHelper.TABLE_NAME,selection,selectionArgs);
                db1.close();
                break;
            case USER:
                String id = uri.getLastPathSegment();
                SQLiteDatabase db2 =dbHelper.getWritableDatabase();
                count = db2.delete(UserDBHelper.TABLE_NAME, "_id=?", new String[]{id});
                db2.close();
                break;
        }
        // Implement this to handle requests to delete one or more rows.
        return count;
    }

    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
        if(URI_MATCH.match(uri)==USERS){
            SQLiteDatabase db = dbHelper.getWritableDatabase();
            db.insert(UserDBHelper.TABLE_NAME,null,values);
        }
       return uri;
    }

    @Override
    public boolean onCreate() {
        // TODO: Implement this to initialize your content provider on startup.
        dbHelper=UserDBHelper.getInstance(getContext());
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        if(URI_MATCH.match(uri)==USERS){
            SQLiteDatabase db = dbHelper.getReadableDatabase();
            return db.query(UserDBHelper.TABLE_NAME,projection,selection,selectionArgs,null,null,null);
        }
        return null;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

AndroidManifest.xml文件
 <provider
            android:name=".provider.UserInfoProvider"
            android:authorities="com.example.chapter07_server.provider.UserInfoProvider"
            android:enabled="true"
            android:exported="true"/>

Client端

public class ContentWriteActivity extends AppCompatActivity implements View.OnClickListener {
    private EditText et_name;
    private EditText et_age;
    private EditText et_height;
    private EditText et_weight;
    private CheckBox ck_married;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_content_write);
        et_name = findViewById(R.id.et_name);
        et_age = findViewById(R.id.et_age);
        et_height = findViewById(R.id.et_height);
        et_weight = findViewById(R.id.et_weight);
        ck_married = findViewById(R.id.ck_married);
        findViewById(R.id.btn_save).setOnClickListener(this);
        findViewById(R.id.btn_delete).setOnClickListener(this);
        findViewById(R.id.btn_read).setOnClickListener(this);

    }

    @SuppressLint("Range")
    @Override
    public void onClick(View v) {
        if(v.getId()==R.id.btn_save){
            ContentValues values = new ContentValues();
            values.put(UserInfoContent.USER_NAME, et_name.getText().toString());
            values.put(UserInfoContent.USER_AGE, Integer.parseInt(et_age.getText().toString()));
            values.put(UserInfoContent.USER_HEIGHT, Integer.parseInt(et_height.getText().toString()));
            values.put(UserInfoContent.USER_WEIGHT, Float.parseFloat(et_weight.getText().toString()));
            values.put(UserInfoContent.USER_MARRIED, ck_married.isChecked());
            getContentResolver().insert(UserInfoContent.CONTENT_URI,values);
            ToastUtil.show(this,"保存成功");
        }else if(v.getId()==R.id.btn_read){
            Cursor cursor = getContentResolver().query(UserInfoContent.CONTENT_URI, null, null, null,null,null);
            if(cursor!=null){
                while(cursor.moveToNext()){
                    User info=new User();
                    info.id = cursor.getInt(cursor.getColumnIndex(UserInfoContent._ID));
                    info.name = cursor.getString(cursor.getColumnIndex(UserInfoContent.USER_NAME));
                    info.age = cursor.getInt(cursor.getColumnIndex(UserInfoContent.USER_AGE));
                    info.height = cursor.getInt(cursor.getColumnIndex(UserInfoContent.USER_HEIGHT));
                    info.weight = cursor.getFloat(cursor.getColumnIndex(UserInfoContent.USER_WEIGHT));
                    info.married = cursor.getInt(cursor.getColumnIndex(UserInfoContent.USER_MARRIED)) == 1 ? true : false;
                    Log.d("lzk",info.toString());
                }
                cursor.close();
            }
        }else if(v.getId()==R.id.btn_delete){
            int count=0;

            //删除单行
            //Uri uri= ContentUris.withAppendedId(UserInfoContent.CONTENT_URI,1);
            //count=getContentResolver().delete(uri,null,null);
            //删除多行
            count=getContentResolver().delete(UserInfoContent.CONTENT_URI,"name=?",new String[]{"Jack"});
            if(count>0){
                ToastUtil.show(this,"删除成功");
            }
        }

    }
}
AndroidManifest.xml文件
<queries>
        <package android:name="com.example.chapter07_server" />
</queries>
public class UserInfoContent implements BaseColumns {
    public static final String AUTHORITY = "com.example.chapter07_server.provider.UserInfoProvider";
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");

    public static final String USER_NAME = "name";
    public static final String USER_AGE = "age";
    public static final String USER_HEIGHT = "height";
    public static final String USER_WEIGHT = "weight";
    public static final String USER_MARRIED = "married";
}

2. 运行时动态权限管理

Android系统为了防止某些App滥用权限,从6.0开始引入了运行时权限管理机制,App在运行过程中动态检查是否拥有某些权限,一旦发现缺少某种必需的权限,则系统会自动弹出小窗提示用户去开启该权限。

动态申请权限的步骤

检查App是否开启了指定权限

  • 调用ContextCompact的checkSelfPermission方法。

请求系统弹窗,以便用户选择是否开启权限

  • 调用ActivityCompact的requestPermission方法,即可命令系统自动弹出权限申请窗口。

判断用户的权限选择结果

  • 重写活动页面的权限请求回调方法OnRequestPermissionResult,在该方法内部处理用户的权限选择结果。

 代码示例

Lazy模式

public class PermissionLazyActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String[] PERMISSIONS_CONTACTS = new String[]{
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_CONTACTS
    };
    private static final int REQUEST_CODE_ALL=3;

    private static final String[] PERMISSIONS = new String[]{
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_CONTACTS,
            Manifest.permission.SEND_SMS,
            Manifest.permission.READ_SMS
    };

    private static final String[] PERMISSIONS_SMS = new String[]{
            Manifest.permission.SEND_SMS,
            Manifest.permission.READ_SMS
    };
    private static final int REQUEST_CODE_CONTACTS = 1;
    private static final int REQUEST_CODE_SMS = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_permission_lazy);
        findViewById(R.id.btn_contact).setOnClickListener(this);
        findViewById(R.id.btn_sms).setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.btn_contact) {
            PermissionUtil.checkPermission(this, PERMISSIONS_CONTACTS, REQUEST_CODE_CONTACTS);

        } else if (view.getId() == R.id.btn_sms) {
            PermissionUtil.checkPermission(this, PERMISSIONS_SMS, REQUEST_CODE_SMS);

        }

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_CODE_CONTACTS:
                if(PermissionUtil.checkGrant(grantResults)){
                    Log.d("lzk","通讯录权限获取成功");

                }else{
                    ToastUtil.show(this,"通讯录权限获取失败");
                    jumpToSetting();
                }
                break;
            case REQUEST_CODE_SMS:
                break;
        }
    }

    public void jumpToSetting(){
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.fromParts("package",getPackageName(),null));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}

Hunger模式

public class PermissionHungryActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String[] PERMISSIONS_CONTACTS = new String[]{
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_CONTACTS
    };
    private static final int REQUEST_CODE_ALL=3;
    private static final String[] PERMISSIONS = new String[]{
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_CONTACTS,
            Manifest.permission.SEND_SMS,
            Manifest.permission.READ_SMS
    };
    private static final String[] PERMISSIONS_SMS = new String[]{
            Manifest.permission.SEND_SMS,
            Manifest.permission.READ_SMS
    };
    private static final int REQUEST_CODE_CONTACTS = 1;
    private static final int REQUEST_CODE_SMS = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_permission_lazy);
        findViewById(R.id.btn_contact).setOnClickListener(this);
        findViewById(R.id.btn_sms).setOnClickListener(this);
        PermissionUtil.checkPermission(this, PERMISSIONS, REQUEST_CODE_ALL);

    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.btn_contact) {
           PermissionUtil.checkPermission(this, new String[]{PERMISSIONS[0], PERMISSIONS[1]},REQUEST_CODE_ALL);

        } else if (view.getId() == R.id.btn_sms) {
          PermissionUtil.checkPermission(this,new String[]{PERMISSIONS[2], PERMISSIONS[3]},REQUEST_CODE_ALL);

        }

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_CODE_ALL:
                if(PermissionUtil.checkGrant(grantResults)){
                    Log.d("lzk","通权限获取成功");

                }else{
                    ok:for (int i=0;i<grantResults.length;i++) {
                        if(grantResults[i]!= PackageManager.PERMISSION_GRANTED){
                            switch (permissions[i]){
                                case Manifest.permission.READ_CONTACTS:

                                case Manifest.permission.WRITE_CONTACTS:
                                    ToastUtil.show(this,"通讯录权限获取失败");
                                    jumpToSetting();
                                    break ok;
                                case Manifest.permission.SEND_SMS:
                                case Manifest.permission.READ_SMS:
                                    ToastUtil.show(this,"收发短信权限获取失败");
                                    jumpToSetting();
                                    break;
                            }
                        }

                        
                    }
                    jumpToSetting();
                }
            case REQUEST_CODE_CONTACTS:
                if(PermissionUtil.checkGrant(grantResults)){
                    Log.d("lzk","通讯录权限获取成功");

                }else{
                    ToastUtil.show(this,"通讯录权限获取失败");
                    jumpToSetting();
                }
                break;
            case REQUEST_CODE_SMS:
                if(PermissionUtil.checkGrant(grantResults)){
                    Log.d("lzk","收发短信权限获取成功");

                }else{
                    ToastUtil.show(this,"收发短信权限获取失败");
                    jumpToSetting();
                }
                break;
        }
    }

    public void jumpToSetting(){
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.fromParts("package",getPackageName(),null));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}

PermissionUtil

public class PermissionUtil {
    // 检查多个权限。返回true表示已完全启用权限,返回false表示未完全启用权限
    public static boolean checkPermission(Activity act, String[] permissions, int requestCode) {
        // Android 6.0 之后开始采用动态权限管理
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            int check = PackageManager.PERMISSION_GRANTED;
            for (String permission : permissions) {
                check = ContextCompat.checkSelfPermission(act, permission);
                if (check != PackageManager.PERMISSION_GRANTED) {
                    break;
                }
            }
            // 未开启该权限,则请求系统弹窗,好让用户选择是否立即开启权限
            if (check != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(act, permissions, requestCode);
                return false;
            }
        }
        return true;
    }
    // 检查权限结果数组,返回true表示都已经获得授权。返回false表示至少有一个未获得授权
    public static boolean checkGrant(int[] grantResults) {
        if (grantResults != null) {
            // 遍历权限结果数组中的每条选择结果
            for (int grant : grantResults) {
                // 未获得授权
                if (grant != PackageManager.PERMISSION_GRANTED) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }
}

2. 案例(联系人操作)

public class ContactAddActivity extends AppCompatActivity implements View.OnClickListener {
    private EditText et_contact_name;
    private EditText et_contact_phone;
    private EditText et_contact_email;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact_add);
        et_contact_name = findViewById(R.id.et_contact_name);
        et_contact_phone = findViewById(R.id.et_contact_phone);
        et_contact_email = findViewById(R.id.et_contact_email);
        findViewById(R.id.btn_add_contact).setOnClickListener(this);
        findViewById(R.id.btn_read_contact).setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {
        if(view.getId()==R.id.btn_add_contact){
            Contact contact=new Contact();
            contact.name=et_contact_name.getText().toString().trim();
            contact.phone=et_contact_phone.getText().toString().trim();
            contact.email=et_contact_email.getText().toString().trim();

            //方式一,使用ContentResolver多次写入,每次一个字段
            addContacts(getContentResolver(),contact);
            //方式2,批处理方式
            addFullContacts(getContentResolver(),contact);
        }else if(view.getId()==R.id.btn_read_contact){
            readPhoneContacts(getContentResolver());

        }


    }
    @SuppressLint("Range")
    private void readPhoneContacts(ContentResolver resolver) {

        Cursor cursor = resolver.query(ContactsContract.RawContacts.CONTENT_URI, new String[]{ContactsContract.RawContacts._ID}, null, null, null, null);
        while(cursor.moveToNext())   {
            int rawContactId = cursor.getInt(0);
            Uri uri = Uri.parse("content://com.android.contacts/contacts/" + rawContactId + "/data");
            Cursor dataCursor = resolver.query(uri, new String[]{Contacts.Data.MIMETYPE, Contacts.Data.DATA1, Contacts.Data.DATA2},
                    null, null, null);
            Contact contact=new Contact();
            while(dataCursor.moveToNext()){
                String data1 = dataCursor.getString(dataCursor.getColumnIndex(Contacts.Data.DATA1));
                String mimeType = dataCursor.getString(dataCursor.getColumnIndex(Contacts.Data.MIMETYPE));
                if(mimeType.equals(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)){
                    //姓名
                    contact.name=data1;
                }else if(mimeType.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)){
                    //电话
                    contact.phone=data1;
                }else if(mimeType.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)){
                    contact.email=data1;
                }

            }
            dataCursor.close();
            if(contact.name!=null){
                Log.d("lzk",contact.toString());
            }


            cursor.close();


        }


    }

    private void addFullContacts(ContentResolver resolver, Contact contact) {
        // 创建一个插入联系人主记录的内容操作器
        ContentProviderOperation op_main = ContentProviderOperation
                .newInsert(ContactsContract.RawContacts.CONTENT_URI)
                .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)
                .build();

        // 创建一个插入联系人姓名记录的内容操作器
        ContentProviderOperation op_name = ContentProviderOperation
                .newInsert(ContactsContract.Data.CONTENT_URI)
                // 将第0个操作的id,即 raw_contacts 的 id 作为 data 表中的 raw_contact_id
                .withValueBackReference(Contacts.Data.RAW_CONTACT_ID, 0)
                .withValue(Contacts.Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
                .withValue(Contacts.Data.DATA2, contact.name)
                .build();

        // 创建一个插入联系人电话号码记录的内容操作器
        ContentProviderOperation op_phone = ContentProviderOperation
                .newInsert(ContactsContract.Data.CONTENT_URI)
                // 将第0个操作的id,即 raw_contacts 的 id 作为 data 表中的 raw_contact_id
                .withValueBackReference(Contacts.Data.RAW_CONTACT_ID, 0)
                .withValue(Contacts.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
                .withValue(Contacts.Data.DATA1, contact.phone)
                .withValue(Contacts.Data.DATA2, CommonDataKinds.Phone.TYPE_MOBILE)
                .build();

        // 创建一个插入联系人电子邮箱记录的内容操作器
        ContentProviderOperation op_email = ContentProviderOperation
                .newInsert(ContactsContract.Data.CONTENT_URI)
                // 将第0个操作的id,即 raw_contacts 的 id 作为 data 表中的 raw_contact_id
                .withValueBackReference(Contacts.Data.RAW_CONTACT_ID, 0)
                .withValue(Contacts.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE)
                .withValue(Contacts.Data.DATA1, contact.email)
                .withValue(Contacts.Data.DATA2, CommonDataKinds.Email.TYPE_WORK)
                .build();

        // 声明一个内容操作器的列表,并将上面四个操作器添加到该列表中
        ArrayList<ContentProviderOperation> operations = new ArrayList<>();
        operations.add(op_main);
        operations.add(op_name);
        operations.add(op_phone);
        operations.add(op_email);

        try {
            // 批量提交四个操作
            resolver.applyBatch(ContactsContract.AUTHORITY, operations);
        } catch (OperationApplicationException e) {
            e.printStackTrace();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private void addContacts(ContentResolver resolver, Contact contact) {
        ContentValues values = new ContentValues();
        // 往 raw_contacts 添加联系人记录,并获取添加后的联系人编号
        Uri uri = resolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
        long rawContactId = ContentUris.parseId(uri);

        ContentValues name = new ContentValues();
        // 关联联系人编号
        name.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
        // “姓名”的数据类型
        name.put(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
        // 联系人的姓名
        name.put(ContactsContract.Contacts.Data.DATA2, contact.name);
        // 往提供器添加联系人的姓名记录
        resolver.insert(ContactsContract.Data.CONTENT_URI, name);


        ContentValues phone = new ContentValues();
        // 关联联系人编号
        phone.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
        // “电话号码”的数据类型
        phone.put(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
        // 联系人的电话号码
        phone.put(ContactsContract.Contacts.Data.DATA1, contact.phone);
        // 联系类型。1表示家庭,2表示工作
        phone.put(ContactsContract.Contacts.Data.DATA2, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
        // 往提供器添加联系人的姓名记录
        resolver.insert(ContactsContract.Data.CONTENT_URI, phone);


        ContentValues email = new ContentValues();
        // 关联联系人编号
        email.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
        //  “电子邮箱”的数据类型
        email.put(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
        // 联系人的电子邮箱
        email.put(ContactsContract.Contacts.Data.DATA1, contact.email);
        // 联系类型。1表示家庭,2表示工作
        email.put(ContactsContract.Contacts.Data.DATA2, ContactsContract.CommonDataKinds.Email.TYPE_WORK);
        // 往提供器添加联系人的姓名记录
        resolver.insert(ContactsContract.Data.CONTENT_URI, email);


    }
}

原视频

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值