Android四大組件(三) ContentProvider

ContentProvider 內容提供者

Android四大組件(三)  ContentProvider。

1. 為什麼需要內容提供者

ContentProvider(內容提供者)可以把私有的數據庫內容暴露出來.在一個程序裡寫好瞭
ContentProvider則在其他的應用程序也可以根據標準來進行第一個應用的數據庫的訪問.

2. 實現ContentProvider步驟(一般開發中用不到,因為都想隱藏數據)

定義內容提供者,定義一個類繼承自ContentProvider

在清單文件中配置一下,類似於

    
    
    

定義一個UriMatcher

private static final UriMatcher sURIMatcher = new UriMatcher(
    UriMatcher.NO_MATCH);

寫一個靜態代碼塊,添加匹配規則

static { // 靜態代碼塊 將期望匹配的內容傳遞進去
    uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR);
    uriMatcher.addURI("com.example.app.provider", "table1/#", TABLE1_ITEM);
    uriMatcher.addURI("com.example.app.provider", "table2", TABLE2_DIR);
    uriMatcher.addURI("com.example.app.provider", "table2/#", TABLE2_ITEM);
}

按照我們添加的匹配規則,暴露想要暴露的方法 實現query(),insert()方法等

隻要是通過內容提供者暴露出來的數據,其他應用訪問的方式都是一樣的,就是通過內容解析者

ContentResolver contentResolver = mContext.getContentResolver();
        Uri uri = Uri.parse("content://com.itheima.provider/query");
        Cursor cursor = contentResolver.query(uri, new String[] { "name",
                "money" }, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            do {

                String name = cursor.getString(cursor.getColumnIndex("name"));
                String money = cursor.getString(cursor.getColumnIndex("money"));

                Log.d("xfhy", "程序二:  name:"+name+"  money:"+money);

            } while (cursor.moveToNext());
        } else {
            Toast.makeText(mContext, "未查詢到數據", Toast.LENGTH_SHORT).show();
        }

3. 讀取聯系人案例

QQ ,微信,陌陌等

data表 data1列表裡存的是所有聯系人的所有信息(包含姓名,地址,郵箱等)
raw_contact_id 列是用來區分一共有幾條聯系人信息
mimetype_id 列是用來區分數據類型(姓名,地址,或者郵箱) raw_contacts 表中contact_id就是data表的 raw_contact_id

查詢聯系人步驟:

先查詢raw_contacts表的contact_id列,就知道有幾條聯系人 我根據contact_id去查詢data表,查詢data1列和mimetype列 view_data是由data表和mimetype表的組合

