使用ShouldInterceptRequest()的音频在Android WebView中不起作用 [英] Audio not working in Android WebView using shouldInterceptRequest()

查看:89
本文介绍了使用ShouldInterceptRequest()的音频在Android WebView中不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的Android应用正在WebView中显示html5电子书.
我有一个包含电子书及其所有资源的压缩文件:文本,图像和音频(mp3文件).
为了解压缩这本书,我使用了shouldInterceptRequest(),它拦截了file:///...请求,并通过WebResourceResponse对象返回数据.该代码适用于文本和图像.
当我获得音频资源时,会遇到运行时错误,并且音频文件无法播放.
注意:我确实看到返回的解压缩文件具有正确的大小(大约10MB).

My Android app is showing an html5 e-book in a WebView.
I have a zipped file containing an e-book with all its resources: text, images and audio (mp3 files).
In order to unzip the book I use shouldInterceptRequest(), which intercepts the file:///... requests, and returns the data via a WebResourceResponse object. The code works fine for text and images.
When I get to audio resources, I get runtime errors, and the audio file is not played.
Note: I do see the unzipped file is returned with the correct size (about 10MB).

我收到的错误消息:
cr_MediaResourceGetter文件不存在
cr_MediaResourceGetter无法配置元数据提取器

Error messages I get:
cr_MediaResourceGetter File does not exist
cr_MediaResourceGetter Unable to configure metadata extractor

我的音频HTML代码:

<div xmlns="http://www.w3.org/1999/xhtml">
  <p style="text-align:center;margin:0px;">
    <audio controls="controls" src="../Audio/01-AudioTrack-01.mp3">Your browser does not support the audio tag.</audio>
    <br />
  </p>
</div>

我的Android代码:

    setWebViewClient(new WebViewClient()
    {
    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, final String url)
    {
        String urlWithoutAnchor = URLUtil.stripAnchor(url); 
        String fileName = urlWithoutAnchor;

        try {
            byte [] resource = tbxPool.getResource(fileName); 
         /* SIMPLE VERSION without calling setResponseHeaders():
            return new WebResourceResponse(mimeType, "UTF-8", new ByteArrayInputStream(resource)); 
            */
            WebResourceResponse returnedMediaResource = new WebResourceResponse(mimeType, "UTF-8", new ByteArrayInputStream(resource));
            if (mimeType.toLowerCase().startsWith("audio")) {
                Map<String, String> responseHeaders = new HashMap<String, String>();

                responseHeaders.put("Content-Type", mimeType);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//2CLEAN
                    returnedMediaResource.setResponseHeaders(responseHeaders); 
                    Logger.v(TAG, "Response Headers added to audio resource");
                }
                else {
                    //TODO: Handle else for API<21. Toast?
                }
            }                               
            return returnedMediaResource;
        } catch (IOException e) {
            Logger.e(TAG, "failed to load resource "+fileName,e);
            return null;
        }
    }
}

环境
Android 6.0.1(Nexus 5) Android System WebView版本47

Environment
Android 6.0.1 (Nexus 5) Android System WebView version 47

澄清要求
音频应像html5文档一样在浏览器中播放,而不必启动外部播放器.

Requirement Clarification
The audio is to play in browser like an html5 document should, without laucnhing external player.

问题:
我究竟做错了什么?!提前非常感谢!

Question:
What am I doing wrong?! Many Thanks in advance!

推荐答案

我发现此问题的解决方法并不优雅,但这是唯一对我有用的方法:将音频文件写入sd卡:(.
阶段1):何时使用章节url调用shouldInterceptRequest().
首先拦截该章(在拦截其他章资源(图像,音频,字体等)之前.
当章节被截取时,我们在html中搜索<audio>标记.如果找到,我们将替换相对路径(例如SRC ="../Audio/abc.mp3") 带有绝对路径(例如SRC ="/storage/tmp/abc.mp3")

The workaround I found to this problem is not elegant, but it's the only one that worked for me: Write the audio file to sd card :(.
Stage 1): When shouldInterceptRequest() is called with a chapter url.
The chapter is intercepted first (before the other chapter resources (images, audio, fonts, ..) are intercepted.
When the chapter is intercepted we search the html for the <audio> tag. If found, we replace the relative path (e.g. SRC="../Audio/abc.mp3") with an absolute path (e.g. SRC="/storage/tmp/abc.mp3")

第2阶段:使用音频网址调用shouldInterceptRequest()时.
你的注意力.像所有变通办法一样,这有点棘手(但可行!):
在第1阶段之后,音频URL将是一个绝对URL(该绝对URL即是现在在修改后的html中写的内容).
现在,我们必须做两件事:
a)从压缩的epub中读取音频文件.
为此,我们需要欺骗"代码,并从其原始的压缩相对URL中读取音频文件,例如在我们的示例中为"../Audio/abc.mp3" (尽管应该使用"/storage/tmp/abc.mp3"调用了shouldInterceptRequest).
b)阅读压缩的音频文件后,将其写入存储设备(sdcard)

