在 Android 7 中,ContentResolver 的方法 openAssetFileDescriptor(vCardUri, "r") 返回 AssetFileDescr

Posted

技术标签:

【中文标题】在 Android 7 中,ContentResolver 的方法 openAssetFileDescriptor(vCardUri, "r") 返回 AssetFileDescriptor 声明长度为 -1【英文标题】:In Android 7, ContentResolver's method-openAssetFileDescriptor(vCardUri, "r") returns AssetFileDescriptor having declaredLength as -1 【发布时间】:2017-06-20 10:22:02 【问题描述】:

android 7 中,getContentResolver().openAssetFileDescriptor(vCardUri, "r") 返回 AssetFileDescriptor,声明长度为 -1,由 getDeclaredLength() 返回。

尝试将联系人作为 vcard 导出到 vcf 文件中。我试过的代码如下

Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
AssetFileDescriptor fd = resolver.openAssetFileDescriptor(uri, "r");
FileInputStream fis = fd.createInputStream();
byte[] b = new byte[(int)fd.getDeclaredLength()];
fis.read(b);

以上代码在 Android 6 或更低版本中完美运行。但是当使用 Android 7 运行时,创建 byte[] 的行导致 NegativeByteArraySizeException,因为声明的Length 为 -1。 当我调试下载 Android 7 的源代码时,我发现了这个问题。 任何一种健康都是非常可观的。

【问题讨论】:

试试getLength() 我得到的 @pskink AssetFileDescriptor 的长度属性为 -1。我尝试了 getLength(),结果与以前相同 - NegativeByteArraySizeException 那么干脆不要使用它:而是在循环中读取你的数据 @pskink 您看待问题的方式与我看待问题的方式非常不同。你向我展示了我的解决方案。我得到了link 的帮助。谢谢。那么,我应该发布我的解决方案吗?或者你有什么建议? 随意发布您的解决方案作为答案 【参考方案1】:

在@pskink 的帮助下,我发现以下解决了我的问题。

String lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
        Uri vCardUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
        AssetFileDescriptor assetFileDescriptor;
        FileInputStream inputStream;
        try 
            assetFileDescriptor = getActivity().getContentResolver().openAssetFileDescriptor(vCardUri, "r");
            if (assetFileDescriptor != null) 
                inputStream = assetFileDescriptor.createInputStream();
                return readAsByteArray(inputStream);
            
         catch (FileNotFoundException e) 
            Log.e(TAG, "Vcard for the contact " + lookupKey + " not found", e);
         catch (IOException e) 
            Log.e(TAG, "Problem creating stream from the assetFileDescriptor.", e);
        

readAsByteArray() 是使用Mihai Snippet 中的代码编写的。

谢谢@pskink

【讨论】:

顺便说一句,我认为您也可以直接使用ContentResolver#openInputStream(Uri uri) 是的。这甚至可以简化我的代码。这也有帮助【参考方案2】:

在您的代码中,您正在使用 openAssestFileDescriptor 而不是使用 openFileDescriptor,因为 openAssestFileDescriptor 用于读取资产文件夹中的文件而不是文件 Uri

    package com.daffo.***test;

    import android.Manifest;
    import android.content.pm.PackageManager;
    import android.content.res.AssetFileDescriptor;
    import android.content.res.AssetManager;
    import android.database.Cursor;
    import android.net.Uri;
    import android.os.Build;
    import android.os.Environment;
    import android.os.ParcelFileDescriptor;
    import android.provider.ContactsContract;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.Toast;

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.nio.channels.FileChannel;
    import java.util.ArrayList;
    import java.util.List;

public class MainActivity extends AppCompatActivity 
    Cursor cursor;
    ArrayList<String> vCard;
    String vfile;
    private static final int PERMISSIONS_REQUEST_READ_CONTACTS = 100;

/**
 * Called when the activity is first created.
 */
@Override
public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    vfile = "Contacts" + "_" + System.currentTimeMillis() + ".vcf";
    /**This Function For Vcard And here i take one Array List in Which i store every Vcard String of Every Conatact
     * Here i take one Cursor and this cursor is not null and its count>0 than i repeat one loop up to cursor.getcount() means Up to number of phone contacts.
     * And in Every Loop i can make vcard string and store in Array list which i declared as a Global.
     * And in Every Loop i move cursor next and print log in logcat.
     * */
    addContactPermissions();



