Moto G5+(Android7/Chrome63)ViewPager+WebView内存崩溃修复咨询
我在使用ViewPager搭配FragmentStatePagerAdapter加载包含<img>标签与富文本的HTML文本列表时,仅在搭载**Android 7系统、Chrome版本为63.0.3239.111的Moto G5+**设备上出现崩溃:滚动页面数秒后触发WebView内存不足崩溃,相关崩溃日志如下:
W/Adreno-GSL: <sharedmem_gpuobj_alloc:2021>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory
E/Adreno-GSL: <gsl_memory_alloc_pure:2125>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.
E/chromium: [ERROR:texture_manager.cc(3408)] [.RenderWorker-0x6df0a000]GL ERROR :GL_OUT_OF_MEMORY : glTexImage2D:
E/chromium: [ERROR:gles2_cmd_decoder.cc(5409)] Error: 5 for Command kTexImage2D
[ERROR:gles2_cmd_decoder.cc(9881)] [GroupMarkerNotSet(crbug.com/242999)!:50082280]RENDER WARNING: texture bound to texture unit 0 is not renderable. It maybe non-power-of-2 and have incompatible texture filtering.
A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x4 in tid 9665 (RenderThread)
现寻求该崩溃问题的修复方案。
这个问题在旧版WebView(Chrome 63)搭配Android 7的Adreno GPU设备上很常见,核心是WebView的内存管理和图片渲染逻辑在特定硬件/系统版本下存在缺陷。结合你的ViewPager+富文本场景,我整理了几个针对性的修复方案:
1. 强制WebView资源回收,避免内存泄漏
旧版WebView在Fragment中极易出现内存泄漏,尤其是配合ViewPager时,必须确保Fragment销毁时WebView被彻底清理:
- 在Fragment的
onDestroyView中手动销毁WebView,不要依赖系统自动回收:@Override public void onDestroyView() { if (webView != null) { // 先移除父布局引用 ViewParent parent = webView.getParent(); if (parent != null) { ((ViewGroup) parent).removeView(webView); } // 停止加载并清空所有资源 webView.stopLoading(); webView.clearHistory(); webView.clearCache(true); webView.removeAllViews(); // 最终销毁WebView实例 webView.destroy(); webView = null; } super.onDestroyView(); } - 针对该设备禁用WebView硬件加速:旧版Adreno GPU处理WebView硬件加速时存在内存管理bug,可单独对Moto G5+关闭硬件加速:
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N && "Moto G5+".equals(Build.MODEL)) { webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); }
2. 优化HTML图片加载,降低内存占用
日志中的GL内存错误几乎都是图片加载导致的,可通过以下方式压缩图片内存:
- 拦截图片请求并压缩尺寸:用
WebViewClient拦截图片URL,将图片压缩到屏幕宽度后再返回给WebView:webView.setWebViewClient(new WebViewClient() { @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { String url = request.getUrl().toString(); if (url.matches(".*\\.(png|jpg|jpeg)$")) { try { InputStream is = new URL(url).openStream(); Bitmap originalBitmap = BitmapFactory.decodeStream(is); int screenWidth = getResources().getDisplayMetrics().widthPixels; // 按屏幕宽度等比例缩放图片 float scale = (float) screenWidth / originalBitmap.getWidth(); Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, screenWidth, (int)(originalBitmap.getHeight()*scale), true); // 将压缩后的Bitmap转为InputStream返回 ByteArrayOutputStream baos = new ByteArrayOutputStream(); scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos); return new WebResourceResponse("image/jpeg", "UTF-8", new ByteArrayInputStream(baos.toByteArray())); } catch (IOException e) { e.printStackTrace(); } } return super.shouldInterceptRequest(view, request); } }); - 开启WebView图片自适应:让WebView自动缩放图片适配屏幕,避免加载原始大图:
WebSettings settings = webView.getSettings(); settings.setLoadWithOverviewMode(true); settings.setUseWideViewPort(true);
3. 调整ViewPager缓存策略,减少同时加载的WebView数量
FragmentStatePagerAdapter虽会销毁不可见Fragment,但可进一步优化缓存逻辑:
- 减少预加载页面数:默认ViewPager会预加载左右各1个页面,可设置为0(牺牲一点滚动流畅度换内存):
viewPager.setOffscreenPageLimit(0); - 页面切换时暂停/恢复WebView:不可见的页面暂停加载,释放内存:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { // 恢复当前页面WebView Fragment currentFrag = getSupportFragmentManager().findFragmentByTag("android:switcher:" + viewPager.getId() + ":" + position); if (currentFrag instanceof WebContentFragment) { ((WebContentFragment) currentFrag).resumeWebView(); } // 暂停其他页面WebView for (int i = 0; i < viewPager.getAdapter().getCount(); i++) { if (i != position) { Fragment frag = getSupportFragmentManager().findFragmentByTag("android:switcher:" + viewPager.getId() + ":" + i); if (frag instanceof WebContentFragment) { ((WebContentFragment) frag).pauseWebView(); } } } } @Override public void onPageScrolled(int position, float offset, int offsetPixels) {} @Override public void onPageScrollStateChanged(int state) {} }); // 在WebContentFragment中实现对应方法 public void pauseWebView() { if (webView != null) { webView.pauseTimers(); webView.stopLoading(); } } public void resumeWebView() { if (webView != null) { webView.resumeTimers(); } }
4. 旧版WebView专属优化
Chrome 63存在不少已知内存问题,可通过以下设置降低内存占用:
- 禁用不必要的JavaScript:如果你的富文本不需要JS交互,直接关闭:
settings.setJavaScriptEnabled(false); - 禁用WebView缓存:旧版WebView的缓存可能存在泄漏,直接关闭:
settings.setAppCacheEnabled(false); settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
内容的提问来源于stack exchange,提问作者Sheetal Suryan

