如何从Android WebView中下载文件?

11

以下是我的代码,它可以很好地加载URL页面。但是,在搜索歌曲后,当我单击下载链接时,它会崩溃。关于如何使下载管理器与WebView一起工作的教程并不多。我做错了什么?

import java.io.File;
import android.app.Activity;
import android.app.DownloadManager;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.webkit.WebView;
import android.webkit.WebViewClient;


public class List1 extends Activity {

        WebView ourBrow;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Use a custom layout file
        setContentView(R.layout.list1);

        final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);

     final File destinationDir = new File (Environment.getExternalStorageDirectory(), getPackageName());
     if (!destinationDir.exists()) {
         destinationDir.mkdir(); // Don't forget to make the directory if it's not there
     }

        ourBrow = (WebView) findViewById(R.id.wvBrowser);
        ourBrow.getSettings().setJavaScriptEnabled(true);
        ourBrow.setInitialScale(50); 
        ourBrow.getSettings().setUseWideViewPort(true); 
        ourBrow.setVerticalScrollBarEnabled(false);
        ourBrow.setHorizontalScrollBarEnabled(false);
        ourBrow.loadUrl("http://www.degjo.com");



        ourBrow.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading (WebView view, String url) {
                boolean shouldOverride = false;
                // We only want to handle requests for mp3 files, everything else the webview
                // can handle normally
                if (url.endsWith(".mp3")) {
                    shouldOverride = true;
                    Uri source = Uri.parse(url);

                    // Make a new request pointing to the mp3 url
                    DownloadManager.Request request = new DownloadManager.Request(source);
                    // Use the same file name for the destination
                    File destinationFile = new File (destinationDir, source.getLastPathSegment());
                    request.setDestinationUri(Uri.fromFile(destinationFile));
                    // Add it to the manager
                    manager.enqueue(request);
                }
                return shouldOverride;
            }
        });





    }
}

LogCat

02-18 19:45:44.891: E/AndroidRuntime(357):  at android.content.ContentProviderProxy.insert(ContentProviderNative.java:408)
02-18 19:45:44.891: E/AndroidRuntime(357):  at android.content.ContentResolver.insert(ContentResolver.java:604)
02-18 19:45:44.891: E/AndroidRuntime(357):  at android.app.DownloadManager.enqueue(DownloadManager.java:750)
02-18 19:45:44.891: E/AndroidRuntime(357):  at com.example.androidbuttonsactivities.List1$1.shouldOverrideUrlLoading(List1.java:78)
02-18 19:45:44.891: E/AndroidRuntime(357):  at android.webkit.CallbackProxy.uiOverrideUrlLoading(CallbackProxy.java:216)
02-18 19:45:44.891: E/AndroidRuntime(357):  at android.webkit.CallbackProxy.handleMessage(CallbackProxy.java:323)
02-18 19:45:44.891: E/AndroidRuntime(357):  at android.os.Handler.dispatchMessage(Handler.java:99)
02-18 19:45:44.891: E/AndroidRuntime(357):  at android.os.Looper.loop(Looper.java:123)
02-18 19:45:44.891: E/AndroidRuntime(357):  at android.app.ActivityThread.main(ActivityThread.java:3683)
02-18 19:45:44.891: E/AndroidRuntime(357):  at java.lang.reflect.Method.invokeNative(Native Method)
02-18 19:45:44.891: E/AndroidRuntime(357):  at java.lang.reflect.Method.invoke(Method.java:507)
02-18 19:45:44.891: E/AndroidRuntime(357):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
02-18 19:45:44.891: E/AndroidRuntime(357):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
02-18 19:45:44.891: E/AndroidRuntime(357):  at dalvik.system.NativeStart.main(Native Method)
02-18 19:45:48.401: I/Process(357): Sending signal. PID: 357 SIG: 9

日志记录器(logcat)对于崩溃有什么说法? - Raghav Sood
3个回答

14

这可能取决于安卓版本的问题,下面的代码将在2.3及以上版本上成功运行,请查看:

ourBrow.setWebViewClient(new WebViewClient() {
        @Override
        public void onReceivedError(WebView view, int errorCode,
            String description, String failingUrl) {
                Log.d("WEB_VIEW_TEST", "error code:" + errorCode + " - " + description);
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
                // handle different requests for different type of files
                // this example handles downloads requests for .apk and .mp3 files
                // everything else the webview can handle normally
                if (url.endsWith(".apk")) {
                    Uri source = Uri.parse(url);
                    // Make a new request pointing to the .apk url
                    DownloadManager.Request request = new DownloadManager.Request(source);
                    // appears the same in Notification bar while downloading
                    request.setDescription("Description for the DownloadManager Bar");
                    request.setTitle("YourApp.apk");
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                        request.allowScanningByMediaScanner();
                        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
                    }
                    // save the file in the "Downloads" folder of SDCARD
                    request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "SmartPigs.apk");
                    // get download service and enqueue file
                    DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
                    manager.enqueue(request);
                }
                else if(url.endsWith(".mp3")) {
                    // if the link points to an .mp3 resource do something else
                }
                // if there is a link to anything else than .apk or .mp3 load the URL in the webview
                else view.loadUrl(url);
                return true;                
        }
    });

