Android 使用VCard数据类型 异步进行联系人备份与恢复操作

本文主要是介绍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;}@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完整代码方法

  1. 访问我的GitHub进行下载,如果觉得有帮助,请点个赞,谢谢请猛戳这里
  2. 通过CSDN下载站进行积分下载请猛戳这里

文章原始来源与转载请标明的出处http://blog.csdn.net/u012145166/article/details/51052880

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

这篇关于Android 使用VCard数据类型 异步进行联系人备份与恢复操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/275071

相关文章

Python使用Pandas对比两列数据取最大值的五种方法

《Python使用Pandas对比两列数据取最大值的五种方法》本文主要介绍使用Pandas对比两列数据取最大值的五种方法,包括使用max方法、apply方法结合lambda函数、函数、clip方法、w... 目录引言一、使用max方法二、使用apply方法结合lambda函数三、使用np.maximum函数

Qt 中集成mqtt协议的使用方法

《Qt中集成mqtt协议的使用方法》文章介绍了如何在工程中引入qmqtt库,并通过声明一个单例类来暴露订阅到的主题数据,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一,引入qmqtt 库二,使用一,引入qmqtt 库我是将整个头文件/源文件都添加到了工程中进行编译,这样 跨平台

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Python调用Orator ORM进行数据库操作

《Python调用OratorORM进行数据库操作》OratorORM是一个功能丰富且灵活的PythonORM库,旨在简化数据库操作,它支持多种数据库并提供了简洁且直观的API,下面我们就... 目录Orator ORM 主要特点安装使用示例总结Orator ORM 是一个功能丰富且灵活的 python O

Nginx设置连接超时并进行测试的方法步骤

《Nginx设置连接超时并进行测试的方法步骤》在高并发场景下,如果客户端与服务器的连接长时间未响应,会占用大量的系统资源,影响其他正常请求的处理效率,为了解决这个问题,可以通过设置Nginx的连接... 目录设置连接超时目的操作步骤测试连接超时测试方法:总结:设置连接超时目的设置客户端与服务器之间的连接

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程