android类webview,android内存优化之webview

提起android端的webview,它既是天使,又是魔鬼。

在混合型app中它是主角,一切由它呈现,如58同城,赶集网等;在另一些超级app中亦有它的影子,微信,qq,支付宝,没有一个超级app能少了它,既能展示最新最潮的实时资讯,又能扮演盘踞一方的全功能型网站,与native结合后又能扮演诸如公众号之内的应用等等,其能力可想而知。

webview在android端的演化可谓曲折,2015年google宣布不在支持4.4版本一下的webview

目前大致的webview内存处理方式分为两类:

1.独立的web进程,与主进程隔开

这个方法被运用于类似qq,微信这样的超级app中,这也是解决任何webview内存问题屡试不爽的方法

对于封装的webactivity,在manifest.xml中设置

然后在关闭webactivity时销毁进程

@Overrideprotected void onDestroy() {

super.onDestroy();

System.exit(0);

}

关闭浏览器后便销毁整个进程,这样一般95%的情况下不会造成内存泄漏之类的问题,但这就涉及到android进程间通讯,比较不方便处理, 优劣参半,也是可选的一个方案.

2.封装过的webview

相比系统内置的webview的支持自2005年之后就没有了,而首推google的chrome。 腾讯的x5webview对h5的兼容性与稳定性与安全性逐渐凸显出来,并自成一系, 下面以使用x5webview为例做说明:

首先使用webview的时候,不在xml里面声明,而是直接代码new个对象,传入application context防止activity引用滥用.

webView = new BridgeWebView(getContext().getApplicationContext());

webFrameLayout.addView(webView, 0);

在使用了这个方式后,基本上90%的webview内存泄漏的问题便得以解决.

而在android4.4版本以下,会出现android webview无法自动释放,如在fragment中,使用ondetach的释放webview是比较好的时机

public void onDetach() {

releaseWebViews();

super.onDetach();

}

public synchronized void releaseWebViews() {

if(webView != null) {

try {

if(webView.getParent() != null) {

((ViewGroup) webView.getParent()).removeView(webView);

}

// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {

//this is causing the segfault occasionally below 4.2

webView.destroy();

// }

}catch (IllegalArgumentException e) {

DLog.p(e);

}

RefWatcher refWatcher = FApplication.getRefWatcher();

refWatcher.watch(webView);

webView = null;

}

}

其中webview自身的销毁代码如下:

@Override

public void destroy()

//flushMessageQueue();

clearCache(true);

clearFormData();

clearMatches();

clearSslPreferences();

clearDisappearingChildren();

clearHistory();

//@Deprecated

//clearView();

clearAnimation();

loadUrl("about:blank");

removeAllViews();

freeMemory();

super.destroy();

}

如果以上的方案还不管用,实时加入反射来清理webview的引用:

public void setConfigCallback(WindowManager windowManager) {

try {

Field field = WebView.class.getDeclaredField("mWebViewCore");

field = field.getType().getDeclaredField("mBrowserFrame");

field = field.getType().getDeclaredField("sConfigCallback");

field.setAccessible(true);

Object configCallback = field.get(null);

if (null == configCallback) {

return;

}

field = field.getType().getDeclaredField("mWindowManager");

field.setAccessible(true);

field.set(configCallback, windowManager);

} catch(Exception e) {

}

}

然后在activity中加入

public void onCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setConfigCallback((WindowManager);

getApplicationContext().getSystemService(Context.WINDOW_SERVICE));

}

publicvoidonDestroy()

{

setConfigCallback(null);

super.onDestroy();

}

对于application 级别的可能的misbehaving callbacks,加入

private static String[] misbehavingClasses = new String[]{

"com.google.android.gms.ads",

"com.android.org.chromium.android_webview.AwContents$AwComponentCallbacks",

};

public static boolean isMisbehavingCallBacks(String name){

for(String s : misbehavingClasses){

if(name.startsWith(s)){

return true;

}

}

return false;

}

然后重写applicaiton类记录这些callback, 并在适当的时机删掉:

@Override

