【问题标题】:Backup the contacts to a .VCF file doesn't work将联系人备份到 .VCF 文件不起作用
【发布时间】:2017-07-17 14:15:28
【问题描述】:

因为我是 Java 新手,所以我自己编写了这段代码,其中一些部分是从其他来源学习的。我想将所有联系人信息备份到主存储或 sdcard 中的 .vcf 文件中(没关系),最后将它们取回。但在这段代码中你只能看到备份部分:

public class MainActivity extends Activity
{
    Cursor cursor;
    ArrayList<String> vCard ;
    String vfile;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        vfile = "Contacts" + "_" + System.currentTimeMillis()+".vcf";
        getVcardString();

    }
    private void getVcardString() {
        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);
                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)
    {

        String lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
        Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
        AssetFileDescriptor fd;
        try {
            fd = this.getContentResolver().openAssetFileDescriptor(uri, "r");

            FileInputStream fis = fd.createInputStream();
            byte[] buf = new byte[(int) fd.getDeclaredLength()];
            fis.read(buf);
            String vcardstring= new String(buf);
            vCard.add(vcardstring);

            String storage_path = Environment.getRootDirectory().toString() + File.separator + vfile;
            FileOutputStream mFileOutputStream = new FileOutputStream(storage_path, false);
            mFileOutputStream.write(vcardstring.toString().getBytes());

        } catch (Exception e1)
        {
            e1.printStackTrace();
        }
    }

}

问题是代码不起作用并且没有出现异常或任何错误。我搜索了根目录和所有文件夹,但没有 .vcf 文件。我的权限是:<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> 我在 API 17 和 24 上运行了它。知道为什么它不起作用吗?

【问题讨论】:

  • 要获取 SD 卡路径,您必须使用 Environment.getExternalStorageDirectory() Environment.getRootDirectory() 返回设备根目录“/” - 您无法在此目录中写入(或读取)在非根设备上。
  • 您也获得了写入外部存储权限吗?
  • @kapsym 啊,我不知道我也应该使用该权限,但我的首要任务是将该文件保存在设备的存储中。如果 Environment.getRootDirectory() 不起作用,那么我应该如何将它保存在设备内存中? (不是外部存储器)
  • 如果你不想要外部存储,那么你可以使用内部存储。在这种情况下,您不需要写权限,并且您的文件将仅对您的应用程序可见。参考这里-developer.android.com/guide/topics/data/…
  • @kapsym Tnx,有几个问题:1:我的代码现在是否正确,应该可以正常用于内部存储?

标签: android backup contact


【解决方案1】:

public void shoro(View v) throws IOException {

    Toast.makeText(this,"ok2",Toast.LENGTH_SHORT).show();
    vfile = "Contacts" + "_" + System.currentTimeMillis()+".vcf";
    vCard = new ArrayList<String>();
    cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
    if(cursor!=null&&cursor.getCount()>0)
    {
        Toast.makeText(this,"ok3",Toast.LENGTH_SHORT).show();
        cursor.moveToFirst();
        for(int i =0;i<cursor.getCount();i++)
        {

            get(cursor);
            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) throws IOException {
    Toast.makeText(this,"ok4",Toast.LENGTH_SHORT).show();

    String lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
    Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
    AssetFileDescriptor fd;

    fd = this.getContentResolver().openAssetFileDescriptor(uri, "r");

    FileInputStream fis = fd.createInputStream();
    byte[] buf = new byte[(int) fd.getDeclaredLength()];
    fis.read(buf);
    String vcardstring= new String(buf);
    vCard.add(vcardstring);

    String storage_path = Environment.getDataDirectory().toString() + File.separator + vfile;
    FileOutputStream mFileOutputStream = new FileOutputStream(storage_path, false);
    mFileOutputStream.write(vcardstring.toString().getBytes());


}

首先看一下那些 Toast,我把它们放出来看看应用程序运行到哪一行。奇怪的是,我第一次问这些问题时忘记将电话号码添加到我的联系人中(我有 3 个联系人进行测试),当我运行应用程序时(当我没有添加号码时)应用程序继续运行没有任何错误,但没有不行(我们之前的谈话是关于这个的,我的错:D)。然后,当我检查 Toast 时,我发现该应用程序永远不会达到“ok3” Toast。之后,当我将号码添加到联系人时,现在该应用程序甚至无法运行。当我按下按钮时,它崩溃并关闭。我将运行中的行放在下面链接(API 17)中的记事本中:

https://ufile.io/i3gu4

我也在 API 24 和 21 上对此进行了测试,但同样的问题。可能是什么问题?

【讨论】:

  • @Magasvazn 我能够找出问题所在。查看更新的答案
【解决方案2】:

public void terp(View v) 抛出 IOException { Toast.makeText(this,"ok",Toast.LENGTH_SHORT).show();

    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this,new String[]{ Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS }, 12345);
    } else {
        // WE ALREADY HAVE PERMISSION, RUN THE MAIN FUNCTION
        shoro();
    }

}


