Утечка памяти при использовании Binder и AHandlerReflector на Android 6.0
Я написал пример кода, используя binder и AHandlerReflector, и у него есть утечки памяти после выполнения.
Весь процесс заключается в том, что существуют IMYClient и IMYService, клиент связывается со службой через связующее, а также служба связывается с клиентом через другое связующее.
В IMYService.cpp он связывается со стороной службы через связыватель (REQUEST_RESOURCES_ASYNC), а со стороны службы он вызывает функцию handleRequestDone(), определенную в IMYClient.cpp.
В IMYClient.cpp он связывается со стороной клиента через связыватель (HANDLE_REQUEST_DONE), а на стороне клиента он вызывает функцию handleRequestDone(), определенную в MYClient.cpp.
В MYClient.cpp он отправляет сообщение "handleRequestDone" в AHandlerReflector onMessageReceived(), в onMessageReceived () он вызывает callback handleRequestDone(), определенный в Test.cpp.
После выполнения Test.cpp будет пропущен поток "MYClient", определенный в MYClient.cpp, потому что деструктор ~MYClient() не вызывается. Но почему деструктор не называется?
test.cpp
#include <MYClientListener.h>
#include <MYRequest.h>
#include <MYResponse.h>
#include <utils/RefBase.h>
struct TestMYClientListener : public MYClientListener {
TestMYClientListener();
virtual void handleRequestDone(const sp<MYResponse> &response);
sp<ValueUpdateListener<bool> > mRequestDone;
sp<MYResponse> mResponse;
};
TestMYClientListener::TestMYClientListener()
:mRequestDone(new ValueUpdateListener<bool>(false))
{
}
void TestMYClientListener::handleRequestDone(const sp<MYResponse> &response)
{
mResponse = response;
mRequestDone->set(true);
}
static const uint64_t kWaitIntervalUs = 10000000UL;
using namespace android;
void testFunc() {
sp<MYClient> mMYClient;
sp<TestMYClientListener> mMYListener;
mMYListener = new TestMYClientListener();
mMYClient = new MYClient(mMYListener);
mMYListener->mRequestDone->set(false);
sp<MYRequest> req = new MYRequest();
req->addComponent(kComponentVideoDecoder);
mMYClient->requestResourcesAsync(req);
mMYListener->mRequestDone->waitForValue(true, kWaitIntervalUs);
}
IMYService.h
#include <sys/types.h> // pid_t
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <media/stagefright/foundation/AHandlerReflector.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
#include <MYTypes.h>
class ALooper;
class AMessage;
class IMYClient;
class MYRequest;
class IMYService : public IInterface {
public:
DECLARE_META_INTERFACE(MYService);
virtual void requestResourcesAsync(const sp<IMYClient> &client,
const sp<MYRequest> &request,
pid_t pid) = 0;
// Name under which this service is exported.
static const String16 kServiceName;
};
class BpMYService : public BpInterface<IMYService> {
public:
BpMYService(const sp<IBinder> &impl);
virtual void requestResourcesAsync(const sp<IMYClient> &client,
const sp<MYRequest> &request,
pid_t pid);
};
class BnMYService : public BnInterface<IMYService> {
public:
virtual status_t onTransact(uint32_t code,
const Parcel &data,
Parcel *reply,
uint32_t flags = 0);
};
IMYService.cpp
#include <utils/Log.h>
#include <IMYService.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
#include <IMYClient.h>
#include <MYRequest.h>
#include <MYResponse.h>
enum {
REQUEST_RESOURCES_ASYNC = IBinder::FIRST_CALL_TRANSACTION,
};
const String16 IMYService::kServiceName("media.hw_mymanager");
BpMYService::BpMYService(const sp<IBinder> &impl)
: BpInterface<IMYService>(impl) {
}
void BpMYService::requestResourcesAsync(const sp<IMYClient> &client,
const sp<MYRequest> &request,
pid_t pid) {
Parcel data;
Parcel reply;
data.writeInterfaceToken(IMYService::getInterfaceDescriptor());
data.writeStrongBinder(client->asBinder(client));
request->writeToParcel(&data);
data.writeInt32(pid);
remote()->transact(REQUEST_RESOURCES_ASYNC, data, &reply, IBinder::FLAG_ONEWAY);
}
IMPLEMENT_META_INTERFACE(MYService, "android.myservice");
// -------------------------------------------------------------------
status_t BnMYService::onTransact(uint32_t code,
const Parcel &data,
Parcel *reply,
uint32_t flags) {
ALOGV("BnMYService::onTransact");
switch(code) {
case REQUEST_RESOURCES_ASYNC: {
CHECK_INTERFACE(IMYService, data, reply);
sp<IMYClient> client = interface_cast<IMYClient>(
data.readStrongBinder());
sp<MYRequest> request = new MYRequest();
request->readFromParcel(data);
pid_t pid = static_cast<pid_t>(data.readInt32());
sp<MYResponse> response = NULL;
client->handleRequestDone(response);
break;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}
ALOGV("BnMYService::onTransact done");
return OK;
}
IMYClient.h
#include <binder/IInterface.h>
#include <binder/Parcel.h>
class AMessage;
class MYResponse;
class IMYClient : public IInterface {
public:
DECLARE_META_INTERFACE(MYClient);
virtual void handleRequestDone(const sp<MYResponse> &resp) = 0;
};
class BpMYClient : public BpInterface<IMYClient> {
public:
explicit BpMYClient(const sp<IBinder> &impl);
virtual void handleRequestDone(const sp<MYResponse> &resp);
};
class BnMYClient : public BnInterface<IMYClient> {
public:
virtual status_t onTransact(uint32_t code,
const Parcel &data,
Parcel *reply,
uint32_t flags = 0);
};
IMYClient.cpp
#include <utils/Log.h>
#include <IMYClient.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <MYResponse.h>
enum {
HANDLE_REQUEST_DONE = IBinder::FIRST_CALL_TRANSACTION
};
BpMYClient::BpMYClient(const sp<IBinder> &impl)
: BpInterface<IMYClient>(impl) {
}
void BpMYClient::handleRequestDone(const sp<MYResponse> &resp) {
Parcel data;
Parcel reply;
data.writeInterfaceToken(IMYClient::getInterfaceDescriptor());
if (resp != NULL) {
resp->writeToParcel(&data);
}
remote()->transact(HANDLE_REQUEST_DONE, data, &reply);
}
IMPLEMENT_META_INTERFACE(MYClient, "android.myclient");
// -------------------------------------------------------------------
status_t BnMYClient::onTransact(uint32_t code,
const Parcel &data,
Parcel *reply,
uint32_t flags) {
CHECK_INTERFACE(IMYClient, data, reply);
switch(code) {
case HANDLE_REQUEST_DONE: {
ALOGV("HANDLE_REQUEST_DONE");
if (data.dataSize() > 0) {
sp<MYResponse> resp = new MYResponse();
resp->readFromParcel(data);
handleRequestDone(resp);
} else {
handleRequestDone(NULL);
}
break;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}
return OK;
}
MYClient.h
#include <media/stagefright/foundation/AHandlerReflector.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
#include <utils/ValueUpdateListener_inl.h>
#include <IMYClient.h>
#include <MYClientListener.h>
#include <MYTypes.h>
class ALooper;
class AMessage;
class IMYService;
class MYClientListener;
class MYRequest;
class MYResponse;
class MYClient : public BnMYClient,
public IBinder::DeathRecipient {
public:
enum {
kWhatCmdResourceRequest = 'creq',
};
explicit MYClient(const sp<MYClientListener> &listener);
virtual void binderDied(const wp<android::IBinder> &binder);
virtual void handleRequestDone(const sp<MYResponse> &resp);
void onMessageReceived(const sp<AMessage> &msg);
void requestResourcesAsync(const sp<MYRequest> &request);
bool refreshMY_l();
sp<IMYService> getMY();
protected:
virtual ~MYClient(); // only RefBase can call destructor
sp<IMYService> mMYService; // can be overriden in test
private:
Mutex mMYServiceLock;
sp<ALooper> mLooper;
sp<AHandlerReflector<MYClient> > mReflector;
sp<MYClientListener> mListener;
};
MYClient.cpp
#include <utils/Log.h>
#include <MYClient.h>
#include <unistd.h> // getpid()
#include <binder/IServiceManager.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AHandlerReflector.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <IMYService.h>
#include <MYClientListener.h>
#include <MYRequest.h>
#include <MYResponse.h>
#include <cutils/properties.h>
static const int64_t kOneSecInUs = 1000000LL;
static AString parseEnum(int32_t msg) {
return AStringPrintf("%c%c%c%c",
(msg >> 24) & 0xff, (msg >> 16) & 0xff, (msg >> 8) & 0xff, msg & 0xff);
}
MYClient::MYClient(const sp<MYClientListener> &listener)
: mLooper(new ALooper()),
mListener(listener) {
mListener->setClient(this);
mLooper->setName("MYClient");
// Virtually converts this class into AHandler.
mReflector = new AHandlerReflector<MYClient>(this);
mLooper->registerHandler(mReflector);
CHECK_EQ(static_cast<status_t>(OK), mLooper->start(
false, /* runOnCallingThread */
true, /* canCallJava */
PRIORITY_NORMAL));
}
MYClient::~MYClient() {
mLooper->unregisterHandler(mReflector->id());
mReflector.clear();
mLooper->stop();
mLooper.clear();
}
void MYClient::binderDied(const wp<android::IBinder> &binder) {
}
void MYClient::handleRequestDone(const sp<MYResponse> &resp) {
sp <AMessage> msg = new AMessage(MYClientListener::kWhatRequestDone,
mReflector);
msg->setObject("response", resp);
msg->post();
}
void MYClient::onMessageReceived(const sp<AMessage> &msg) {
switch(msg->what()) {
case kWhatCmdResourceRequest: {
sp<IMYService> my = getMY();
if (my == NULL) {
sp<MYResponse> response = NULL;
mListener->handleRequestDone(response);
return;
}
sp<RefBase> obj;
CHECK(msg->findObject("request", &obj));
sp<MYRequest> request = static_cast<MYRequest *>(obj.get());
my->requestResourcesAsync(this, request, getpid());
break;
}
case MYClientListener::kWhatRequestDone: {
sp<RefBase> obj;
CHECK(msg->findObject("response", &obj));
sp<MYResponse> response = static_cast<MYResponse *>(obj.get());
mListener->handleRequestDone(response);
break;
}
default: {
break;
}
}
}
void MYClient::requestResourcesAsync(const sp<MYRequest> &request) {
sp<AMessage> msg = new AMessage(kWhatCmdResourceRequest, mReflector);
msg->setObject("request", request);
msg->post();
}
bool MYClient::refreshMY_l() {
sp<IServiceManager> sm = defaultServiceManager();
if (sm == NULL) {
ALOGE("refreshMY_l:Failed to get default service manager.");
return false;
}
sp<IBinder> binder = sm->getService(IMYService::kServiceName);
if (binder == NULL) {
ALOGE("refreshMY_l:Failed to get %s service.",
String8(IMYService::kServiceName).string());
return false;
}
mMYService = interface_cast<IMYService>(binder);
return true;
}
sp<IMYService> MYClient::getMY() {
AutoMutex l(mMYServiceLock);
if (mMYService == NULL) {
if (!refreshMY_l()) {
return NULL;
}
}
return mMYService;
}
MYClientListener.h
#include <media/stagefright/foundation/AHandler.h>
#include <media/stagefright/foundation/AHandlerReflector.h>
#include <utils/RefBase.h>
#include <MYTypes.h>
class AMessage;
class MYClient;
class MYRequest;
class MYResponse;
class MYClientListener : public RefBase {
public:
enum {
kWhatRequestDone = 'ldon', // TODO: use a common starting indexes
};
MYClientListener();
void setClient(const wp<MYClient> &client);
ALooper::handler_id getHandlerId();
void onMessageReceived(const sp<AMessage> &msg);
virtual void handleRequestDone(const sp<MYResponse> &response) = 0;
wp<MYClient> mClient;
sp<ALooper> mLooper;
sp<AHandlerReflector<MYClientListener> > mReflector;
protected:
virtual ~MYClientListener(); // only RefBase can call destructor
};
MYClientListener.cpp
#include <utils/Log.h>
#include <MYClientListener.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <utils/RefBase.h>
#include <MYClient.h>
#include <MYResponse.h>
MYClientListener::MYClientListener()
: mLooper(new ALooper()) {
mLooper->setName("MYClientListener");
// Virtually converts this class into AHandler.
mReflector = new AHandlerReflector<MYClientListener>(this);
mLooper->registerHandler(mReflector);
CHECK_EQ(static_cast<status_t>(OK), mLooper->start(
false, /* runOnCallingThread */
true, /* canCallJava */
PRIORITY_NORMAL));
}
MYClientListener::~MYClientListener() {
ALOGV("~MYClientListener");
mLooper->stop();
mLooper->unregisterHandler(mReflector->id());
}
void MYClientListener::onMessageReceived(const sp<AMessage> &msg) {
ALOGE("Error: msg %d not handled:", msg->what());
}
ALooper::handler_id MYClientListener::getHandlerId() {
return mReflector->id();
}
void MYClientListener::setClient(const wp<MYClient> &client) {
CHECK(mClient == NULL);
mClient = client;
}