我已经尝试了 3 种方法来解决这个问题,每种方法都是其他方法的变体,每种方法都涉及将原始音频文件从内部“资产”目录(默认 MediaPlayer 显然无法访问)复制到一个目录可以访问。各种目标目录在链接中描述:
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" ]
在所有情况下,文件都被分配了“世界可读”权限,以使默认 MediaPlayer 可以访问它们。第一种方法(内部)是首选,因为文件作为包本身的一部分并入,可以通过设备的“管理应用程序”应用程序从“清除数据”选项中删除,并在卸载应用程序时自动删除。第二种方法与第一种方法类似,但仅在卸载应用程序时删除文件,依赖于外部存储,并且需要权限才能写入清单中指定的外部存储。第三种方法是最不利的;它与第二种类似,但在卸载应用程序时没有清理音频文件。为每个标签提供了两个标签,
内部存储解决方案
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" />
 
</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" />
 
</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(我最终可能会对此进行试验)。