/**
 * Show the contacts in the ListView.
 */
private void addContactPermissions() 
    // Check the SDK version and whether the permission is already granted or not.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && (checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED || checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) 
        requestPermissions(new String[]Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_EXTERNAL_STORAGE, PERMISSIONS_REQUEST_READ_CONTACTS);
        //After this point you wait for callback in onRequestPermissionsResult(int, String[], int[]) overriden method
     else 
        // Android version is lesser than 6.0 or the permission is already granted.
        getVcardString();
    


@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
                                       int[] grantResults) 
    if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS) 
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) 
            // Permission is granted
            getVcardString();
         else 
            Toast.makeText(this, "Until you grant the permission, we canot display the names", Toast.LENGTH_SHORT).show();
        
    


private void getVcardString() 
    // TODO Auto-generated method stub
    vCard = new ArrayList<String>();
    cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
    if (cursor != null && cursor.getCount() > 0) 
        cursor.moveToFirst();
        for (int i = 0; i < cursor.getCount(); i++) 

            get(cursor);
            if (vCard.size() > 0) 
                Log.d("TAG", "Contact " + (i + 1) + "VcF String is" + vCard.get(i));
            
            cursor.moveToNext();
        

     else 
        Log.d("TAG", "No Contacts in Your Phone");
    



public void get(Cursor cursor) 


    //cursor.moveToFirst();
    String lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
    Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
    ParcelFileDescriptor fd;
    try 

        fd = this.getContentResolver().openFileDescriptor(uri, "r");
        // Your Complex Code and you used function without loop so how can you get all Contacts Vcard.??


       /* FileInputStream fis = fd.createInputStream();
        byte[] buf = new byte[(int) fd.getDeclaredLength()];
        fis.read(buf);
        String VCard = new String(buf);
        String path = Environment.getExternalStorageDirectory().toString() + File.separator + vfile;
        FileOutputStream out = new FileOutputStream(path);
        out.write(VCard.toString().getBytes());
        Log.d("Vcard",  VCard);*/

        FileInputStream fis = new FileInputStream(fd.getFileDescriptor());
        byte[] buf = new byte[fis.available()];
        fis.read(buf);
        String vcardstring = new String(buf);
        vCard.add(vcardstring);

        String storage_path = Environment.getExternalStorageDirectory().toString() + File.separator + vfile;
        FileOutputStream mFileOutputStream = new FileOutputStream(storage_path, false);
        mFileOutputStream.write(vcardstring.toString().getBytes());
     catch (Exception e1) 
        // TODO Auto-generated catch block
        e1.printStackTrace();
    


【讨论】:

按照 pskink 的建议 - 顺便说一句,我认为您也可以直接使用 ContentResolver#openInputStream(Uri uri) - pskink,我现在不使用任何一个描述符。但是,这不是我的代码的问题。在问这个问题之前,我已经尝试过了。感谢您有兴趣回答问题。你的代码看起来不错【参考方案3】:

在 Android 牛轧糖中:

byte[] b = new byte[(int)fd.getDeclaredLength()];

fd.getDeclaredLength() 返回 -1。

在这里查看我的答案link。

【讨论】:

【参考方案4】:

替换这个

AssetFileDescriptor assetFileDescriptor = activity.getContentResolver().openAssetFileDescriptor(uri, "r");
FileInputStream fis = assetFileDescriptor.createInputStream();
byte[] buf = new byte[(int) assetFileDescriptor.getDeclaredLength()];

有了这个

AssetFileDescriptor assetFileDescriptor = activity.getContentResolver().openAssetFileDescriptor(uri, "r");
FileInputStream fis = assetFileDescriptor.createInputStream();
byte[] buf = new byte[fis.available()];

【讨论】:

以上是关于在 Android 7 中,ContentResolver 的方法 openAssetFileDescriptor(vCardUri, "r") 返回 AssetFileDescr的主要内容,如果未能解决你的问题,请参考以下文章

Android应用开发中出现appcompat-v7错误

如何在Android Studio中添加RecyclerView-v7支持包

关于 Android 7.0 适配中 FileProvider 部分的总结

为啥我的外部存储在 Android 7 中不可读写 [重复]

使用 appcompat-v7 在 Android 中视频播放器熄灯模式

我们来一探究竟 Android 7.0 适配中 FileProvider 部分