前言
我写了一个基础的绑定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) {
}
};
思考
- 假如Service的生命周期和Activity的生命周期一样,也就是在Activity中的onDestory调用unbind将Service解绑,那么本例就不会出现内存泄漏,因为Activity最终也会被销毁了。
- 假如应用是一个单Activity多fragment的应用,一直不会解绑,还需要考虑unbind带来的内存泄漏吗?当然是不需要。有的时候内存泄漏是可以允许存在的。也需要具体分析。