蜂窝、allowScanningByMediaScanner和VISIBILITY_VISIBLE_NOTIFY_COMPLETED无法解决。我正在模拟器版本2.3.3上测试应用程序,我是否应该创建某种下载文件夹才能使其正常工作? - Dritan Berna
不需要创建下载文件夹,它一定会下载。尝试使用更高版本的模拟器,否则将您的清单文件最小SDK更改为最新版本并尝试。 - Brendon

1

你在清单文件中添加了DownloadManager权限吗?

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

-----编辑-----

我正在复制一些代码片段,供您参考。这些代码来自Android股票浏览器,用于启动下载任务:

import android.net.WebAddress;

// java.net.URI is a lot stricter than KURL so we have to encode some
// extra characters. Fix for b 2538060 and b 1634719
WebAddress webAddress;
try {
    webAddress = new WebAddress(url);
    webAddress.setPath(encodePath(webAddress.getPath()));
} catch (Exception e) {
    // This only happens for very bad urls, we want to chatch the
    // exception here
    Log.e(LOGTAG, "Exception trying to parse url:" + url);
    return;
}

String addressString = webAddress.toString();
Uri uri = Uri.parse(addressString);
final DownloadManager.Request request;
try {
    request = new DownloadManager.Request(uri);
} catch (IllegalArgumentException e) {
    Toast.makeText(activity, R.string.cannot_download, Toast.LENGTH_SHORT).show();
    return;
}
request.setMimeType(mimetype);
// set downloaded file destination to /sdcard/Download.
// or, should it be set to one of several Environment.DIRECTORY* dirs depending on mimetype?
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
// let this downloaded file be scanned by MediaScanner - so that it can 
// show up in Gallery app, for example.
request.allowScanningByMediaScanner();
request.setDescription(webAddress.getHost());
// XXX: Have to use the old url since the cookies were stored using the
// old percent-encoded url.
String cookies = CookieManager.getInstance().getCookie(url, privateBrowsing);
request.addRequestHeader("cookie", cookies);
request.addRequestHeader("User-Agent", userAgent);
request.addRequestHeader("Referer", referer);
request.setNotificationVisibility(
        DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
if (mimetype == null) {
    if (TextUtils.isEmpty(addressString)) {
        return;
    }
    // We must have long pressed on a link or image to download it. We
    // are not sure of the mimetype in this case, so do a head request
    new FetchUrlMimeType(activity, request, addressString, cookies,
            userAgent).start();
} else {
    final DownloadManager manager
            = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
    new Thread("Browser download") {
        public void run() {
            manager.enqueue(request);
        }
    }.start();
}
Toast.makeText(activity, R.string.download_pending, Toast.LENGTH_SHORT)
        .show();

添加了这个权限之后,同样的事情发生了,又崩溃了。 - Dritan Berna
我在清单文件中添加了WRITE_EXTERNAL_STORAGE权限,现在不会崩溃了,但只是显示了一秒钟的下载通知,然后就消失了。 - Dritan Berna
如果您能提供更多的logcat信息,可能会有所帮助。例如,在您发布的第一行之前提供几行(以便我们可以确切地知道问题所在):02-18 19:45:44.891: E/AndroidRuntime(357): at android.content.ContentProviderProxy.insert(ContentProviderNative.java:408) - Wei WANG
@CoronaPintu:这是来自 android.net.WebAddress。我刚刚更新了帖子以避免混淆。 - Wei WANG

0

试一下这个

mWebView.setDownloadListener(new DownloadListener() {       

@Override
public void onDownloadStart(String url, String userAgent,
                                String contentDisposition, String mimetype,
                                long contentLength) {
        DownloadManager.Request request = new DownloadManager.Request(
                Uri.parse(url));

        request.allowScanningByMediaScanner();
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); //Notify client once download is completed!
        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "Name of your downloadble file goes here, example: Mathematics II ");
        DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        dm.enqueue(request);
        Toast.makeText(getApplicationContext(), "Downloading File", //To notify the Client that the file is being downloaded
                Toast.LENGTH_LONG).show();

    }
});

还有,不要忘记将以下代码添加到manifest.xml文件中,这非常重要,它将授予保存到存储的权限,就像为weview授予Internet权限一样。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

它会自动开始下载文件吗?还是需要一些触发? - Nadeem Iqbal
@Ibrahim,你的解决方案帮我解决了下载文件的问题。现在我遇到了上传控件的问题,当我点击上传控件的浏览按钮时,文件打开对话框没有显示出来,请问你能否为这个问题提供一些解决方案呢?我的sdk版本是29。 - ITSagar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接