You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android WebView APK文件上传下载功能失效问题求助

WebView APK 文件上传/下载功能失效问题

我用Android Studio将个人网站转换为WebView APK应用,整体运行正常,但文件上传与下载功能完全无法使用。已在AndroidManifest中添加所有必要权限,问题仍未解决。


MainActivity.java 代码

package com.example.eprepareacademy2o;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
import android.webkit.DownloadListener;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import androidx.appcompat.app.AppCompatActivity;


public class MainActivity extends AppCompatActivity {

    String websiteURL = "xyz.com/"; // 设置网页地址
    private WebView webview;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if( ! CheckNetwork.isInternetAvailable(this)) // 检查网络是否可用
        {
            setContentView(R.layout.activity_main);
            new AlertDialog.Builder(this)
                    .setTitle("无网络连接")
                    .setMessage("请检查移动数据或WiFi网络")
                    .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            finish();
                        }
                    })
                    .show();
        }
        else
        {
            // WebView配置
            webview = findViewById(R.id.webView);
            webview.getSettings().setJavaScriptEnabled(true);
            webview.getSettings().setAllowFileAccess(true);
            webview.getSettings().setAllowFileAccessFromFileURLs(true);
            webview.getSettings().setAllowUniversalAccessFromFileURLs(true);
            webview.setWebChromeClient(new WebChromeClient() {
                public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
                    // 未处理文件上传
                    return true;
                }
            });

            webview.setDownloadListener(new DownloadListener() {
                public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
                    // 未处理文件下载
                }
            });

            webview.setWebViewClient(new WebViewClient() {
                public boolean shouldOverrideUrlLoading(WebView webView, String url) {
                    if (url.endsWith(".pdf")) {
                        // 未处理PDF下载
                        return true;
                    }
                    return false;
                }
            });

            webview.loadUrl("xyz.com/");
            webview.getSettings().setDomStorageEnabled(true);
            webview.setOverScrollMode(WebView.OVER_SCROLL_NEVER);
            webview.loadUrl(websiteURL);
            webview.setWebViewClient(new WebViewClientDemo());
            webview.setWebChromeClient(new WebChromeClient() {
                @Override
                public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
                    // 重复设置,未处理文件上传
                    return true;
                }
            });
        }
    }

    private class WebViewClientDemo extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if (url.startsWith("http:") || url.startsWith("https:")) {
                view.loadUrl(url);
                return true;
            } else {
                view.loadUrl(websiteURL + url);
                return true;
            }
        }
    }

    @Override
    public void onBackPressed() {
        if (webview.isFocused() && webview.canGoBack()) {
            webview.goBack();
        } else {
            new AlertDialog.Builder(this)
                    .setTitle("退出应用")
                    .setMessage("确定要关闭应用吗?")
                    .setPositiveButton("是", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            finish();
                        }
                    })
                    .setNegativeButton("否", null)
                    .show();
        }
    }
}

class CheckNetwork {
    private static final String TAG = CheckNetwork.class.getSimpleName();

    public static boolean isInternetAvailable(Context context)
    {
        NetworkInfo info = (NetworkInfo) ((ConnectivityManager)
                context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();

        if (info == null)
        {
            Log.d(TAG,"无网络连接");
            return false;
        }
        else
        {
            if(info.isConnected())
            {
                Log.d(TAG,"网络连接正常");
                return true;
            }
            else
            {
                Log.d(TAG,"网络连接异常");
                return true; // 此处逻辑错误,未连接应返回false
            }
        }
    }

    // 未被使用的冗余内部类
    private class WebViewClientDemo extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            view.loadUrl("javascript:(function() { " +
                    "var videos = document.getElementsByTagName('video'); " +
                    "for(var i=0;i<videos.length;i++) { " +
                    "videos[i].setAttribute('webkit-playsinline', ''); " +
                    "videos[i].setAttribute('playsinline', ''); " +
                    "videos[i].setAttribute('controls', ''); " +
                    "videos[i].setAttribute('style', 'max-width:100%; height:auto;'); " +
                    "}" +
                    "var iframes = document.getElementsByTagName('iframe');" +
                    "for(var i=0;i<iframes.length;i++){" +
                    "if(iframes[i].src.includes('youtube') || iframes[i].src.includes('vimeo')) {" +
                    "iframes[i].setAttribute('frameborder', '0');" +
                    "iframes[i].setAttribute('allowfullscreen', '');" +
                    "iframes[i].setAttribute('webkitallowfullscreen', '');" +
                    "iframes[i].setAttribute('mozallowfullscreen', '');" +
                    "iframes[i].setAttribute('style', 'width:100%; height:100%;');" +
                    "}" +
                    "}" +
                    ")()");
        }
    }
}