Stage 2): When shouldInterceptRequest() is called with an audio url.
Your attention. Like all workarounds this is a bit tricky (but works!):
After Stage 1) the audio url will be an absolute url (the absolute url is what is now written in the modified html).
We now have to do 2 things:
a) read the audio file from the zipped epub.
To do this we need to "fool" the code, and read the audio file from its original zipped relative url, e.g. "../Audio/abc.mp3" in our example (although shouldInterceptRequest has been called with "/storage/tmp/abc.mp3").
b) After reading the zipped audio file, write it to the storage (sdcard)

第3阶段)当使用一章网址调用shouldInterceptRequest()时, 我们删除临时音频文件 注意:如果遵循该代码,您将在shouldInterceptRequest()中看到这是步骤0),该步骤在阶段1)之前执行,但是如上所述,我发现它更清楚了.

Stage 3) When shouldInterceptRequest() is called with a chapter url, We delete the temp audio files Note: If you follow the code, you will see this is Step 0) in shouldInterceptRequest(), executed before stage 1), but I found it clearer explained as above.

if (isChapterFile(mimeType)) {  
    deleteTempFiles(); // this line is stage 3)  
        changeAudioPathsInHtml(tzis); // this line is stage 1)

这是代码:

setWebViewClient(new WebViewClient()
{
    private String tmpPath = TbxApplication.getAppPath(null) + "/tmp/"; 
    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, final String url) 
    {
        Logger.d(TAG, "in shouldInterceptRequest for " + url);  
        String urlWithoutAnchor = URLUtil.stripAnchor(url); 
        String mimeType = StringUtils.getFileMimeType(urlWithoutAnchor);
        String urlWithoutBase; //the url stripped from leading 'epubBaseUrl' (base url for example:"file:///storage/.../123456.tbx")

            if (isAudioFile(mimeType)) { //write AUDIO file to phone storage. See AUDIO WORKAROUND DOCUMENTATION    
                String storagePath = StringUtils.truncateFileScheme(url);  //WebView calls shoudlInterceptRequest() with "file://..."
                try {
                    String oEBPSAudioPath = storagePathToOEBPSAudioPath(storagePath); //e.g. change"/storage/tmp" to "OEBPS/Audio/abc.mp3"
                    byte[] audioBytes = tbxPool.getMedia(oEBPSAudioPath); 
                    FileUtils.writeByteArrayToFile(audioBytes, storagePath); //TODO: To be strict, write in separate thread
                    Logger.d(TAG, String.format("%s written to %s", oEBPSAudioPath, storagePath));
                    return null;//webView will read resource from file
                                //Note: return new WebResourceResponse("audio/mpeg", "UTF-8", new ByteArrayInputStream(audioBytes));
                                //did NOT work,so we had to change html for audio to point to local storage & write to disk
                                //see AUDIO WORKAROUND DOCUMENTATION in this file
                } catch (Exception e) {
                    Logger.e(TAG,e.getMessage());
                    return null;
                } 
            } 
            .....
            else {
                if (isChapterFile(mimeType)) { //This is a CHAPTER
                    deleteTempFiles(); //Loading a new chapter. Delete previous chapter audio files. See AUDIO WORKAROUND DOCUMENTATION in this file
                    InputStream htmlWithChangedAudioPaths = changeAudioPathsInHtml(tzis); //see AUDIO WORKAROUND DOCUMENTATION in this file
                    WebResourceResponse webResourceResponse = new WebResourceResponse(mimeType, "UTF-8", htmlWithChangedAudioPaths);
                    return webResourceResponse; 
                }

        //Changes relative paths of audio files, to absolute paths on storage
        //see AUDIO WORKAROUND DOCUMENTATION in this file
        private InputStream changeAudioPathsInHtml(InputStream inputStream) {
            String inputString = StringUtils.inputStreamToString(inputStream);
            String outputString = inputString.replaceAll("\"../Audio/", "\"" + tmpPath);// e.g. SRC="../Audio/abc.mp3" ==>SRC="/sdcard/tmp/abc.mp3"                                                                              //where '*' stands for multiple whitespaces would be more elegant
            return StringUtils.stringToInputStream(outputString); 
        }

        /** Example:
         * storagePath="/storage/tmp/abc.mp3
         * Returns: "OEBPS/Audio/abc.mp3"*/
        private String storagePathToOEBPSAudioPath(String storagePath){
            String fileName = StringUtils.getFileName(storagePath);
            String tbxOEBPSAudioPath = "OEBPS/Audio/" + fileName;
            return tbxOEBPSAudioPath; 
        }

public static void writeByteArrayToFile(byte[] byteArray, String outPath) { 
    try {
        File file = new File(outPath);
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(byteArray);
        fos.close();
    } catch (IOException e) {
        Logger.e(TAG, String.format("Could not write %s", outPath));
    }
}

这篇关于使用ShouldInterceptRequest()的音频在Android WebView中不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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