Android 第一行代碼 書中示例

    ArrayAdapter adapter;
    List contactList = new ArrayList<>();

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

        ListView lv_contact = (ListView) findViewById(R.id.lv_contact);

        adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, contactList);
        lv_contact.setAdapter(adapter);

        //檢查用戶是否已經授權瞭讀取聯系人的權限     如果相等則授權瞭
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
                != PackageManager.PERMISSION_GRANTED) {
            //不相等   則申請權限
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_CONTACTS}, 1);
        } else {
            readContacts();
        }
    }

    /**
     * 讀取聯系人
     */
    private void readContacts() {
        Cursor cursor = null;

        try{
            //查詢聯系人數據   得到Cursor對象
            cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                    null,null,null,null);
            if(cursor != null){
                while(cursor.moveToNext()){
                    //獲取聯系人姓名
                    String name = cursor.getString(cursor.getColumnIndex(
                            ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                    //獲取聯系人號碼
                    String phone = cursor.getString(cursor.getColumnIndex(
                            ContactsContract.CommonDataKinds.Phone.NUMBER));
                    contactList.add(name+"\n"+phone);
                }
                adapter.notifyDataSetChanged();  //刷新一下ListView
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            //最後一定要記得關閉cursor
            if(cursor != null){
                cursor.close();
            }
        }

    }

    //每申請一此危險權限,這個方法就會被調一次
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        //根據申請碼 進行判斷
        switch (requestCode) {
            case 1:
                if(grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    readContacts();
                } else {
                    Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
                }

                break;
            default:
                break;
        }
    }

自己寫的完整的讀取聯系人,並且排序

    public class ContactListActivity extends BaseActivity {

    private ListView lvContact;
    private final static String TAG = "ContactListActivity";
    /**
     * 封裝聯系人數據
     */
    private List<hashmap> contactList = new ArrayList<>();
    /**
     * 聯系人數據準備就緒
     */
    private final static int DATA_IS_READY = 10001;
    /**
     * 封裝聯系人數據的集合HashMap中的key
     */
    private final static String CONTACT_NAME_KEY = "contact_name";
    private final static String CONTACT_PHONE_KEY = "contact_phone";
    /**
     * 適配器
     */
    private MyContactAdapter mContactAdapter;


    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case DATA_IS_READY:
                    //初始化適配器
                    mContactAdapter = new MyContactAdapter();
                    //設置ListView的adapter
                    lvContact.setAdapter(mContactAdapter);
                    break;
                default:
                    break;
            }
        }
    };

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

        initUI();
        requestReadContactPermission();
    }

    /**
     * 申請聯系人權限
     */
    private void requestReadContactPermission() {
        //先申請讀取聯系人權限
        //檢查用戶是否已經給我們授權瞭權限,相等則已經授權,不等則沒授權
        if (ContextCompat.checkSelfPermission(this, Manifest.permission
                .READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            //參數:Context上下文,權限數組,申請碼(申請碼隻要唯一就行)
            ActivityCompat.requestPermissions(this, new String[]{Manifest
                    .permission.READ_CONTACTS}, ConstantValue.MY_PERMISSIONS_READ_CONTACTS);
        } else {
            //如果已經有權限
            initData();
        }
    }

    /**
     * 初始化數據
     */
    private void initData() {
        //1, 讀取聯系人    因為可能聯系人比較多,可能會讀取很久,不想阻塞主線程,所以放到子線程中讀取聯系人
        new Thread(new Runnable() {
            @Override
            public void run() {
                //2, 獲取ContentResolver對象   通過該對象可以查詢系統聯系人數據庫
                ContentResolver contentResolver = getContentResolver();

                String sortOrder = "sort_key COLLATE LOCALIZED ASC";
                /*
                 * Cursor query (Uri uri,
                 String[] projection,
                 String selection,
                 String[] selectionArgs,
                 String sortOrder)
                 */
                //3, 查詢聯系人數據
                Cursor cursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone
                                .CONTENT_URI
                        , null, null, null, sortOrder);

                //4, 如果有數據   就循環查詢裡面的Cursor數據
                if (cursor != null && cursor.moveToFirst()) {
                    contactList.clear();
                    LogUtil.d(TAG, "有聯系人數據");
                    do {
                        //聯系人姓名
                        String contactName = cursor.getString(cursor.getColumnIndex
                                (ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                        //獲取聯系人號碼
                        String contactPhone = cursor.getString(cursor.getColumnIndex(
                                ContactsContract.CommonDataKinds.Phone.NUMBER));
                        LogUtil.d(TAG, contactName + "----> " + contactPhone);

                        //5, 判斷聯系人數據是否為空  如果姓名或者電話有任何一項是空的,那麼就不保存
                        if (!TextUtils.isEmpty(contactName) && !TextUtils.isEmpty(contactPhone)) {
                            HashMap contact = new HashMap<>();
                            contactPhone = contactPhone.replace("-", "");
                            contact.put(CONTACT_NAME_KEY, contactName);
                            contact.put(CONTACT_PHONE_KEY, contactPhone);
                            contactList.add(contact);
                        }

                    } while (cursor.moveToNext());

                    //6, 用完記得關閉
                    cursor.close();

                    //7, 聯系人數據準備完畢,發送給主線程,更新UI
                    Message msg = Message.obtain();
                    msg.what = DATA_IS_READY;
                    mHandler.sendMessage(msg);

                }
            }
        }).start();
    }

    /**
     * 初始化UI
     */
    private void initUI() {
        lvContact = (ListView) findViewById(R.id.lv_contact);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case ConstantValue.MY_PERMISSIONS_READ_CONTACTS:
                if (grantResults.length > 0 && grantResults[0] == PackageManager
                        .PERMISSION_GRANTED) {
                    //申請權限成功
                    LogUtil.d(TAG, "申請聯系人權限成功");
                    initData();
                } else {
                    //申請權限失敗
                    ToastUtil.showWarning("親~未授權讀取聯系人權限就無法讀取聯系人哦");
                    backToSetup3Activity("");
                }
                break;
            default:
                break;
        }
    }

    /**
     * 返回Setup3Activity
     */
    private void backToSetup3Activity(String number) {
        Intent intent = new Intent();
        intent.putExtra(ConstantValue.EXTRA_FOR_CONTACT_NUMBER, number);
        setResult(REQUEST_CODE, intent);
        finish();   //關閉當前Activity
    }

    /**
     * ListView的適配器
     */
    class MyContactAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            return contactList != null ? contactList.size() : 0;
        }

        @Override
        public Object getItem(int position) {
            return contactList.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            //1,  首先獲得當前項的數據
            HashMap contactModel = (HashMap) getItem(position);
            View view;
            ViewHolder viewHolder;

            if (convertView == null) {
                //2, 如果先前沒有加載,則就沒有緩存View,則需要加載一下
                view = View.inflate(MyApplication.getContext(), R.layout.item_contact_layout,
                        null);
                viewHolder = new ViewHolder();

                //3, 獲得一個選項佈局中的控件id
                viewHolder.tvContactName = (TextView) view.findViewById(R.id.tv_contact_name);
                viewHolder.tvContactPhone = (TextView) view.findViewById(R.id.tv_contact_phone);

                //4, 將這個內部類(緩存的數據類)保存到view中(進行緩存)
                view.setTag(viewHolder);
            } else {
                //5, 第二次加載,則隻需要加載之前的緩存數據
                view = convertView;
                viewHolder = (ViewHolder) view.getTag();
            }

            //6, 設置佈局文件中控件的數據
            viewHolder.tvContactName.setText(contactModel.get(CONTACT_NAME_KEY));
            viewHolder.tvContactPhone.setText(contactModel.get(CONTACT_PHONE_KEY));

            //7, 將這個view返回回去作為該子項的佈局
            return view;
        }

        /**
         * 用來緩存 item條目上的所有的控件對象
         */
        class ViewHolder {
            TextView tvContactName;
            TextView tvContactPhone;
        }

    }

}
</hashmap

4. 內容觀察者

內容觀察者不是四大組件,它不需要在清單文件中配置

// 1.註冊內容觀察者
Uri uri = Uri.parse("content://sms/");
getContentResolver().registerContentObserver(uri, true,
        new MyContentObserver(new Handler()));

定義內容觀察者

// 2.定義一個內容觀察者
class MyContentObserver extends ContentObserver {

    public MyContentObserver(Handler handler) {
        super(handler);
    }

    // 被監聽的數據庫,當數據庫發送變化的時候調用
    @Override
    public void onChange(boolean selfChange) {
        Log.d("xfhy", "短信數據庫內容發送變化");
        super.onChange(selfChange);
    }

}

應用場景:短信監聽器

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *