使用< audio>的Android 4.0.4 WebView MediaPlayer错误(1,-2147483648)标签和本地资产文件 [英] Android 4.0.4 WebView MediaPlayer Error (1, -2147483648) Using <audio> Tag and Local Assets File

查看:202
本文介绍了使用< audio>的Android 4.0.4 WebView MediaPlayer错误(1,-2147483648)标签和本地资产文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Android新手,一直在尝试获取HTML5< audio>标记可在WebView浏览器中使用,但始终会收到MediaPlayer错误(1,-2147483648).我要播放的文件位于资产"目录下.我尝试引用"res/raw"目录中的文件,但结果相同.

I'm new to Android and have been trying to get the HTML5 <audio> tag to work in a WebView browser but keep getting MediaPlayer Error (1, -2147483648). The file I'm trying to play resides below the "assets" directory. I've tried referencing a file in the "res/raw" directory, but with the same result.

为了验证是否可以找到和播放文件,作为测试的一部分,我创建了代码的变体,其中将通过&​​lt; a>触发声音.标记,并将由WebViewClient使用以下建议进行处理:

To verify that the files could be found and played, as part of my tests I created a variation of the code where the sound would be triggered through an <a> tag and would be processed by a WebViewClient, using the suggestions here:

Android:使用WebView播放资产声音

它起作用了(尽管我不得不从URL中删除前导的"file:///android_asset"),但是使用锚并不是我希望页面运行的方式.我想在打开页面时播放背景声音,并在某些< div>设置为时通过Javascript触发其他声音.标签被点击.我在其他地方读过Android现在支持标签,但是我对它们没有运气,我使用的是最新的SDK.

It worked (although I had to trim off the leading "file:///android_asset" from the URL), but using anchors is not how I want the pages to operate. I'd like a background sound to play when the page opens and other sounds to be triggered through Javascript when certain <div> tags are clicked. I've read elsewhere that Android now supports tags, but I've had no luck with them and I'm using the latest SDK.

我创建了一个简化的测试页进行试验,其详细信息如下所示.我四处寻找没有运气的解决方案.我不知道缺少什么(我的首选是尽可能避免使用任何附加组件,而只能与Android一起使用).

I've created a stripped down test page to experiment with, details of which are shown below. I've looked all over for a solution with no luck. I don't know what's missing (my preference is to avoid any add-ons if possible and work with Android alone).

资产目录布局

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>

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"/>
        &#160;
     </audio>
   </div>
</body>
</html>

推荐答案

我已经尝试了3种方法来解决此问题,每种方法都是其他方法的变体,每种方法都涉及从内部复制原始音频文件.资产"目录(显然默认MediaPlayer无法访问)到可以访问的目录.链接中描述了各种目标目录:

I've experimented with 3 approaches to addressing this problem, each of which is a variation of the others, and each involve copying the original audio files from the internal "assets" directory (apparently not accessible to the default MediaPlayer) to a directory which can be accessed. The various target directories are described at link:

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

使用了这三个:

  • 内部存储[/data/data/your.package.name/files ;IE.context.getFilesDir()]
  • 外部应用程序存储[/mnt/sdcard/Android/data/package_name/files/Music ;IE.context.getExternalFilesDir(null)]
  • 外部常规存储[/mnt/sdcard/temp ;IE.Environment.getExternalStorageDirectory()+"/temp"]
  • internal storage [ /data/data/your.package.name/files; ie. context.getFilesDir() ]
  • external application storage [ /mnt/sdcard/Android/data/package_name/files/Music; ie. context.getExternalFilesDir(null) ]
  • external general storage [ /mnt/sdcard/temp; ie. Environment.getExternalStorageDirectory() + "/temp" ]

在所有情况下,文件都被分配了世界可读"特权,以使默认MediaPlayer可以访问它们.首选第一种方法(内部),因为这些文件作为软件包本身的一部分被合并,并且可以通过设备的管理应用程序"应用程序从清除数据"选项中删除,并且在卸载该应用程序时会自动删除.第二种方法与第一种类似,但是仅在卸载应用程序时才会删除文件,它们依赖于外部存储,并且需要特权才能写入清单中指定的外部存储.第三种方法是最不利的.它与第二种类似,但是在卸载应用程序时不会清除音频文件.为每个标签提供了两个标签,第一个使用Android路径,第二个使用原始路径,这样在将HTML文件合并到Android App中之前,可以先通过Chrome浏览器对其进行检查.

In all cases, the files were assigned "world readable" privileges to make them accessible to the default MediaPlayer. The first approach (internal) is preferred because the files are incorporated as part of the package itself and can be removed from the "Clear data" option through the device's "Manage Apps" application and are automatically removed when the application is uninstalled. The second approach is similar to the first, but the files are only removed when the application is uninstalled, is dependent on external storage, and requires privileges to write to external storage specified in the manifest. The third approach is the least favourable; it is similar to the second, but there is no cleanup of the audio files when the application is uninstalled. Two tags were provided for each tag, the first uses the Android path, and the second uses the original path so that the HTML files can be examined through a Chrome browser before being incorporated in the Android App.

内部存储解决方案

Java代码

Java Code

@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标签

Corresponding HTML Tags

   <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()"的讨论确认其代码可能无法执行:

With this solution, attempted to use "onDestroy()" method to cleanup temporary files copied to "/mnt/sdcard/temp" directory when done but in practise, the method never seemed to executed (tried both "onStop()" and "onPause()" which were called, but they prematurely removed the files). A discussion of "onDestroy()" given here confirms that it's code may not execute:

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

清单输入

Manifest Entry

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

Java代码

Java Code

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标签

Corresponding HTML Tags

   <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".

I won't list the code here, but it's essentially a blend of the solutions shown above. Note that Android must look at the file extensions and decide where to store the files; whether you specify directory "context.getExternalFilesDir(null)" or "context.getExternalFilesDir(Environment.DIRECTORY_MUSIC)", in both cases, the files will be written to directory "/mnt/sdcard/Android/data/package_name/files/Music".

最终评论

尽管上述方法可行,但存在缺点.首先,相同的数据被复制,因此使存储空间增加了一倍.其次,所有声音文件都使用相同的MediaPlayer,因此播放一个声音会中断其前身,从而阻止了播放背景音频,并在该背景音频上播放单独的前景音频.当我尝试使用此处描述的解决方案时,我能够同时播放多个音频文件:

While the above approach works, there are shortcomings. First, the same data is duplicated and therefore doubles the storage space. Second, the same MediaPlayer is used for all sound files so the playing of one will interrupt its predecessor which prevents being able to play background audio over which separate foreground audio is played. When I experimented with the solution described here, I was able to play multiple audio files simultaneously:

Android:使用WebView播放资产声音

我认为一个更好的解决方案是使用Javascript函数来启动音频,该音频又调用启动其自己的MediaPlayer的Android Java方法(我可能最终会尝试使用它).

I think a better solution would be one that uses a Javascript function to launch the audio that in turn calls an Android Java method that starts its own MediaPlayer (I may eventually experiment with that).

这篇关于使用&lt; audio&gt;的Android 4.0.4 WebView MediaPlayer错误(1,-2147483648)标签和本地资产文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