public void registerComponentCallbacks(ComponentCallbacks callback) {

super.registerComponentCallbacks(callback);

ComponentCallbacksBehavioralAdjustmentToolIcs.INSTANCE.onComponentCallbacksRegistered(callback);

}

@Override

public void unregisterComponentCallbacks(ComponentCallbacks callback) {

ComponentCallbacksBehavioralAdjustmentToolIcs.INSTANCE.onComponentCallbacksUnregistered(callback);

super.unregisterComponentCallbacks(callback);

}

public void forceUnregisterComponentCallbacks() {

ComponentCallbacksBehavioralAdjustmentToolIcs.INSTANCE.unregisterAll(this);

}

private static class ComponentCallbacksBehavioralAdjustmentToolIcs {

private static final String TAG = "componentCallbacks";

static ComponentCallbacksBehavioralAdjustmentToolIcs INSTANCE = new ComponentCallbacksBehavioralAdjustmentToolIcs();

private WeakHashMap mCallbacks = new WeakHashMap<>();

private boolean mSuspended = false;

public void onComponentCallbacksRegistered(ComponentCallbacks callback) {

Throwable thr = new Throwable("Callback registered here.");

ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(thr);

if (FApplication.DEBUG) DLog.w(TAG, "registerComponentCallbacks: " + callback.getClass().getName(), thr);

if (!mSuspended) {

if (BugFix.isMisbehavingCallBacks(callback.getClass().getName())) {

mCallbacks.put(callback, ci);

}

// TODO: other classes may still prove to be problematic? For now, only watch for .gms.ads, since we know those are misbehaving

} else {

if (FApplication.DEBUG) DLog.e(TAG, "ComponentCallbacks was registered while tracking is suspended!");

}

}

public void onComponentCallbacksUnregistered(ComponentCallbacks callback) {

if (!mSuspended) {

if (FApplication.DEBUG) {

DLog.i(TAG, "unregisterComponentCallbacks: " + callback, new Throwable());

}

mCallbacks.remove(callback);

}

}

public void unregisterAll(Context context) {

mSuspended = true;

for (Map.Entry entry : mCallbacks.entrySet()) {

ComponentCallbacks callback = entry.getKey();

if (callback == null) continue;

if (FApplication.DEBUG) {

DLog.w(TAG, "Forcibly unregistering a misbehaving ComponentCallbacks: " + entry.getKey());

DLog.w(TAG, entry.getValue().stackTrace);

}

try {

context.unregisterComponentCallbacks(entry.getKey());

} catch (Exception exc) {

if (FApplication.DEBUG) DLog.e(TAG, "Unable to unregister ComponentCallbacks", exc);

}

}

mCallbacks.clear();

mSuspended = false;

}

}

为方便webactivity的debug

在application的oncreate里面,我们加入

private static void enableStrictMode() {

StrictMode.ThreadPolicy.Builder threadPolicyBuilder =

new StrictMode.ThreadPolicy.Builder()

.detectAll()

.penaltyLog();

StrictMode.VmPolicy.Builder vmPolicyBuilder =

new StrictMode.VmPolicy.Builder()

.detectAll()

.penaltyLog();

threadPolicyBuilder.penaltyFlashScreen();

vmPolicyBuilder.setClassInstanceLimit(WebActivity.class, 1);

StrictMode.setThreadPolicy(threadPolicyBuilder.build());

StrictMode.setVmPolicy(vmPolicyBuilder.build());

}

在webview中,对于android4.4以上的版本,我们开启调试模式

if (FApplication.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

WebView.setWebContentsDebuggingEnabled(true);

}

最后不可缺少的是, leakcanary的加入来跟踪这些消耗内存的组件:

在webfragment,webactivity,webview ondestory后加上

RefWatcher refWatcher = FApplication.getRefWatcher();

refWatcher.watch(obj);

总结: 如果你只是简单地用 webview 做呈现, 使用application context启动webview已经足够了,但如果你需要webview来播放视频,处理弹窗等复杂工作, 新建一个进程来处理会更可靠.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值