Android部分设备打开URL报ActivityNotFoundException异常如何解决
根因分析
- Android 11及以上版本引入了包可见性限制,如果应用没有在
AndroidManifest.xml中声明查询浏览器应用的权限,resolveActivity接口会返回null,无法正确获取可用的浏览器包名,进而导致跳转失败。 - 现有Custom Tabs代码存在逻辑缺陷:使用仅携带
http://的Intent查询浏览器,部分设备上默认浏览器可能仅注册了https scheme的响应,会导致查询结果不准确,强制设置错误的包名后触发异常。 - 部分设备确实存在用户主动禁用所有系统/第三方浏览器、或是企业MDM策略限制浏览器启动的场景,此时外部跳转逻辑必然无法找到对应的处理Activity。
修复方案
该方案完全兼容Android 5.0及以上系统版本,按照优先级做多层降级可覆盖绝大多数场景。
1. 适配包可见性要求
在AndroidManifest.xml中加入如下queries配置,适配Android 11+的包可见性规则,低版本系统会自动忽略该配置不影响运行:
<queries> <intent> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="https" /> </intent> <intent> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="http" /> </intent> </queries>
添加后resolveActivity才能在高版本系统上正确查询到可用的浏览器应用。
2. 优化跳转逻辑,增加多级降级
按照「Custom Tabs跳转 -> 原生Intent跳转 -> 应用内置WebView打开」的优先级做降级处理:
优化后的跳转示例代码:
public void openUrl(Uri uri) { // 先尝试Custom Tabs跳转 try { CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); builder.setShowTitle(true); CustomTabsIntent customTabsIntent = builder.build(); // 直接查询能处理目标uri的应用,不要用固定http://查询 ResolveInfo resolveInfo = getPackageManager().resolveActivity(customTabsIntent.intent, PackageManager.MATCH_DEFAULT_ONLY); if (resolveInfo != null && !TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) { customTabsIntent.intent.setPackage(resolveInfo.activityInfo.packageName); customTabsIntent.launchUrl(this, uri); return; } } catch (ActivityNotFoundException e) { // 捕获异常,走下一级降级逻辑 } // 尝试原生Intent跳转 try { Intent browserIntent = new Intent(Intent.ACTION_VIEW, uri); if (browserIntent.resolveActivity(getPackageManager()) != null) { startActivity(browserIntent); return; } } catch (ActivityNotFoundException e) { // 捕获异常,走下一级降级逻辑 } // 终极兜底:跳转内置WebView页面加载链接 Intent webViewIntent = new Intent(this, MyInnerWebViewActivity.class); webViewIntent.putExtra("target_url", uri.toString()); startActivity(webViewIntent); }
3. 实现内置WebView兜底页
新建一个轻量的MyInnerWebViewActivity作为终极兜底方案,哪怕用户禁用了所有外部浏览器也能在应用内打开链接:
public class MyInnerWebViewActivity extends AppCompatActivity { private WebView webView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_inner_webview); webView = findViewById(R.id.webview); webView.getSettings().setJavaScriptEnabled(true); webView.setWebViewClient(new WebViewClient()); String targetUrl = getIntent().getStringExtra("target_url"); if (!TextUtils.isEmpty(targetUrl)) { webView.loadUrl(targetUrl); } } @Override public void onBackPressed() { if (webView.canGoBack()) { webView.goBack(); return; } super.onBackPressed(); } }
注:仅在企业MDM策略同时限制了应用内WebView访问的极端场景下该方案会失效,此时可增加弹窗提示用户当前设备无法打开链接即可。
内容的提问来源于stack exchange,提问作者Aykut Uludağ
相关产品推荐
相关产品推荐