public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    Toast.makeText(this,"salam1",Toast.LENGTH_SHORT).show();
    if ((requestCode == 12345) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
        Toast.makeText(this,"salam2",Toast.LENGTH_SHORT).show();
        // WE GOT THE PERMISSION, RUN THE MAIN FUNCTION
        try {
            shoro();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

shoro() 是主函数。

【讨论】:

    【解决方案3】:

    Android 不允许您在 Environment.getDataDirectoy() 中创建文件。这指向供系统使用的 /data 文件夹。如果你想使用内部存储,你可以使用 /data/data/your_app_package 存储。要访问它,您只需更改 1 行代码 -

    String storage_path = getFilesDir().getAbsolutePath() + File.separator + vfile;
    

    要验证您的文件是否已创建,您可以执行以下步骤

    1. 从工具打开 Android 设备监视器 -> Android -> Android 设备监视器
    2. 在左侧的“设备”选项卡中选择模拟器。
    3. 选择右侧部分的文件资源管理器选项卡
    4. 转到 /data/data/YOUR_PACKAGE_NAME 文件夹。在里面,您将看到创建文件的文件部分

    如果您想使用外部存储,请使用 Environment.getExternalStorageDirectory() 方法。

    要了解 Android 设备中的不同路径,请查看here

    更新 1 讨论后这里是更新的代码。有多个问题 - 1)我们需要接受多个权限并满足 API >= 23 的所有权限 2) AssetFileDescriptor 将 API > = 24 的长度设为 -1 因此代码被破坏了。

    所以在 2 个地方改变 - 改变你的 get 方法 -

     public void get(Cursor cursor) throws IOException {
        Toast.makeText(this,"ok4",Toast.LENGTH_SHORT).show();
    
        String lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
        Uri vCardUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
        AssetFileDescriptor assetFileDescriptor;
        String vcardstring="";
        if (Build.VERSION.SDK_INT >= 24) {
    
            FileInputStream inputStream=null;
            byte[] buffer = new byte[4096];
            ByteArrayOutputStream ous = null;
            try {
                assetFileDescriptor = this.getContentResolver().openAssetFileDescriptor(vCardUri, "r");
    
                if (assetFileDescriptor != null) {
                    ous = new ByteArrayOutputStream();
                    int read = 0;
                    inputStream = assetFileDescriptor.createInputStream();
                    while ((read = inputStream.read(buffer)) != -1) {
                        ous.write(buffer, 0, read);
                    }
                }
            } 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);
            }finally {
                try {
                    if (ous != null)
                        ous.close();
                } catch (IOException e) {
                }
    
                try {
                    if (inputStream != null)
                        inputStream.close();
                } catch (IOException e) {
                }
            }
            vcardstring= new String(ous.toByteArray());
    
        }else{
    
            assetFileDescriptor = this.getContentResolver().openAssetFileDescriptor(vCardUri, "r");
    
            FileInputStream fis = assetFileDescriptor.createInputStream();
            byte[] buf = new byte[(int) assetFileDescriptor.getDeclaredLength()];
            fis.read(buf);
            vcardstring= new String(buf);
    
        }
    
    
        vCard.add(vcardstring);
        Log.i("File Kapil", "path "+getFilesDir().getAbsolutePath().toString());
        String storage_path = getFilesDir().getAbsolutePath() + File.separator + vfile;
        FileOutputStream mFileOutputStream = new FileOutputStream(storage_path, false);
        mFileOutputStream.write(vcardstring.toString().getBytes());
    
    }
    

    对于请求权限更改 -

    public  boolean isStoragePermissionGranted() {
        if (Build.VERSION.SDK_INT >= 23) {
    
            if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    == PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.WRITE_CONTACTS)
                    == PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.READ_CONTACTS)
                    == PackageManager.PERMISSION_GRANTED) {
                Log.v(TAG,"Permission is granted");
                return true;
            } else {
    
                Log.v(TAG,"Permission is revoked");
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS}, 1);
                return false;
            }
        }
        else { //permission is automatically granted on sdk<23 upon installation
            Log.v(TAG,"Permission is granted");
            return true;
        }
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case 0:
                boolean isPerpermissionForAllGranted = false;
                if (grantResults.length > 0 && permissions.length==grantResults.length) {
                    for (int i = 0; i < permissions.length; i++){
                        if (grantResults[i] == PackageManager.PERMISSION_GRANTED){
                            isPerpermissionForAllGranted=true;
                        }else{
                            isPerpermissionForAllGranted=false;
                        }
                    }
    
                    Log.e("value", "Permission Granted, Now you can use local drive .");
                } else {
                    isPerpermissionForAllGranted=true;
                    Log.e("value", "Permission Denied, You cannot use local drive .");
                }
                if(isPerpermissionForAllGranted){
                    shoro();
                }
                break;
        }
    }
    

    在你的 Shoro 方法中稍微修改一下代码——

     public void shoro(){
       boolean isPermissionGiven =  isStoragePermissionGranted();
       Toast.makeText(this,"ok2",Toast.LENGTH_SHORT).show();
        if (isPermissionGiven){
            // Do stuff
        }
     }
    

    完整代码可见-Here

    【讨论】:

    • 谢谢,这个问题似乎只在不需要权限的 API 上得到解决(我认为是 22 岁以上),较新 API 上的应用程序再次崩溃,并在运行部分出现此错误:java.lang.RuntimeException: Failure delivering result ResultInfo{who=@android:requestPermissions:, request=12345, result=-1, data=Intent { act=android.content.pm.action.REQUEST_PERMISSIONS (has extras) }} to activity {com.example.k.backup2/com.example.k.backup2.MainActivity}: java.lang.NegativeArraySizeException: -1
    • 另外,我将下面代码的第 4 行更改为:ActivityCompat.requestPermissions(this,new String[]{ Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE }, 12345); 没有再次工作。也许我错了,但我知道现在该怎么做。顺便说一句,应用程序在其他 API(不需要权限)上运行良好,我可以看到你所说的 .vcf 文件。
    • 我正在寻找更高的 API 版本。给我15分钟
    • @Magasvazn 抱歉花了我这么多时间。我已经用更新的代码更新了答案。看看。
    • @Magasvazn 那么这最终对你有用吗?它至少在两个 API 上都对我有用
    猜你喜欢
    • 2011-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-26
    • 2013-03-22
    • 2016-11-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多