android webview 内存泄漏

猴屁股
  • 92

每次启动这个activity memory allocated 逐渐增加,且不会被回收
求解决方案,下面是具体实现

public class NetWork {

    private static AuthApi authApi;
    private static UserApi userApi;

    private static Converter.Factory gsonConverterFactory= GsonConverterFactory.create();
    private static CallAdapter.Factory rxJavaCallAdapterFactory= RxJavaCallAdapterFactory.create();

    public static AuthApi getAuthApi(){
        Log.d("NetWork", "authApi==null:" + (authApi == null));
        if(authApi == null){
            Retrofit retrofit=new Retrofit.Builder()
                    .baseUrl(UrlConfig.ACCESS_TOKEN)
                    .addCallAdapterFactory(rxJavaCallAdapterFactory)
                    .addConverterFactory(gsonConverterFactory)
                    .build();
            authApi=retrofit.create(AuthApi.class);

        }
        return authApi;
    }

    public static UserApi getUserApi(){
        Log.d("NetWork", "userApi==null:" + (userApi == null));
        if(userApi == null){
            Retrofit retrofit=new Retrofit.Builder()
                    .baseUrl(UrlConfig.BASE_URL)
                    .addCallAdapterFactory(rxJavaCallAdapterFactory)
                    .addConverterFactory(gsonConverterFactory)
                    .build();
            userApi=retrofit.create(UserApi.class);

        }
        return userApi;
    }
}
public class OAuthLoginActivity extends AppCompatActivity {
    private WebViewProgress mWebView;

    Subscription mSubscription;
    Subscription mProgressSubscription;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_oauth);
        initView();
        /*
        * 1.getCode client_id scope
        * 2.getToken client_id client_secret code
        * */

        mWebView.loadUrl(UrlConfig.LOGIN_URL);
        Log.d("webViewURL",mWebView.getUrl());

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mWebView.removeAllViews();        
        mWebView.destroy();
        if(mSubscription!=null){
            mSubscription.unsubscribe();
        }
        if(mProgressSubscription!=null){
            mProgressSubscription.unsubscribe();
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()){
            case android.R.id.home:
                finish();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    class MyWebViewClient extends WebViewClient{
        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            Log.d("MyWebViewClient", url);
            if(url.contains("?code=")){
                Uri uri=Uri.parse(url);
                String code=uri.getQueryParameter("code");
                getUser(code);
            }
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            mProgressSubscription=Observable.timer(1, TimeUnit.SECONDS)
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Action1<Long>() {
                @Override
                public void call(Long aLong) {
                    mWebView.mProgressBar.setVisibility(View.GONE);
                }
            });

        }

    }

    private void initView(){

        Toolbar toolbar=(Toolbar) findViewById(R.id.toolbar);
        mWebView=(WebViewProgress) findViewById(R.id.web_view);
        toolbar.setTitle("授权登录");
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        WebSettings webSettings=mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setSupportZoom(true);
        webSettings.setBuiltInZoomControls(true);
        webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
        mWebView.requestFocusFromTouch();
        mWebView.setWebViewClient(new MyWebViewClient());

    }

    private void getUser(String code){

        mSubscription=NetWork.getAuthApi().getAccessToken(UrlConfig.CLIENT_ID,UrlConfig.CLIENT_SECRET,code)
                .flatMap(new Func1<AccessToken, Observable<User>>() {
                    @Override
                    public Observable<User> call(AccessToken accessToken) {
                        return NetWork.getUserApi().getUser(accessToken.getAccess_token());
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<User>() {
                    @Override
                    public void onCompleted() {
                        Log.d("OAuthLoginActivity", "completed");
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.d("OAuthLoginActivity", "e:" + e);
                    }

                    @Override
                    public void onNext(User user) {
                        Log.d("OK",user.getLogin());
                        Toast.makeText(OAuthLoginActivity.this, user.getLogin(), Toast.LENGTH_SHORT).show();
                    }
                });
    }
}
回复
阅读 3.8k
3 个回答

补充:感谢@DOS和@shaobin0604提醒

你可以试试我的方法:

  1. 不要在布局文件中声明<WebView>,改成在Activity中创建 如,WebView mWebView = new WebView(getApplicationContext());
  2. 在布局文件中用容器类布局,比如FrameLayout作为WebView的容器,在Activity中主动把WebView添加到容器中。
  3. 在OnDestory()中移除、销毁WebView。

举个例子吧:我们用FrameLayout作为WebView的父容器

1: 使用容器包裹WebView

<FrameLayout
    android:id="@+id/container"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"/>

2:在Activity中创建WebView,在OnDestroy()方法中从容器中移除、销毁WebView

public class MyActivity extends Activity {
    private FrameLayout mContainer;
    private WebView mWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

         ...
            
        mContainer = (FrameLayout) findViewById(R.id.container);
        mWebView = new WebView(getApplicationContext());
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
                               ViewGroup.LayoutParams.MATCH_PARENT,
                               ViewGroup.LayoutParams.MATCH_PARENT);
        mWebView.setLayoutParams(p);
        mContainer.addView(mWebView);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mContainer.removeAllViews();
        mWebView.destroy();
    }
}

之所以这么做的原因是在XML文件中创建WebView,会把Activity作为Context传给WebView,而不是Application Context。所以在finishingActivity的时候,WebView任然持有Activity引用,导致Activity无法被回收。更多详情,戳这里

楼主可以使用MAT分析一下,具体是哪个对象在持有Activity对象,然后在就可以定位到问题了。

再加个:独立进程

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