Android 使用VCard數據類型 異步進行聯系人備份與恢復操作


生活中常有人因為更換手機而丟失聯系人信息,又需要重新一個個去找親戚朋友獲取。
這樣的情況下,聯系人備份與恢復功能就顯得非常實用。所以我學習了相關內容,並進行了適當整合,在這里整理出來。

本篇博客兩個重點

  1. 使用VCard庫進行聯系人備份與恢復
  2. 異步進行備份與恢復操作

為什么要用VCard?

VCard是用於聯系人數據存儲的標准數據格式,且有一套已經成熟的庫可以使用,通用於手機,也通用於郵件等,所以使用VCard進行聯系人的數據存儲與備份格式是非常好的選擇。當然是用XML格式去存儲也是可以的。

下載android-vcard.jar請猛戳這里

為什么要異步進行操作?

聯系人備份與恢復都涉及到了數據庫讀寫,而數據庫的操作一向都是比較費時的,更何況是大量數據的情況下,所以將這些操作進行異步處理是一個非常有必要的事情,否則容易造成UI主線程卡頓。異步的另一個好處是同時能夠在UI界面上給用戶一些過程進度上的反饋,這個在重視產品交互體驗的今天是非常重要的。

一個異步操作UI效果庫,源自GitHub請猛戳這里

VCard本身庫比較復雜,不再多說,有興趣的可以研究源碼。這里貼上一個已經做過一層封裝的聯系人處理類,直接集成調用即可。

封裝好的聯系人處理類model

model的屬性包括基本的聯系人姓名、電話、郵箱。其他屬性如果有需求可以自己添加。

package com.pku.codingma.backupandrecovercontactdemo;

import android.app.Activity;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.ContactsContract;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import a_vcard.android.provider.Contacts;
import a_vcard.android.syncml.pim.VDataBuilder;
import a_vcard.android.syncml.pim.VNode;
import a_vcard.android.syncml.pim.vcard.ContactStruct;
import a_vcard.android.syncml.pim.vcard.VCardComposer;
import a_vcard.android.syncml.pim.vcard.VCardException;
import a_vcard.android.syncml.pim.vcard.VCardParser;

/**
* Created by ma on 2016/4/1.
*/

public class ContactInfo {

/**
* MUST exist
*/

private String name; // 姓名

/**
* 聯系人電話信息
*/

public static class PhoneInfo {
/**
* 聯系電話類型
*/

public int type;
/**
* 聯系電話
*/

public String number;
}

/**
* 聯系人郵箱信息
*/

public static class EmailInfo {
/**
* 郵箱類型
*/

public int type;
/**
* 郵箱
*/

public String email;
}

private List<PhoneInfo> phoneList = new ArrayList<PhoneInfo>(); // 聯系號碼
private List<EmailInfo> email = new ArrayList<EmailInfo>(); // Email

/**
* 構造聯系人信息
*
* @param name 聯系人姓名
*/

public ContactInfo(String name) {
this.name = name;
}

/**
* 姓名
*/

public String getName() {
return name;
}

/**
* 姓名
*/

public ContactInfo setName(String name) {
this.name = name;
return this;
}

/**
* 聯系電話信息
*/

public List<PhoneInfo> getPhoneList() {
return phoneList;
}

/**
* 聯系電話信息
*/

public ContactInfo setPhoneList(List<PhoneInfo> phoneList) {
this.phoneList = phoneList;
return this;
}

/**
* 郵箱信息
*/

public List<EmailInfo> getEmail() {
return email;
}

/**
* 郵箱信息
*/

public ContactInfo setEmail(List<EmailInfo> email) {
this.email = email;
return this;
}

@Override
public String toString() {
return "{name: " + name + ", number: " + phoneList + ", email: " + email + "}";
}
}

封裝在ContactInfo內部的操作handler類

handler內部二次封裝了通過VCard庫對聯系人操作的函數

/**
* 聯系人
* 備份/還原操作
*
*/

public static class ContactHandler {

private static ContactHandler instance_ = new ContactHandler();

/**
* 獲取實例
*/

public static ContactHandler getInstance() {
return instance_;
}
}

通過數據庫獲取手機中的聯系人

