Service内部类Binder造成的内存泄漏

前言

我写了一个基础的绑定service,点击按钮解绑的时候LeakCanary告诉我内存泄漏了。。。

Code

// ITest.aidl
package com.jesse.first.aidl;

interface ITest {
    int getData();
    void setData(int i);
}
//MyService.java
package com.jesse.first;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.jesse.first.aidl.ITest;

public class MyService extends Service {
    private static final String TAG = "MainActivity";
    private MyBinder myBinder = new MyBinder();

    @Override
    public IBinder onBind(Intent intent) {
        return myBinder;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "服务已销毁");
    }

    class MyBinder extends ITest.Stub{

        @Override
        public int getData() throws RemoteException {
            return 0;
        }

        @Override
        public void setData(int i) throws RemoteException {
            Log.d(TAG, i + "");
        }
    }
}
//MainActivity.java
package com.jesse.first;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Button;
import com.jesse.first.aidl.ITest;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private Button btn;
    private ITest iTest;

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "服务已开启");
            iTest = ITest.Stub.asInterface(service);
            try {
                Log.d(TAG, iTest.getData() + "");
                iTest.setData(10);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, MyService.class);
        this.bindService(intent, conn, BIND_AUTO_CREATE);
        btn = findViewById(R.id.btn);
        btn.setOnClickListener(v -> {
            Log.d(TAG, "销毁服务");
            this.unbindService(conn);
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}

LeakCanary Warning & Android Profiler

内存泄漏
内存泄漏

分析原因

MainActivity中的成员变量 iTest 持有Binder对象,Binder对象是Service中的内部类,该内部类持有Service对象,所以Service对象在解绑后也不会销毁。

解决方案

将成员变量 iTest 改为方法中的局部变量

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "服务已开启");
            ITest iTest = ITest.Stub.asInterface(service);
            try {
                Log.d(TAG, iTest.getData() + "");
                iTest.setData(10);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

思考

  1. 假如Service的生命周期和Activity的生命周期一样,也就是在Activity中的onDestory调用unbind将Service解绑,那么本例就不会出现内存泄漏,因为Activity最终也会被销毁了。
  2. 假如应用是一个单Activity多fragment的应用,一直不会解绑,还需要考虑unbind带来的内存泄漏吗?当然是不需要。有的时候内存泄漏是可以允许存在的。也需要具体分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值