本文主要是介绍Android 使用VCard数据类型 异步进行联系人备份与恢复操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
生活中常有人因为更换手机而丢失联系人信息,又需要重新一个个去找亲戚朋友获取。
这样的情况下,联系人备份与恢复功能就显得非常实用。所以我学习了相关内容,并进行了适当整合,在这里整理出来。
本篇博客两个重点
- 使用VCard库进行联系人备份与恢复
- 异步进行备份与恢复操作
为什么要用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;}@Overridepublic 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);// 初始化联系人信息// 查看联系人有多少电话号码, 如果没有返回0int 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);}}// 获得联系人的EMAILCursor 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 {// 遍历所有的emailString 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();// 获取联系人电话信息, 添加至 ContactStructList<PhoneInfo> numberList = info.getPhoneList();for (ContactInfo.PhoneInfo phoneInfo : numberList) {contact.addPhone(phoneInfo.type, phoneInfo.number,null, true);}// 获取联系人Email信息, 添加至 ContactStructList<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执行一个空值插入,目的是获取系统返回的rawContactIdUri 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(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (msg.what == BACKUP_WHAT){//add your actionif (msg.arg1 == SUCCESS_FLAG){mBackupContactButton.setProgress(100);}else {mBackupContactButton.setProgress(-1);}}else if (msg.what == RECOVER_WHAT){//add your actionif (msg.arg1 == SUCCESS_FLAG){mRecoverContactButton.setProgress(100);}else {mRecoverContactButton.setProgress(-1);}}ShowTipTool.showTip(getActivity(), msg.obj.toString());}};@Overridepublic 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);}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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完整代码方法
- 访问我的GitHub进行下载,如果觉得有帮助,请点个赞,谢谢请猛戳这里
- 通过CSDN下载站进行积分下载请猛戳这里
文章原始来源与转载请标明的出处http://blog.csdn.net/u012145166/article/details/51052880
参考http://www.cnblogs.com/lw900320/archive/2013/01/10/2855145.html
这篇关于Android 使用VCard数据类型 异步进行联系人备份与恢复操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!