第一步通過ContentResolver對所有聯系人進行查詢,獲得Cursor
第二步將Cursor中的數據依次取出,構造成ContactInfo數組,為第三步做准備

        /**
* 獲取聯系人指定信息
*
* @param projection 指定要獲取的列數組, 獲取全部列則設置為null
* @return
* @throws Exception
*/

public Cursor queryContact(Activity context, String[] projection) {
// 獲取聯系人的所需信息
Cursor cur = context.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, projection, null, null, null);
return cur;
}

/**
* 獲取聯系人信息
*
* @param context
* @return
*/

public List<ContactInfo> getContactInfo(Activity context) {
List<ContactInfo> infoList = new ArrayList<ContactInfo>();

Cursor cur = queryContact(context, null);

if (cur.moveToFirst()) {
do {

// 獲取聯系人id號
String id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
// 獲取聯系人姓名
String displayName = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
ContactInfo info = new ContactInfo(displayName);// 初始化聯系人信息

// 查看聯系人有多少電話號碼, 如果沒有返回0
int phoneCount = cur.getInt(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));

if (phoneCount > 0) {

Cursor phonesCursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + id, null, null);

if (phonesCursor.moveToFirst()) {
List<PhoneInfo> phoneNumberList = new ArrayList<PhoneInfo>();
do {
// 遍歷所有電話號碼
String phoneNumber = phonesCursor.getString(phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
// 對應的聯系人類型
int type = phonesCursor.getInt(phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE));

// 初始化聯系人電話信息
ContactInfo.PhoneInfo phoneInfo = new ContactInfo.PhoneInfo();
phoneInfo.type = type;
phoneInfo.number = phoneNumber;

phoneNumberList.add(phoneInfo);
} while (phonesCursor.moveToNext());
// 設置聯系人電話信息
info.setPhoneList(phoneNumberList);
}
}

// 獲得聯系人的EMAIL
Cursor emailCur = context.getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID + "=" + id, null, null);

if (emailCur.moveToFirst()) {
List<EmailInfo> emailList = new ArrayList<EmailInfo>();
do {
// 遍歷所有的email
String email = emailCur.getString(emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA1));
int type = emailCur.getInt(emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE));

// 初始化聯系人郵箱信息
ContactInfo.EmailInfo emailInfo = new ContactInfo.EmailInfo();
emailInfo.type = type; // 設置郵箱類型
emailInfo.email = email; // 設置郵箱地址

emailList.add(emailInfo);
} while (emailCur.moveToNext());

info.setEmail(emailList);
}

//Cursor postalCursor = getContentResolver().query(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI, null, ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID + "=" + id, null, null);
infoList.add(info);
} while (cur.moveToNext());
}
cur.close();
return infoList;
}

將獲取到的聯系人數據通過VCard數據格式備份

第三步在獲取到ContactInfo數組后,將其寫入VCard文件中

/**
* 備份聯系人
*/

public void backupContacts(Activity context, List<ContactInfo> infos) {

try {
String path = Environment.getExternalStorageDirectory() + "/contacts.vcf";

OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(path), "UTF-8");

VCardComposer composer = new VCardComposer();

for (ContactInfo info : infos) {
ContactStruct contact = new ContactStruct();
contact.name = info.getName();
// 獲取聯系人電話信息, 添加至 ContactStruct
List<PhoneInfo> numberList = info
.getPhoneList();
for (ContactInfo.PhoneInfo phoneInfo : numberList) {
contact.addPhone(phoneInfo.type, phoneInfo.number,
null, true);
}
// 獲取聯系人Email信息, 添加至 ContactStruct
List<EmailInfo> emailList = info.getEmail();
for (ContactInfo.EmailInfo emailInfo : emailList) {
contact.addContactmethod(Contacts.KIND_EMAIL,
emailInfo.type, emailInfo.email, null, true);
}
String vcardString = composer.createVCard(contact,
VCardComposer.VERSION_VCARD30_INT);
writer.write(vcardString);
writer.write("\n");

writer.flush();
}
writer.close();

} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (VCardException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

獲取VCard文件中的聯系人信息

反之如果要從VCard中恢復聯系人數據的話,也先是將其轉化為ContactInfo數組

/**
* 獲取vCard文件中的聯系人信息
*
* @return List<ContactInfo>
*/

public List<ContactInfo> restoreContacts() throws Exception {
List<ContactInfo> contactInfoList = new ArrayList<ContactInfo>();

VCardParser parse = new VCardParser();
VDataBuilder builder = new VDataBuilder();
String file = Environment.getExternalStorageDirectory() + "/contacts2.vcf";

BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));