AndroidManifest.xml 权限配置

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET"/> <!-- 重复权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

<application
    android:usesCleartextTraffic="true"
    android:allowBackup="true"
    android:dataExtractionRules="@xml/data_extraction_rules"
    android:fullBackupContent="@xml/backup_rules"
    android:icon="@drawable/ic_launcher_foreground"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/Theme.EprepareAcademy2O"
    tools:targetApi="31">
    <activity
        android:name=".MainActivity"
        android:exported="false" />
    <activity
        android:name=".SplashActivity"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

问题修复方案

1. 文件上传功能修复

原代码onShowFileChooser为空实现,需实现文件选择Intent跳转并处理结果:

  • 添加全局变量:
    private ValueCallback<Uri[]> mFilePathCallback;
    private static final int FILE_CHOOSER_REQUEST_CODE = 1000;
    
  • 重写onShowFileChooser:
    webview.setWebChromeClient(new WebChromeClient() {
        @Override
        public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
            if (mFilePathCallback != null) {
                mFilePathCallback.onReceiveValue(null);
                mFilePathCallback = null;
            }
            mFilePathCallback = filePathCallback;
    
            Intent intent = fileChooserParams.createIntent();
            try {
                startActivityForResult(intent, FILE_CHOOSER_REQUEST_CODE);
            } catch (ActivityNotFoundException e) {
                mFilePathCallback = null;
                Toast.makeText(MainActivity.this, "无法打开文件选择器", Toast.LENGTH_SHORT).show();
                return false;
            }
            return true;
        }
    });
    
  • 处理Activity结果:
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == FILE_CHOOSER_REQUEST_CODE) {
            Uri[] results = null;
            if (resultCode == RESULT_OK && data != null) {
                String[] filePaths = WebChromeClient.FileChooserParams.parseResult(resultCode, data);
                if (filePaths != null && filePaths.length > 0) {
                    results = new Uri[filePaths.length];
                    for (int i = 0; i < filePaths.length; i++) {
                        results[i] = Uri.parse(filePaths[i]);
                    }
                } else {
                    results = new Uri[]{data.getData()};
                }
            }
            if (mFilePathCallback != null) {
                mFilePathCallback.onReceiveValue(results);
                mFilePathCallback = null;
            }
        }
    }
    

2. 文件下载功能修复

实现DownloadListener调用系统下载管理器:

webview.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.setMimeType(mimeType);
        String filename = URLUtil.guessFileName(url, contentDisposition, mimeType);
        request.setTitle(filename);
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);

        DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        if (downloadManager != null) {
            downloadManager.enqueue(request);
        }
        Toast.makeText(MainActivity.this, "开始下载", Toast.LENGTH_SHORT).show();
    }
});

3. 权限问题修复

  • 移除Manifest中重复的INTERNET权限
  • Android 10+添加Scoped Storage适配或MANAGE_EXTERNAL_STORAGE权限
  • 动态申请危险权限:
    private static final String[] PERMISSIONS = {
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.CAMERA
    };
    private static final int PERMISSION_REQUEST_CODE = 2000;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
                    || checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
                    || checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(PERMISSIONS, PERMISSION_REQUEST_CODE);
            }
        }
        // 后续逻辑...
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSION_REQUEST_CODE) {
            boolean allGranted = true;
            for (int result : grantResults) {
                if (result != PackageManager.PERMISSION_GRANTED) {
                    allGranted = false;
                    break;
                }
            }
            if (!allGranted) {
                Toast.makeText(this, "需授予权限才能使用文件功能", Toast.LENGTH_SHORT).show();
            }
        }
    }
    

4. 代码冗余修复

  • 移除重复设置的WebChromeClient和WebViewClient
  • 删除CheckNetwork中未使用的WebViewClientDemo内部类
  • 修复CheckNetwork.isInternetAvailable逻辑错误,未连接时返回false

内容的提问来源于stack exchange,提问作者Cyber fahad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 09:52:02