先给出方案以及遇到的问题,最后给出分析过程。
修改方案
import android.content.Context;
import android.support.v4.content.FileProvider;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.util.Date;
import java.text.SimpleDateFormat;
private File getVCardFilePath(Context context, String name) {
final String fileExtension = ".vcf";
final String export_file_prefix = "vcards_";
if (name == null) {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
final String currentDateString = dateFormat.format(new Date()).toString();
final String localFilename = export_file_prefix + currentDateString + fileExtension;
return new File(context.getCacheDir(), localFilename);
} else {
return new File(context.getCacheDir(), name + fileExtension);;
}
}
// the uri must be a vcard uri created by Contacts
public Uri getVCardFilePathByUri(Context context, Uri uri) {
if (uri == null) {
return uri;
}
File file = getVCardFilePath(context);
try {
InputStream in = null;
OutputStream out = null;
try {
in = context.getContentResolver().openInputStream(uri);
out = new FileOutputStream(file);
byte[] buf = new byte[8096];
int size = 0;
while ((size = in.read(buf)) != -1) {
out.write(buf, 0, size);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
} catch (Exception e) {
Log.e(TAG, "exception getVCardFilePathByUri ", e);
}
return FileProvider.getUriForFile(context, getString(
R.string.contacts_file_provider_authority), file);
}
AndroidManifest.xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="@string/contacts_file_provider_authority"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
res/xml/file_paths.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Offer access to files under Context.getCacheDir() -->
<cache-path name="my_cache" />
</paths>
res/values/donottranslate_config.xml
<string name="contacts_file_provider_authority">com.android.contacts.files</string>
思路就是本地创建vcf临时文件,然后传递新的uri。分享的代码
private void shareContact() {
final String lookupKey = mContactData.getLookupKey();
final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
Uri contentUri = getVCardFilePathByUri(shareUri, mContactData.getDisplayName());
final Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType(Contacts.CONTENT_VCARD_TYPE);
intent.putExtra(Intent.EXTRA_STREAM, contentUri);
// Launch chooser to share contact via
final CharSequence chooseTitle = getResources().getQuantityString(
R.plurals.title_share_via, /* quantity */ 1);
final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);
try {
mHasIntentLaunched = true;
ImplicitIntentsUtil.startActivityOutsideApp(this, chooseIntent);
} catch (final ActivityNotFoundException ex) {
Toast.makeText(this, R.string.share_error, Toast.LENGTH_SHORT).show();
}
}
遇到的问题
FileOutputStream与openFileOutput()的两种写文件方式的区别
1. openFileOutput()
在使用文件对数据进行存储时,Activity提供了openFileOutput()方法可以用于把数据输出到文件中。
public void getVCardFileNameByUri(Uri uri) {
if (uri == null) {
return new String();
}
//final String filename = createVCardFileName();
File file = getVCardFilePath(name);
try {
InputStream in = null;
OutputStream out = null;
try {
in = mContext.getContentResolver().openInputStream(uri);
//out = mContext.openFileOutput(filename, Context.MODE_PRIVATE);
out = new FileOutputStream(file);
byte[] buf = new byte[8096];
int size = 0;
while ((size = in.read(buf)) != -1) {
out.write(buf, 0, size);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
} catch (Exception e) {
Log.e(TAG, "exception getVCardFileNameByUri ", e);
}
}
openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符"/" ,如果文件不存在,Android 会自动创建它。创建的文件保存在/data/data/<package name>/files目录,如: /data/data/com.android.contacts/files/test.vcf ,
openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为: Context.MODE_PRIVATE = 0
Context.MODE_APPEND = 32768
Context.MODE_WORLD_READABLE = 1
Context.MODE_WORLD_WRITEABLE = 2
Context.MODE_PRIVATE:为默认操作模式
但是最新sdk(android N之后),除了MODE_PRIVATE模式,其他模式已禁止使用使用会抛出SecurityException异常。
Activity还提供了getCacheDir()和getFilesDir()方法:
getCacheDir()方法用于获取/data/data/<package name>/cache目录
getFilesDir()方法用于获取/data/data/<package name>/files目录
2.FileOutputStream
File saveFile = new File(getCacheDir(), "a.txt");
FileOutputStream out = new FileOutputStream(saveFile);
out.write("test".getBytes());
out.close();
该方案的副作用,你以为cache会自动清理吗?笑话!!
cache里的文件还是需要手动清理。这里有个方案清理cache,后续可以考虑加上
private final long A_DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
private final String EXPORT_FILE_PREFIX = "vcards_";
/**
* Delete the files (that are untouched for more than 1 day) in the cache directory.
* We cannot rely on VCardService to delete export files because it will delete export files
* right after finishing writing so no files could be shared. Therefore, our approach to
* deleting export files is:
* 1. put export files in cache directory so that Android may delete them;
* 2. manually delete the files that are older than 1 day when service is connected.
*/
private void clearExportFiles() {
for (File file : getCacheDir().listFiles()) {
final long ageInMillis = System.currentTimeMillis() - file.lastModified();
if (file.getName().startsWith(EXPORT_FILE_PREFIX) && ageInMillis > A_DAY_IN_MILLIS) {
file.delete();
}
}
}
分析过程
log定位到
Fail:
Gmail : Gmail:Error adding attachment
04-27 15:05:03.404750 914 12032 I ActivityManager: START u0 {act=android.intent.action.SEND typ=text/x-vcard flg=0xb080001 cmp=com.google.android.gm/.ComposeActivityGmailExternal clip={text/x-vcard U:content://com.android.contacts/contacts/as_vcard/347ic77d8d10d1af44f} (has extras)} from uid 10019
PASS:
ActivityManager: START u0 {act=android.intent.action.SEND typ=text/x-vcard flg=0xb080001 cmp=com.google.android.gm/.ComposeActivityGmailExternal clip={text/x-vcard U:content://com.google.android.contacts.files/my_cache/Admin1.vcf} (has extras)} from uid 10093
ActivityManager: START u0 {act=android.intent.action.SEND typ=text/x-vcard flg=0xb080001 cmp=com.google.android.gm/.ComposeActivityGmailExternal clip={text/x-vcard U:content://com.android.contacts.files/my_cache/Yy.vcf} (has extras)} from uid 10013
ActivityManager: START u0 {act=android.intent.action.SEND typ=text/x-vcard flg=0xb080001 cmp=com.google.android.gm/.ComposeActivityGmailExternal clip={text/x-vcard U:content://com.google.android.contacts.files/my_cache/vcards_20180428_122715.vcf} (has extras)} from uid 10093
这样就很明显了,Gmail更新后,应该没有去本地保存vcf文件,需要Intent发送时,传入的uri对应就需要是分享联系人的路径。
百度面试失败总结:上来就问你用过哪些开源框架,MDZZ,用了别人的框架就说明你经验多了?自己解决问题就不行吗!唉,算了还是去看开源框架吧。。。