String vcardString = "";
String line;
while ((line = reader.readLine()) != null) {
vcardString += line + "\n";
}
reader.close();

boolean parsed = parse.parse(vcardString, "UTF-8", builder);

if (!parsed) {
throw new VCardException("Could not parse vCard file: " + file);
}

List<VNode> pimContacts = builder.vNodeList;

for (VNode contact : pimContacts) {

ContactStruct contactStruct = ContactStruct.constructContactFromVNode(contact, 1);
// 獲取備份文件中的聯系人電話信息
List<ContactStruct.PhoneData> phoneDataList = contactStruct.phoneList;
List<PhoneInfo> phoneInfoList = new ArrayList<PhoneInfo>();
for (ContactStruct.PhoneData phoneData : phoneDataList) {
ContactInfo.PhoneInfo phoneInfo = new ContactInfo.PhoneInfo();
phoneInfo.number = phoneData.data;
phoneInfo.type = phoneData.type;
phoneInfoList.add(phoneInfo);
}

// 獲取備份文件中的聯系人郵箱信息
List<ContactStruct.ContactMethod> emailList = contactStruct.contactmethodList;
List<EmailInfo> emailInfoList = new ArrayList<EmailInfo>();
// 存在 Email 信息
if (null != emailList) {
for (ContactStruct.ContactMethod contactMethod : emailList) {
if (Contacts.KIND_EMAIL == contactMethod.kind) {
ContactInfo.EmailInfo emailInfo = new ContactInfo.EmailInfo();
emailInfo.email = contactMethod.data;
emailInfo.type = contactMethod.type;
emailInfoList.add(emailInfo);
}
}
}
ContactInfo info = new ContactInfo(contactStruct.name).setPhoneList(phoneInfoList).setEmail(emailInfoList);
contactInfoList.add(info);
}

return contactInfoList;
}

將從VCard中獲取的聯系人數據插入到手機中

獲取到ContactInfo數組后,再通過ContentResolver類將ContactInfo數組依次插入到系統聯系人數據庫中

/**
* 向手機中錄入聯系人信息
*
* @param info 要錄入的聯系人信息
*/

public void addContacts(Activity context, ContactInfo info) {
ContentValues values = new ContentValues();
//首先向RawContacts.CONTENT_URI執行一個空值插入,目的是獲取系統返回的rawContactId
Uri rawContactUri = context.getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);

//往data表入姓名數據
values.clear();
values.put(ContactsContract.RawContacts.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, info.getName());
context.getContentResolver().insert(
ContactsContract.Data.CONTENT_URI, values);

// 獲取聯系人電話信息
List<PhoneInfo> phoneList = info.getPhoneList();
/** 錄入聯系電話 */
for (ContactInfo.PhoneInfo phoneInfo : phoneList) {
values.clear();
values.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
// 設置錄入聯系人電話信息
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneInfo.number);
values.put(ContactsContract.CommonDataKinds.Phone.TYPE, phoneInfo.type);
// 往data表入電話數據
context.getContentResolver().insert(
ContactsContract.Data.CONTENT_URI, values);
}

// 獲取聯系人郵箱信息
List<EmailInfo> emailList = info.getEmail();

/** 錄入聯系人郵箱信息 */
for (ContactInfo.EmailInfo email : emailList) {
values.clear();
values.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
// 設置錄入的郵箱信息
values.put(ContactsContract.CommonDataKinds.Email.DATA, email.email);
values.put(ContactsContract.CommonDataKinds.Email.TYPE, email.type);
// 往data表入Email數據
context.getContentResolver().insert(
ContactsContract.Data.CONTENT_URI, values);
}

}

}

異步處理與功能使用DEMO

使用了基本的線程去實現異步,通過Handler傳遞結果消息,並更新按鈕狀態。

package com.pku.codingma.backupandrecovercontactdemo;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.dd.CircularProgressButton;

