Android 4.0.4 Ошибка WebView MediaPlayer (1, -2147483648) Использование файла <audio> Tag и локальных активов

Я новичок в Android и стараюсь, чтобы тег HTML5 <audio> работал в браузере WebView, но продолжайте получать MediaPlayer Error (1, -2147483648). Файл, который я пытаюсь воспроизвести, находится ниже каталога «assets». Я попытался ссылаться на файл в каталоге «res / raw», но с тем же результатом.

Чтобы проверить, что файлы могут быть найдены и воспроизведены, в рамках моих тестов я создал вариант кода, в котором звук будет запускаться с помощью тега <a> и будет обрабатываться WebViewClient, используя следующие предложения:

Android: воспроизведение звука с помощью WebView

Он работал (хотя мне пришлось обрезать ведущий «файл: /// android_asset» из URL-адреса), но использование якорей – это не то, как я хочу, чтобы страницы работали. Я хотел бы, чтобы фоновый звук воспроизводился при открытии страницы, а другие звуки запускались через Javascript при нажатии определенных тегов <div>. Я читал в другом месте, что Android теперь поддерживает теги, но мне не повезло с ними, и я использую последнюю версию SDK.

Я экспериментировал с разделенной тестовой страницей, подробности которой приведены ниже. Я искал решение для решения без везения. Я не знаю, чего не хватает (я предпочитаю избегать любых надстроек, если это возможно, и работать с Android в одиночку).

Макет каталога активов

assets > audio > a-00099954.mp3 > a-00099954.ogg > image > script > style > video audioTest.html 

Код Java

 package com.test.audiotag; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.webkit.WebView; public class MainActivity extends Activity { private WebView localBrowser; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); localBrowser = (WebView)findViewById(R.id.localbrowser); localBrowser.getSettings().setJavaScriptEnabled(true); localBrowser.loadUrl("file:///android_asset/audioTest.html"); } } 

манифест

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android ="http://schemas.android.com/apk/res/android" package ="com.test.audiotag" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="14" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name =".MainActivity" android:label ="@string/app_name" android:screenOrientation="portrait"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> ? <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android ="http://schemas.android.com/apk/res/android" package ="com.test.audiotag" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="14" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name =".MainActivity" android:label ="@string/app_name" android:screenOrientation="portrait"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> 

HTML-страница

 <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="width=300, height=400"> <style type="text/css"> #centre_all { -webkit-box-flex : 0; position : relative; background-color : #D0D000; border : 2px dotted red; -webkit-box-shadow: 0 0 5px red; } </style> </head> <body> <div id="centre_all" style="width:300px;height:400px;"> <audio controls="controls" autoplay="autoplay"> <source src="audio/a-00099954.mp3" type="audio/mpeg" /> <source src="audio/a-00099954.ogg" type="audio/ogg"/>   </audio> </div> </body> </html> 

Я экспериментировал с тремя подходами к решению этой проблемы, каждая из которых является разновидностью других, и каждый из них предполагает копирование исходных аудиофайлов из внутреннего каталога «assets» (по-видимому, не доступного для MediaPlayer по умолчанию) в каталог, который Могут быть доступны. Различные целевые каталоги описаны в ссылке:

http://developer.android.com/guide/topics/data/data-storage.html

Из которых эти три были использованы:

  • Внутреннее хранилище [ /data/data/your.package.name/files ; то есть. Context.getFilesDir ()]
  • Внешнее хранилище приложений [ / mnt / sdcard / Android / data / имя_пакета / файлы / музыка ; то есть. Context.getExternalFilesDir (null)]
  • Внешнее общее хранилище [ / mnt / sdcard / temp ; то есть. Environment.getExternalStorageDirectory () + "/ temp"]

Во всех случаях для файлов были назначены «общедоступные» привилегии, чтобы сделать их доступными для MediaPlayer по умолчанию. Первый подход (внутренний) является предпочтительным, поскольку файлы включены как часть самого пакета и могут быть удалены из опции «Очистить данные» через приложение «Управление приложениями» устройства и автоматически удаляются при удалении приложения. Второй подход похож на первый, но файлы удаляются только при удалении приложения, зависит от внешнего хранилища и требует наличия прав на запись во внешнее хранилище, указанное в манифесте. Третий подход является наименее благоприятным; Он похож на второй, но при удалении приложения очистка аудиофайлов отсутствует. Для каждого тега были предоставлены два тега: первый использует путь Android, а второй использует исходный путь, чтобы файлы HTML могли быть просмотрены через браузер Chrome, прежде чем быть включенными в приложение Android.

Внутреннее хранилище

