Webview.loadDataWithBaseURL

本文介绍了解决Android 4.4浏览器兼容性问题的方法。通过使用AssetsUtil类从assets文件夹读取HTML文件,并根据不同API版本加载不同的CSS样式,确保了不同版本浏览器下页面显示的一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题原因:由于4.4浏览器不兼容,因为这里列出了二个样式来适应不同的浏览器版本

第一种加载:mWebView.loadUrl("file:///android_asset/chart_top01.html");
这种加载方式,会出现问题,那么采用第二种加载:
说明:这种方式是字符串的加载形式,直接给文件的地址会出现错误,因此需要把上面本地文件读出来,然后采用下面的加载方式:

webView.loadDataWithBaseURL("about:blank",bodyBuffer.toString(),"text/html", "utf-8",null);
读取assets文件的方法:这里可以读取HTML文件:
/**
 * 资产文件帮助类
 */
public class AssetsUtil {

	// 从assets 文件夹中获取文件并读取数据
	public static String getFromAssets(Context context, String fileName) {
		String result = "";
		try {
			InputStream in = context.getResources().getAssets().open(fileName);
			// 获取文件的字节数
			int lenght = in.available();
			// 创建byte数组
			byte[] buffer = new byte[lenght];
			// 将文件中的数据读到byte数组中
			in.read(buffer);
			result = EncodingUtils.getString(buffer, "UTF-8");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

}

将上面读取的字符串二次加载:
这样能完成网页的二次添加样式,从而去掉4.4的BUG
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mWebView = (WebView) findViewById(R.id.webview);

		WebViewUtils.setting(mWebView);

		// mWebView.loadUrl("file:///android_asset/chart_top01.html");
		// mWebView.loadDataWithBaseURL("about:blank",
		// "APK'>file:///android_asset/chart_top01.html", "text/html",
		// "utf-8",null);
		// String data =
		// "<HTML>在模拟器 2.1 上测试,这是<IMG src=\"APK'>file:///android_asset/igg.jpg\"/>APK里的图片";

		// webView.loadDataWithBaseURL("about:blank",bodyBuffer.toString(),
		// "text/html", "utf-8",null);
		String data = getFromAssets("chart_top01.html");

		int currentapiVersion = android.os.Build.VERSION.SDK_INT;
		Log.i("fuck", currentapiVersion + "");
		if (currentapiVersion >= 19) {
			data += "<link rel=\"stylesheet\" type=\"text/css\" href=\"product_survey.css\" />";
			mWebView.loadDataWithBaseURL("file:///android_asset/product_survey.css", data, "text/html", "utf-8", null);
		} else {
			data += "<link rel=\"stylesheet\" type=\"text/css\" href=\"product_survey2.css\" />";
			mWebView.loadDataWithBaseURL("file:///android_asset/product_survey2.css", data, "text/html", "utf-8", null);
		}

	}

<think>我们之前讨论的是在系统源码层面修改WebView的行为,使得第三方应用内部WebView打开的URL跳转到浏览器打开。用户现在遇到的问题是:修改了WebView.java和WebViewClient.java中的代码,但是没有生效,即没有触发我们添加的逻辑。 可能的原因及解决方案: 1. **修改的源码位置不正确**: - 在Android系统中,WebView的实现已经从Android 4.4开始使用基于Chromium的WebView。因此,我们修改的`frameworks/base`下的`WebView.java`和`WebViewClient.java`可能只适用于AOSP的WebView,而有些设备使用的是其他WebView实现(如Chrome WebView,即com.google.android.webview)。因此,我们需要确保修改的是当前系统使用的WebView实现。 2. **系统实际使用的WebViewProvider不同**: - 从Android 5.0开始,WebView可以通过WebViewProvider使用不同的实现(如AOSP WebView,Chrome Stable, Chrome Beta等)。因此,我们需要修改的可能是Chromium的WebView实现,而不是AOSP的WebView。 3. **编译和部署问题**: - 修改了系统源码后,是否重新编译了正确的模块?是否将修改后的系统镜像刷入设备?有时候可能只编译了部分模块,或者刷机不成功。 4. **修改的代码逻辑没有被触发**: - 我们之前修改的是`WebView.loadUrl`方法,但是第三方应用可能会使用其他方法加载URL,比如`loadUrl`的重载方法(带有额外参数的)或者`loadData`、`loadDataWithBaseURL`等。因此,我们需要确保在多个入口都进行了拦截。 5. **系统版本差异**: - 不同Android版本的WebView实现有差异,需要针对具体版本修改。 6. **WebViewClient的覆盖**: - 第三方应用可以设置自己的WebViewClient,并重写`shouldOverrideUrlLoading`方法。如果应用已经重写了这个方法,并且返回了false,那么我们的系统级修改(在WebViewClient.java中)可能不会被调用。因此,我们的修改需要更底层。 7. **权限问题**: - 启动浏览器需要相应的权限,检查是否在系统应用中声明了权限,或者是否在系统级代码中正确处理了启动Activity的权限。 8. **白名单机制**: - 我们之前添加了白名单,可能因为白名单配置问题导致我们的拦截逻辑被跳过。 针对以上可能的原因,我们可以采取以下措施: ### 解决方案一:确保修改的是正确的WebView实现 - 确定当前系统使用的WebView实现。可以通过以下命令查看: ```bash adb shell dumpsys webviewupdate ``` 查看当前WebView的实现包名。 - 如果系统使用的是Chrome WebView(com.google.android.webview),那么我们需要修改Chromium的源码,而不是AOSP的WebView。 ### 解决方案二:在多个加载入口添加拦截 - 除了`loadUrl(String url)`,还需要考虑以下方法: - `loadUrl(String url, Map<String, String> additionalHttpHeaders)` - `loadData(String data, String mimeType, String encoding)` - `loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)` - `postUrl(String url, byte[] postData)` 在WebView.java中,我们需要在这些方法中都加入拦截逻辑。 ### 解决方案三:在更底层的网络请求处拦截 - 如果应用使用了自定义WebViewClient,那么我们在WebViewClient.java中的修改可能被覆盖。因此,我们可以考虑在WebView加载URL的底层网络请求处拦截,例如在`WebViewClient`的`shouldInterceptRequest`方法中,但这里通常用于资源拦截,不太适合做跳转。 - 更合适的做法是在`WebView.loadUrl`的各个重载方法以及`loadDataWithBaseURL`等入口处进行拦截,因为这些是加载URL的起点。 ### 解决方案四:检查权限和Intent启动 - 在启动浏览器时,确保系统有足够的权限。系统应用通常具有较高的权限,但也要注意在启动Activity时添加`Intent.FLAG_ACTIVITY_NEW_TASK`标志。 ### 解决方案五:完善白名单机制 - 确保白名单配置正确,特别是系统浏览器和系统关键组件的包名。 ### 修改示例(在WebView.java中多个入口添加拦截): 在`WebView.java`中,我们可以在多个加载URL的方法里调用我们的拦截逻辑。 ```java public void loadUrl(String url) { if (tryInterceptUrl(url)) { return; } // 原始逻辑 } public void loadUrl(String url, Map<String, String> additionalHttpHeaders) { if (tryInterceptUrl(url)) { return; } // 原始逻辑 } public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl) { // 注意:这个方法的baseUrl可能是网页的基础URL,而data是HTML内容,我们无法直接拦截 // 但是,如果baseUrl是一个http/https的URL,我们可以考虑拦截 if (baseUrl != null && (baseUrl.startsWith("http://") || baseUrl.startsWith("https://"))) { if (tryInterceptUrl(baseUrl)) { return; } } // 原始逻辑 } // 其他加载方法类似 private boolean tryInterceptUrl(String url) { if (shouldForceOpenInBrowser(url)) { openUrlInSystemBrowser(url); return true; } return false; } ``` 注意:对于`loadDataWithBaseURL`,我们只能拦截baseUrl,而无法拦截HTML内容中可能包含的链接。对于HTML内容中的链接,我们可以在页面加载后通过JavaScript进行拦截,但这需要修改WebViewClient的行为,而且应用可能会覆盖WebViewClient。因此,我们更倾向于在链接被点击时(即`shouldOverrideUrlLoading`)拦截,但这里又面临应用覆盖的问题。 ### 解决方案六:修改WebViewClient的shouldOverrideUrlLoading默认行为 我们之前修改了`WebViewClient.java`中的`shouldOverrideUrlLoading`方法,但是应用可以设置自己的WebViewClient,覆盖这个方法。因此,我们需要在系统级强制所有WebViewClient的`shouldOverrideUrlLoading`方法都先经过我们的逻辑。 我们可以修改`WebView.setWebViewClient`方法,将传入的WebViewClient包装成我们自己的WebViewClient: ```java public void setWebViewClient(WebViewClient client) { // 使用我们包装的WebViewClient super.setWebViewClient(new SystemWebViewClientWrapper(client)); } private static class SystemWebViewClientWrapper extends WebViewClient { private WebViewClient mOriginalClient; SystemWebViewClientWrapper(WebViewClient original) { mOriginalClient = original; } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { // 先执行系统级的拦截 if (view.getContext().getApplicationInfo().uid > Process.FIRST_APPLICATION_UID) { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); view.getContext().startActivity(intent); return true; } // 再执行应用设置的WebViewClient的逻辑 if (mOriginalClient != null) { return mOriginalClient.shouldOverrideUrlLoading(view, url); } return false; } } ``` 这样,无论应用设置什么样的WebViewClient,我们的拦截逻辑都会先执行。 但是,这种方法可能会破坏应用原有的行为,因为我们在应用自己的WebViewClient之前就拦截了URL,并且直接启动了浏览器,没有给应用处理的机会。因此,我们需要权衡是否所有第三方应用都需要强制跳转。 ### 最终建议 由于修改系统源码影响较大,建议采用分步骤的方式: 1. 确保修改的是当前系统使用的WebView实现(AOSP WebView还是Chrome WebView)。 2.WebView的多个加载入口添加拦截。 3. 通过包装WebViewClient来确保系统级拦截先于应用级拦截。 4. 完善白名单,避免系统应用被拦截。 如果问题仍然存在,需要检查系统日志(logcat)来查看拦截逻辑是否执行,以及是否有异常抛出。 由于修改系统源码的复杂性,建议在修改后通过以下命令重新编译WebView模块(以AOSP WebView为例): ```bash mmm frameworks/base ``` 然后重新打包system.img并刷机。 注意:不同Android版本的具体实现可能不同,以上代码示例可能需要根据具体版本调整。 希望这些解决方案能帮助您解决问题。</think>### 安卓系统源码修改WebView跳转未生效问题分析及解决方案 在系统源码修改后WebView跳转浏览器未生效,且未触发相关逻辑,主要存在以下技术原因和解决方案: --- ### 一、核心问题分析 1. **WebViewClient覆盖机制** 第三方应用可自定义`WebViewClient`覆盖系统默认行为[^1]: ```java // 应用层可覆盖系统级修改 webView.setWebViewClient(new CustomWebViewClient()); ``` 此时系统源码中的`shouldOverrideUrlLoading`不会被触发 2. **Chromium内核隔离** Android 4.4+使用独立Chromium内核,修改AOSP框架层代码对实际渲染引擎无效[^3] 3. **多入口加载问题** URL加载可能通过以下未被拦截的入口: ```java webView.loadUrl(url, headers) // 带额外参数的加载 webView.loadDataWithBaseURL() // 基础URL加载 webView.evaluateJavascript() // JS触发的导航 ``` 4. **进程边界限制** 系统级修改需在WebView提供者进程生效,而非应用进程: $$ \text{应用进程} \xrightarrow{\text{Binder IPC}} \text{WebView提供者进程} $$ --- ### 二、系统级解决方案 #### 方案1:强制覆盖WebViewClient(推荐) 在`frameworks/base/core/java/android/webkit/WebView.java`: ```java // 修改setWebViewClient方法 public void setWebViewClient(WebViewClient client) { // 强制注入系统级拦截器 super.setWebViewClient(new SystemWebViewClient(client)); } private class SystemWebViewClient extends WebViewClient { private final WebViewClient mAppClient; SystemWebViewClient(WebViewClient appClient) { mAppClient = appClient; } @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { // 系统级拦截逻辑 if(isThirdPartyApp() && isHttpUrl(request.getUrl())) { launchExternalBrowser(view.getContext(), request.getUrl()); return true; } // 应用自定义逻辑 return mAppClient != null && mAppClient.shouldOverrideUrlLoading(view, request); } } ``` **优势**:确保系统拦截器始终在最外层执行 #### 方案2:Chromium内核层修改 文件路径:`src/chrome/android/java/src/org/chromium/android_webview/AwContents.java` ```java // 修改导航处理核心 private void handleNavigation(String url, boolean isRedirect) { if (shouldForceExternal(url)) { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); return; // 中断WebView内部加载 } // 原始导航逻辑... } ``` **编译要求**: ```bash # 需单独编译WebView模块 m module_webview ``` #### 方案3:Binder代理拦截 在`frameworks/base/core/java/android/webkit/IWebViewProviderService.aidl`: ```java interface IWebViewProviderService { // 添加拦截方法 boolean shouldOverrideUrl(in String url); } // 实现类中 public boolean shouldOverrideUrl(String url) { return SystemUrlInterceptor.shouldBlock(url); } ``` --- ### 三、关键验证点 1. **进程检查** 通过`adb shell dumpsys webviewupdate`确认修改的WebView提供者正在运行 2. **内核版本匹配** 使用`WebView.getCurrentWebViewPackage()`验证修改的内核版本是否生效 3. **多入口测试用例**: ```java // 需覆盖的测试场景 webView.loadUrl("https://example.com"); webView.loadUrl("https://example.com", customHeaders); webView.loadDataWithBaseURL("https://base.com", htmlData); ``` --- ### 四、调试建议 在系统代码中添加日志: ```java // 在WebViewCore中 Log.d("WEBVIEW_DEBUG", "Loading URL: " + url + " | Caller UID:" + Binder.getCallingUid() + " | Stack:" + Log.getStackTraceString(new Throwable())); ``` 通过`adb logcat -s WEBVIEW_DEBUG`观察加载路径 --- ### 五、兼容性处理 在`frameworks/base/core/res/res/values/config.xml`添加: ```xml <!-- 启用WebView重定向功能 --> <bool name="config_webview_force_redirect">true</bool> <!-- 排除应用白名单 --> <string-array name="config_webview_redirect_exclusions"> <item>com.android.chrome</item> <item>com.tencent.mm</item> <!-- 微信等特殊应用 --> </string-array> ``` --- ### 六、技术原理图 ```mermaid graph TD A[第三方应用] -->|loadUrl| B(系统WebView API) B --> C{是否自定义WebViewClient?} C -->|是| D[应用级shouldOverrideUrlLoading] C -->|否| E[系统默认实现] D -->|返回false| F[Chromium内核加载] E --> G[系统级拦截器] G -->|拦截| H[启动外部浏览器] G -->|放行| F F --> I[实际渲染进程] ``` --- ### 相关问题 1. 如何在不修改系统源码的情况下实现第三方应用WebView跳转控制? 2. Android Chromium内核与系统WebView API的通信机制是怎样的? 3. 系统级WebView修改如何通过CTS兼容性测试? 4. WebView的Binder IPC通信模型存在哪些安全边界? 5. 如何为不同Android版本实现差异化的WebView行为控制?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值