import java.util.List;


/**
* A placeholder fragment containing a simple view.
*/

public class BackupAndRecoverContactActivityFragment extends Fragment implements View.OnClickListener{
CircularProgressButton mBackupContactButton;
CircularProgressButton mRecoverContactButton;

//標記消息的來源
public final int BACKUP_WHAT = 0;
public final int RECOVER_WHAT = 1;

//標記成功還是失敗
public final int SUCCESS_FLAG = 1;
public final int FAIL_FLAG = 0;

//用於進行備份和還原操作的ContactHandler內部類
ContactInfo.ContactHandler handler = ContactInfo.ContactHandler.getInstance();

public BackupAndRecoverContactActivityFragment() {

}

Handler mBackupAndRecoverProcessHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == BACKUP_WHAT){
//add your action
if (msg.arg1 == SUCCESS_FLAG){
mBackupContactButton.setProgress(100);
}else {
mBackupContactButton.setProgress(-1);
}
}else if (msg.what == RECOVER_WHAT){
//add your action
if (msg.arg1 == SUCCESS_FLAG){
mRecoverContactButton.setProgress(100);
}else {
mRecoverContactButton.setProgress(-1);
}
}
ShowTipTool.showTip(getActivity(), msg.obj.toString());
}
};

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_backup_and_recover_contact, container, false);
mBackupContactButton = (CircularProgressButton) view.findViewById(R.id.backup_contact_button);
mRecoverContactButton = (CircularProgressButton) view.findViewById(R.id.recover_contact_button);
initEvent();
return view;
}

private void initEvent() {
mRecoverContactButton.setOnClickListener(this);
mBackupContactButton.setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.backup_contact_button:
backup_contact();
break;
case R.id.recover_contact_button:
recover_contact();
break;
default:
break;
}
}

public void backup_contact(){
//讓按鈕進入工作狀態
mBackupContactButton.setIndeterminateProgressMode(true);
mBackupContactButton.setProgress(50);
new Thread(new Runnable() {
@Override
public void run() {
//新建一條Handler處理的消息
Message message = new Message();
try{
// 進行備份聯系人信息動作
handler.backupContacts(getActivity(), handler.getContactInfo(getActivity()));
//如果順利,則將消息的參數設置為成功
message.obj = "backup success";
message.arg1 = SUCCESS_FLAG;
}catch (Exception e){
//如果出現異常,則將消息的參數設置為失敗
message.obj = "backup fail";
message.arg1 = FAIL_FLAG;
e.printStackTrace();
}finally {
//最后設置消息來源並發送
message.what = BACKUP_WHAT;
mBackupAndRecoverProcessHandler.sendMessage(message);
}
}
}).start();
}

//與backup基本相同,不再注釋
public void recover_contact(){
mRecoverContactButton.setIndeterminateProgressMode(true);
mRecoverContactButton.setProgress(50);
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
try {
// 獲取要恢復的聯系人信息
List<ContactInfo> infoList = handler.restoreContacts();
for (ContactInfo contactInfo : infoList) {
// 恢復聯系人
handler.addContacts(getActivity(), contactInfo);
}
message.obj = "recover success";
message.arg1 = SUCCESS_FLAG;
} catch (Exception e) {
message.obj = "recover fail";
message.arg1 = FAIL_FLAG;
e.printStackTrace();
}finally {
message.what = RECOVER_WHAT;
mBackupAndRecoverProcessHandler.sendMessage(message);
}
}
}).start();
}
}

添加讀寫權限

需要使用到讀寫聯系人權限和讀寫外部存儲權限,否則會報錯

<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

效果圖若干張

依次為初始狀態,處理狀態,完成狀態,異常出錯狀態
聯系人數據的具體變化就不再貼了
這里寫圖片描述

獲取Demo完整代碼方法

  1. 訪問我的GitHub進行下載,如果覺得有幫助,請點個贊,謝謝請猛戳這里
  2. 通過CSDN下載站進行積分下載請猛戳這里

文章原始來源與轉載請標明的出處http://blog.csdn.net/u012145166/article/details/51052880

參考http://www.cnblogs.com/lw900320/archive/2013/01/10/2855145.html


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com