Код Java

 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); localBrowser = (WebView)findViewById(R.id.localbrowser); localBrowser.getSettings().setJavaScriptEnabled(true); localBrowser.setWebViewClient(new WebViewClient()); localBrowser.setWebChromeClient(new WebChromeClient()); ... Context context = localBrowser.getContext(); File filesDir = context.getFilesDir(); Log.d("FILE PATH", filesDir.getAbsolutePath()); if (filesDir.exists()) { filesDir.setReadable(true, false); try { String[] audioFiles = context.getAssets().list("audio"); if (audioFiles != null) { byte[] buffer; int length; InputStream inStream; FileOutputStream outStream; for (int i=0; i<audioFiles.length; i++) { inStream = context.getAssets().open( "audio/" + audioFiles[i] ); outStream = context.openFileOutput(audioFiles[i], Context.MODE_WORLD_READABLE); buffer = new byte[8192]; while ((length=inStream.read(buffer)) > 0) { outStream.write(buffer, 0, length); } // Close the streams inStream.close(); outStream.flush(); outStream.close(); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } // Feedback String[] fileList = context().fileList(); Log.d("FILE LIST", "--------------"); for (String fileName : fileList) { Log.d("- FILE", fileName); } ... } 

Соответствующие HTML-теги

  <div id="centre_all" style="width:300px;height:400px;"> <audio controls="controls" autoplay="autoplay"> <source src="/data/data/com.test.audiotag/files/a-00099954.mp3" type="audio/mpeg" /> <source src="audio/a-00099954.mp3" type="audio/mpeg" /> &#160; </audio> </div> 

Внешнее общее решение для хранения

С помощью этого решения попытался использовать метод onDestroy () для очистки временных файлов, скопированных в каталог «/ mnt / sdcard / temp», когда это было сделано, но на практике метод никогда не исполнялся (пробовал как «onStop ()», OnPause () ", которые были вызваны, но преждевременно удалили файлы). Дискуссия «onDestroy ()», приведенная здесь, подтверждает, что код не может выполняться:

http://developer.android.com/reference/android/app/Activity.html#onDestroy ()

Запись манифеста

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 

Код Java

 private String[] audioList; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); localBrowser = (WebView)findViewById(R.id.localbrowser); localBrowser.getSettings().setJavaScriptEnabled(true); localBrowser.setWebViewClient(new WebViewClient()); localBrowser.setWebChromeClient(new WebChromeClient()); ... Context context = localBrowser.getContext(); File dirRoot = new File(Environment.getExternalStorageDirectory().toString()); Log.d("ROOT DIR PATH", dirRoot.getAbsolutePath()); if (dirRoot.exists()) { File dirTemp = new File(dirRoot.getAbsolutePath() + "/temp"); if (!dirTemp.exists()) { if (!dirTemp.mkdir()) { Log.e("AUDIO DIR PATH", "FAILED TO CREATE " + dirTemp.getAbsolutePath()); } } else { Log.d("AUDIO DIR PATH", dirTemp.getAbsolutePath()); } if (dirTemp.exists()) { dirTemp.setReadable(true, false); dirTemp.setWritable(true); try { String[] audioFiles = context.getAssets().list("audio"); if (audioFiles != null) { byte[] buffer; int length; InputStream inStream; FileOutputStream outStream; audioList = new String[audioFiles.length]; for (int i=0; i<audioFiles.length; i++) { inStream = context.getAssets().open( "audio/" + audioFiles[i] ); audioList[i] = dirTemp.getAbsolutePath() + "/" + audioFiles[i]; outStream = new FileOutputStream(audioList[i]); buffer = new byte[8192]; while ( (length=inStream.read(buffer)) > 0) { outStream.write(buffer, 0, length); } // Close the streams inStream.close(); outStream.flush(); outStream.close(); //audioFile = new File(audioList[i]); //audioFile.deleteOnExit(); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } // Feedback String[] fileList = dirTemp.list(); Log.d("FILE LIST", "--------------"); for (String fileName : fileList) { Log.d("- FILE", fileName); } } ... } // onCreate() @Override public void onDestroy() { for (String audioFile : audioList) { File hFile = new File(audioFile); if (hFile.delete()) { Log.d("DELETE FILE", audioFile); } else { Log.d("DELETE FILE FAILED", audioFile); } } super.onDestroy(); } // onDestroy() 

Соответствующие HTML-теги

  <div id="centre_all" style="width:300px;height:400px;"> <audio controls="controls" autoplay="autoplay"> <source src="/mnt/sdcard/temp/a-00099954.mp3" type="audio/mpeg" /> <source src="audio/a-00099954.mp3" type="audio/mpeg" /> &#160; </audio> </div> 

Решение для хранения внешних приложений

Я не буду перечислять здесь код, но это, по сути, смесь решений, показанных выше. Обратите внимание, что Android должен посмотреть расширения файлов и решить, где хранить файлы; Задаете ли вы каталог «context.getExternalFilesDir (null)» или «context.getExternalFilesDir (Environment.DIRECTORY_MUSIC)», в обоих случаях файлы будут записаны в каталог «/ mnt / sdcard / Android / data / package_name / files / Music ».

Последние комментарии

Хотя вышеупомянутый подход работает, есть недостатки. Во-первых, одни и те же данные дублируются и поэтому удваивают пространство для хранения. Во-вторых, тот же MediaPlayer используется для всех звуковых файлов, поэтому воспроизведение одного из них прервет его предшественника, что предотвратит возможность воспроизведения фонового звука, по которому воспроизводится отдельный звук переднего плана. Когда я экспериментировал с описанным здесь решением, я смог одновременно воспроизводить несколько аудиофайлов:

Android: воспроизведение звука с помощью WebView

Я думаю, что лучшим решением будет тот, который использует функцию Javascript для запуска звука, который, в свою очередь, вызывает метод Android Java, который запускает свой собственный MediaPlayer (я могу в конечном итоге поэкспериментировать с этим).

В моем случае ошибка была вызвана тем, что медиаплеер не имеет прав доступа к файлам локально сохраненного аудио в каталоге ресурсов. Попробуйте сохранить аудио в каталоге / mnt / sdCARD.

Остерегайтесь, подход грубой силы

Я предполагаю, что вы хотите, чтобы звук воспроизводился внутри веб-страницы, а не напрямую. Я не уверен, что следующее поможет, но я столкнулся с подобными ситуациями, когда я не контролирую HTML (т.е. загружается с какого-то стороннего сайта), и мне все же нужно контролировать его поведение при запуске. Например, Vimeo появляется (на данный момент), чтобы игнорировать параметр «autoplay» uri в мгновенной версии WebView. Таким образом, чтобы получить видеоролик, когда загружалась страница, я использовал запатентованные «вставные руки» примерно до подхода локтей. В вашем случае скрытие тега (за сообщение, которое вы цитируете выше), а затем симуляция щелчка имеет преимущество, заключающееся в том, что вы не набираете страницу до того, как WebView полностью загрузится. Затем вы вводите JS-код (который принят здесь: в Android Webview, могу ли я изменить DOM веб-страницы? ):

  mWebView.setWebViewClient(new CustomVimeoViewClient(){ @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); String injection = injectPageMonitor(); if(injection != null) { Log.d(TAG, " Injecting . . ."); view.loadUrl(injection); } Log.d(TAG, "Page is loaded"); } } 

Используя либо прямой JS, либо имитирующий клики, вы можете управлять своей страницей. Загрузка jQuery кажется немного хам-кулаком только для небольшого расположения тегов, поэтому, возможно, вы можете использовать следующее (что бесстыдно снято здесь. Как получить элемент по классу в JavaScript? ):

  private String injectPageMonitor() { return( "javascript:" + "function eventFire(el, etype){ if (el.fireEvent) { (el.fireEvent('on' + etype)); } else { var evObj = document.createEvent('Events'); evObj.initEvent(etype, true, false); el.dispatchEvent(evObj); }}" + "eventFire(document.querySelectorAll('.some_class_or_another')[0], 'click');"; } 

И до тех пор, пока я уже рассказываю вам о смущающих вещах, я завершусь, исповедуя, что я использую Chrome, чтобы тыкать на сторонние страницы, чтобы найти вещи, которые я хочу использовать для этого. Поскольку на Android нет инструментов разработчика (пока), я настраиваю UserAgent, описанный здесь http://www.technipages.com/google-chrome-change-user-agent-string.html . Затем я закупаю кофе (жидкость, а не Rails Gem), и тыкать в чужой код, нанося вред.

Intereting Posts
Получите медиаплеер видеовидео на Android Как вернуться из основного экрана настроек на главный экран в PreferenceFragmentCompat? Как определить ответное или отклоненное состояние в исходящем вызове Сообщение «Службы Google Play обновляются» в формате SupportMapFragment, когда в режиме деблокирования Приложение Android NDK не может ударить по любой точке останова Используйте вкладку с новым ToolBar (AppCompat v7-21) Android Понимание размеров кучи Получить информацию об устройстве (например, продукт, модель) из команды adb Android – копирование файлов из активов в папку данных / данных Android не может решить намерение конструктора Android: в биллинге приложения RESULT_SERVICE_UNAVAILABLE Синхронные вызовы службы в Android Локальные значки в меню «Настройки Android» растягиваются, чтобы заполнить пункт меню, неясное название Разница между базами данных MySQL / SQLite / etc? Какие ошибки обнаруживаются в «сборке» в Android Studio – роль Gradle