Андроид 6 - запись на внешний носитель не только при первом запуске


Я знаю, что вопрос, похожий на этот, задавался несколько раз, но эта проблема-другая. Мне нужно записать данные во внешнее хранилище. Я знаю, что мне нужно попросить разрешения во время выполнения на Android 6 . Пока все это работает нормально. Только одно странно. Предоставленное разрешение, кажется, работает только во второй раз, когда я запускаю приложение.

Мой пример выглядит так:

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_WRITE_STORAGE: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "Permission granted!");
                    write();
                } else {
                    Log.d(TAG, "No permission granted!");
                }
            }
        }
    }


    @Override
    public void onClick(View v) {
        boolean hasPermission = (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
        if (!hasPermission) {
            Log.d(TAG, "Has no permission! Ask!");
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
        } else {
            Log.d(TAG, "Permission already given!");
            write();
        }
    }

    private void write(){
        Log.d(TAG, "Trying to write");
        // Get the directory for the user's public pictures directory.
        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        if (! root.exists() && !root.mkdirs()) {
            Log.e(TAG, "Directory not created");
            return;
        }

        File testFile = new File(root, "TEST.tmp");
        FileWriter writer;
        try {
            StringBuilder sb = new StringBuilder();
            sb.append("TEST TEST TEST");
            writer = new FileWriter(testFile, false);
            writer.append(sb.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "Write successful");
    }

Нажатие кнопки вызывает onClick(), который проверяет наличие разрешений. Если при условии, что он вызывает метод write() - если он не запрашивает разрешения.

Итак, при первом нажатии кнопки открывается всплывающее окно разрешения, я нажимаю " да " и предполагаю, что он напишет мой тестовый файл. Тем не менее, я получаю следующее сообщение об ошибке, и файл или даже каталог не был создан:

D/LOG: Has no permission! Ask! 
D/LOG: Permission granted! 
D/LOG: Trying to write 
E/LOG: Directory not created

Если я нажму еще раз (с уже предоставленным разрешением), он покажет только

D/LOG: Permission already given!
D/LOG: Trying to write
E/LOG: Directory not created
На самом деле ни директория, ни файл еще не существуют. Поэтому я закрываю приложение и запускаю его снова и нажмите кнопку. Консоль показывает следующее:
D/LOG: Permission already given!
D/LOG: Trying to write
D/LOG: Write successful

Вуаля, теперь каталог и файл существуют.

Так почему же он работает только во второй раз, когда я запускаю приложение? Похоже, что разрешения обновляются только при запуске приложения. Есть идеи?

---- править ----

В связи с комментарием Я разделил exists() и mkdirs().

Тем не менее метод выглядит так:

private void write(){
        Log.d(TAG, "Trying to write");
        // Get the directory for the user's public pictures directory.
        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        if (! root.exists()){
            if(!root.mkdirs()) {
                Log.e(TAG, "Directory not created");
                return;
            } else {
                Log.e(TAG, "Directory created");
            }
        }
        ...

Тем не менее, при первом запуске приложения я получаю Каталог не созданный. При следующем запуске приложения каталог был создан. Так что, похоже, это не проблема.

Также я добавил метод, проверяющий, существует ли Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES). Это верно, даже с самого начала. Что не удается-это mkdirs() для моей вложенной папки.

3 4

3 ответа:

Как опубликовано здесь: https://stackoverflow.com/a/37135078/1565635 Я думаю, что нашел решение - или, по крайней мере, объяснение.

Оказывается, что это, кажется, более серьезная проблема. Изменение разрешения на запись во внешнее хранилище изменяет gid Linux для этого процесса. Чтобы изменить идентификатор, процесс должен быть перезапущен. В следующий раз, когда приложение откроется, будет установлен новый идентификатор группы и предоставлено разрешение. Это приводит к выводу, что это не ошибка, а в факт более серьезная проблема с Linux и Android.

Я "решил" это, попросив разрешения при первом запуске приложения и перезапустив его, когда разрешение дается следующим образом:

PackageManager packageManager = getPackageManager();
        Intent intent = packageManager.getLaunchIntentForPackage(getPackageName());
        ComponentName componentName = intent.getComponent();
        Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName);
        startActivity(mainIntent);
        System.exit(0);

Решение, которое я не пробовал, состояло в том, чтобы создать службу, работающую в фоновом режиме (таким образом, имея другой идентификатор процесса) и дать ей разрешение. Таким образом, необходимо перезапустить только службу, но не все приложение. С другой стороны, это может сделать больше работы для связи между процессы. Если я правильно понимаю, контент-провайдер тоже может работать, но также означает больше работы.

--- править ---

Пользователь M66B (https://stackoverflow.com/a/32473449/1565635 ) нашел список связанных гид. Дополнительную информацию можно найти здесь: https://android.googlesource.com/platform/frameworks/base/+/master/data/etc/platform.xml

Попробуйте этот код: -

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_WRITE_STORAGE: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "Permission granted!");
                    write();
                } else {
                    Log.d(TAG, "No permission granted!");
                }
            }
        }
    }


    @Override
    public void onClick(View v) {
        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        dircheck(root);
        filecheck(new File(root, "TEST.tmp"));
        boolean hasPermission = (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
        if (!hasPermission) {
            Log.d(TAG, "Has no permission! Ask!");
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
        } else {
            Log.d(TAG, "Permission already given!");
            write();
        }
    }

    private void dircheck(File file) {

            if (!file.exists()) {
                file.mkdirs();
            }

    }

    private void filecheck(File file){
        if(!file.exists()){
            file.createNewFile();
        }
    }
    private void write(){

        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");

        File testFile = new File(root, "TEST.tmp");
        FileWriter writer;
        try {
            StringBuilder sb = new StringBuilder();
            sb.append("TEST TEST TEST");
            writer = new FileWriter(testFile, false);
            writer.append(sb.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "Write successful");
    }

Отредактированный ответ : -

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case REQUEST_WRITE_STORAGE: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Log.d(TAG, "Permission granted!");
                write();
            } else {
                Log.d(TAG, "No permission granted!");
            }
        }
    }
}


@Override
public void onClick(View v) {
    boolean hasPermission = (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
    if (!hasPermission) {
        Log.d(TAG, "Has no permission! Ask!");
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
    } else {
        Log.d(TAG, "Permission already given!");
        write();
    }
}

private void dircheck(File file) {
    if (!file.exists()) {
        file.mkdirs();
    }

}

private void filecheck(File file){
    if(!file.exists()){
        try {
            file.createNewFile();    
        }catch (Exception e){e.printStackTrace();}

    }
}
private void write(){
    File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
    dircheck(root);
    filecheck(new File(root, "TEST.tmp"));

    File testFile = new File(root, "TEST.tmp");
    FileWriter writer;
    try {
        StringBuilder sb = new StringBuilder();
        sb.append("TEST TEST TEST");
        writer = new FileWriter(testFile, false);
        writer.append(sb.toString());
        writer.flush();
        writer.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    Log.d(TAG, "Write successful");
}