Использование ContentResolver для записи во внешние файлы [Chrome-ARC]
У меня есть проблема, связанная с записью во внешние файлы в приложении Android, специально предназначенном для Chrome OS, скомпилированном с App Runtime for Chrome. Эта функция приложения работает, как и предполагалось, на двух тестовых устройствах Android (один API 19, как я полагаю, ARC), но всегда терпит неудачу при тестировании на Chromebook. minSdkVersion
моего приложения 19
,
Мое приложение представляет собой текстовый редактор, и я хочу загрузить текст, используя Intent.ACTION_OPEN_DOCUMENT
, сохраняя файл Uri
в процессе. Позже, когда я хочу сохранить, я могу использовать это Uri
чтобы получить доступ к файлу и обновить его содержимое, без необходимости беспокоить пользователя диалоговым окном "Поделиться" каждый раз, когда он хочет сохранить файл (или если они настроили параметр автосохранения. Мое приложение по умолчанию настроено на ACTION_SEND
Намерение, если есть FileNotFoundException
).
Вот соответствующий код. Может ли кто-нибудь предложить обходной путь или какое-либо улучшение, которое я мог бы сделать?
public class EditorHost extends AppCompatActivity implements EditorFragment.EditorInterface {
private static final int REQUEST_OPEN = 15;
//...
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mEditorFragmentChild == null)
mEditorFragmentChild = (EditorFragment) mFragmentManager.findFragmentByTag(EDITOR_FRAGMENT_TAG);
switch (item.getItemId()) {
case R.id.load_file:
open();
break;
case R.id.save_file:
if (mEditorFragmentChild.getUri() == null){
shareTask();
break;
}
mEditorFragmentChild.saveTask();
break;
case R.id.share:
mIntent = new Intent(Intent.ACTION_SEND);
mIntent.putExtra(Intent.EXTRA_TEXT, mEditorFragmentChild.mExtendedEditText.getText().toString());
mIntent.setType("text/plain");
startActivity(Intent.createChooser(mIntent, getString(R.string.share_title)));
break;
@TargetApi(Build.VERSION_CODES.KITKAT)
private void open(){
mIntent = new Intent().setType("text/plain");
startActivityForResult(mIntent.setAction(Intent.ACTION_OPEN_DOCUMENT).addCategory(Intent.CATEGORY_OPENABLE), REQUEST_OPEN);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData){
if (resultCode == Activity.RESULT_OK){
Uri uri = null;
if (resultData != null) {
uri = resultData.getData();
if (requestCode == REQUEST_OPEN) {
Cursor c = getContentResolver().query(uri,null, null, null, null);
if (c != null && c.moveToFirst()){
title = c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME));
String[] cols = c.getColumnNames();
for (int i = 0; i < c.getColumnCount(); i++){
Log.e(LOG_TAG, cols[i]);
}
content = readFromFile(uri);
if (title == null || title.equals("")){
title = getString(R.string.app_name);
}
getSupportActionBar().setTitle(title);
mEditorFragmentChild.updateTextFromFile(content.trim());
mEditorFragmentChild.setUri(uri.toString());
if (!c.isClosed()) {
c.close();
}
}
}
}
}
}
и в EditorFragment
учебный класс:
public class EditorFragment extends Fragment implements ... {
//...
String getUri(){
return mUri;
}
void setUri(String newUri){
mUri = newUri;
}
void saveTask(){
if (mUri == null || fromShareTask) {
fromShareTask = false;
return;
}
try {
OutputStream outputStream = mAppCompatActivity.getContentResolver().openOutputStream(Uri.parse(mUri));
outputStream.write(mExtendedEditText.getText().toString().getBytes());
outputStream.close();
//Toast.makeText(mAppCompatActivity, mAppCompatActivity.getString(R.string.file_updated_confirmation), Toast.LENGTH_SHORT).show();
} catch (IOException e){
e.printStackTrace();
dispatchShareEvent();
Toast.makeText(mAppCompatActivity, mAppCompatActivity.getString(R.string.save_failed), Toast.LENGTH_LONG).show();
mExtendedEditText.setText(e.toString() + "\n\n" + exceptionStacktraceToString(e) + "\n\n" + "mUri is " + mUri);
}
}
И, наконец, сообщение об ошибке (повторюсь сверху, это происходит только на моем Chromebook, а не на тестовом устройстве Android):
java.io.FileNotFoundException: Can't access data/data/org.chromium.arc/external/(etc.)...
Спасибо!
РЕДАКТИРОВАТЬ:
Хотя я не мог понять, как получить трассировку стека из моего Chromebook, я смог добавить следующий метод из принятого ответа на этот вопрос, чтобы распечатать его в EditText
, Я также напечатал Uri
в конце. Вот вывод:
java.io.FileNotFoundException: Can't access /data/data/org.chromium.arc/external/E5783234425D1719508FC512B2BEE2A5/1kTest.txt
java.io.FileNotFoundException: Can't access /data/data/org.chromium.arc/external/E5783234425D1719508FC512B2BEE2A5/1kTest.txt
at com.android.providers.media.MediaProvider.checkAccess(MediaProvider.java:4620)
at com.android.providers.media.MediaProvider.openFileAndEnforcePathPermissionsHelper(MediaProvider.java:4569)
at com.android.providers.media.MediaProvider.openFile(MediaProvider.java:4493)
at android.content.ContentProvider.openAssetFile(ContentProvider.java:1211)
at android.content.ContentProvider.openAssetFile(ContentProvider.java:1274)
at android.content.ContentProvider$Transport.openAssetFile(ContentProvider.java:314)
at android.content.ContentProvider$Transport.openAssetFile(Native Method)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:926)
at android.content.ContentResolver.openOutputStream(ContentResolver.java:673)
at android.content.ContentResolver.openOutputStream(ContentResolver.java:649)
at com.werdpressed.partisan.filterforchrome.EditorFragment.saveTask(EditorFragment.java:370)
at com.werdpressed.partisan.filterforchrome.EditorHost.onOptionsItemSelected(EditorHost.java:119)
at android.app.Activity.onMenuItemSelected(Activity.java:2619)
at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:353)
at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:144)
at android.support.v7.internal.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:99)
at android.support.v7.app.AppCompatDelegateImplV7.onMenuItemSelected(AppCompatDelegateImplV7.java:538)
at android.support.v7.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:802)
at android.support.v7.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:153)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:949)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:939)
at android.support.v7.widget.ActionMenuView.invokeItem(ActionMenuView.java:598)
at android.support.v7.internal.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:139)
at android.view.View.performClick(View.java:4440)
at android.view.View$PerformClick.run(View.java:18407)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:5181)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at android.os.Process$1.run(Process.java:418)
mUri is content://media/external/file/8
Ошибка возникает в следующем try/catch
блок:
try {
OutputStream outputStream = mAppCompatActivity.getContentResolver().openOutputStream(Uri.parse(mUri));
outputStream.write(mExtendedEditText.getText().toString().getBytes());
outputStream.close();
//Toast.makeText(mAppCompatActivity, mAppCompatActivity.getString(R.string.file_updated_confirmation), Toast.LENGTH_SHORT).show();
} catch (IOException e){
e.printStackTrace();
dispatchShareEvent();
Toast.makeText(mAppCompatActivity, mAppCompatActivity.getString(R.string.save_failed), Toast.LENGTH_LONG).show();
mExtendedEditText.setText(e.toString() + "\n" + exceptionStacktraceToString(e) + "\n\n" + "mUri is " + mUri);
}
В частности, это линия 370
ссылка в журнале ошибок:
OutputStream outputStream = mAppCompatActivity.getContentResolver().openOutputStream(Uri.parse(mUri));
1 ответ
Я думаю, вы просто нашли интерфейс для доступа к внешним файлам, о которых мы не думали (или чувствовали, что работать не так важно). Мы исправляем файловую систему для обработки путей sdcard, но не вносим никаких изменений в MediaProvider для обработки содержимого Uri, подобного тому, которое вы пытались использовать.
Пожалуйста, не стесняйтесь сообщать об ошибке с примером кода (или, по крайней мере, со ссылкой на этот пост), чтобы мы могли расставить приоритеты для исправления этой проблемы с нашей другой работой.