Я пишу написал приложение, которое открывает текстовый файл через Uri: (getContentResolver().openInputStream(mUri))
дальше после изменения текста в EditText по нажатию кнопки я пытаюсь сохранить содержимое файла по полученному Uri: (getContentResolver().openOutputStream(mUri);)
проблема в том, что на эмуляторах весь код отрабатывает, а вот когда отлаживаю на реальном устройстве (Asus ZE553KL Android 8.0.0), то при сохранении выдает ошибку: "java.io.IOException: write failed: EBADF (Bad file descriptor)"
Помогите пожалуйста правильно реализовать код. Спасибо! Ниже весь код MainActivity.
package com.aginf.notetohelp;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
public class MainActivity extends AppCompatActivity {
private EditText mEditText;
private Uri mUri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEditText = (EditText) findViewById(R.id.editText);
//////////////////////////////////////
// Получаю Uri только при SCHEME_CONTENT
// и сразу открываю файл и помещаю его в EditText
Intent intent = getIntent();
String action = intent.getAction();
if (action.compareTo(Intent.ACTION_VIEW) == 0) {
String scheme = intent.getScheme();
if (scheme.compareTo(ContentResolver.SCHEME_CONTENT) == 0) {
mUri = intent.getData();
OpenFromUri();
}
}
}
///////////////////////////////////
// Процедура получения содержимого файла по Uri
private void OpenFromUri() {
StringBuilder stringBuilder = new StringBuilder();
Boolean res;
try{
InputStream inputStream = getContentResolver().openInputStream(mUri);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line + "\n");
}
mEditText.setText(stringBuilder.toString());
}catch(Throwable t){
Toast.makeText(getApplicationContext(),"ERROR (open from Uri): "+t.toString(), Toast.LENGTH_LONG).show();
}
}
///////////////////////////////////
// ПСохранение содержимого в файл по Uri
public void onSaveClick(View view) {
try{
OutputStream outputStream = getContentResolver().openOutputStream(mUri);
OutputStreamWriter osw = new OutputStreamWriter(outputStream);
osw.write(mEditText.getText().toString());
osw.close();
outputStream.close();
Toast.makeText(MainActivity.this, "Saved", Toast.LENGTH_SHORT).show();
}catch(Throwable t){
Toast.makeText(getApplicationContext(),"ERROR (save from Uri):"+t.toString(), Toast.LENGTH_LONG).show();
}
}
}
Манифест:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.aginf.notetohelp">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:icon="@mipmap/ic_launcher_round" android:label="Note to help" android:priority="0">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" />
<data android:mimeType="text/plain" />
<data android:pathPattern=".*\\.txt" />
<data android:pathPattern=".*\\..*\\.txt" />
<data android:pathPattern=".*\\..*\\..*\\.txt" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.txt"/>
</intent-filter>
</activity>
</application>
</manifest>
вы забыли закрыть stream, в методе OpenFromUri()
private void OpenFromUri() {
StringBuilder stringBuilder = new StringBuilder();
Boolean res;
try{
InputStream inputStream = getContentResolver().openInputStream(mUri);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line + "\n");
}
mEditText.setText(stringBuilder.toString());
inputStream .close(); // ДОБАВИЛ строку тут
}catch(Throwable t)
{
Toast.makeText(getApplicationContext(),"ERROR (open from Uri): "+t.toString(), Toast.LENGTH_LONG).show(); } }
По итогам дискуссии в комментариях вижу следующий диагноз. Сначала заглянем под капот:
Ваш use-case подразумевает, что юзер через стоковый файл-менеджер (какой бы он ни был) выбирает текстовый файл и "открывает" его. При этом в зависимости от локальной ситуации конкретных настроек девайса юзер либо выбирает с помощью чего он будет открывать текстовый файл, либо открывает приложением по умолчанию - в любом случае ваш use case подразумевает, что юзер откроет его с помощью вашего приложения.
На стороне файл-менеджера происходит при этом следующее:
1) На сцену выходит ContentProvider
, в задачу которого входит формирование Uri
для выдачи его внешнему Intent
'у - в данном случае вашему приложению. Поскольку речь идет об Android 8 то прямые ссылки типа file://
запрещены, поэтому Uri
должен быть вида content://
2) В зависимости от файлового менеджера провайдер может иметь вид FileProvider
или DocumentsProvider
и т.д.
3) В любом случае в провайдере должны быть вариации с наличием/отсутствием разрешения на редактирование, типа:
<provider android:authorities="list"
android:grantUriPermissions=["true" | "false"]
android:permission="string"
android:readPermission="string"
android:writePermission="string" >
</provider>
4) В вашем случае, очевидно, что провайдер не предоставляет разрешения на запись.
Это либо баг конкретного провайдера файлового менеджера либо фича :) То есть надо писать в поддержку/форум поставщика девайса
Другой вариант, при "вылете" Exception
- интеллигентно сообщить юзеру - так мол и так, файл увы не поддается редактированию.
Есть еще вариант поиграться с разными видами ACTION_OPEN_DOCUMENT
, ACTION_EDIT
и т.д. - надежды конечно мало.
Как то так.
Update
Вам нужен доступ к файлам через Storage Access Framework, вам надо получать доступ к файлам именно через него, а он активируется применением ACTION_OPEN_DOCUMENT
, а не ACTION_VIEW
, только это гарантирует доступ к файлу в режиме редактирования.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Хотите улучшить этот вопрос? Обновите вопрос так, чтобы он вписывался в тематику Stack Overflow на русском
По какой причине нельзя создать несколько public-классов в одномjava-файле? Почему можно создавать без модификатора доступа?
Вот такой код у меня получился для воспроизведения радио через Exoplayer: