MtpService.java的onStartCommand:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mUnlocked = intent.getBooleanExtra(UsbManager.USB_DATA_UNLOCKED, false);
if (LOGD) { Log.d(TAG, "onStartCommand intent=" + intent + " mUnlocked=" + mUnlocked); }
synchronized (mBinder) {
updateDisabledStateLocked();
mPtpMode = (intent == null ? false
: intent.getBooleanExtra(UsbManager.USB_FUNCTION_PTP, false));
String[] subdirs = null;
if (mPtpMode) {
int count = PTP_DIRECTORIES.length;
subdirs = new String[count];
for (int i = 0; i < count; i++) {
File file =
Environment.getExternalStoragePublicDirectory(PTP_DIRECTORIES[i]);
// make sure this directory exists
file.mkdirs();
subdirs[i] = file.getPath();
}
}
final StorageVolume primary = StorageManager.getPrimaryVolume(mVolumes);
if (mDatabase != null) {
mDatabase.setServer(null);
}
mDatabase = new MtpDatabase(this, MediaProvider.EXTERNAL_VOLUME,
primary.getPath(), subdirs);
manageServiceLocked();
}
return START_REDELIVER_INTENT;
}
此处每次进来都会new一个MtpDatabase对象,内存溢出就是因为这个对象不能被回收导致。
MtpDatabase对象在native中有一个global的引用,而释放哪个引用的函数是在java MtpDatabase的finilize方法中调用的:
@Override
protected void finalize() throws Throwable {
try {
native_finalize();
} finally {
super.finalize();
}
}
android_mtp_mtpdatabase.cpp
static JNINativeMethod gMtpDatabaseMethods[] = {
{"native_setup", "()V", (void *)android_mtp_MtpDatabase_setup},
{"native_finalize", "()V", (void *)android_mtp_MtpDatabase_finalize},
};
native_finalize对应的函数为android_mtp_MtpDatabase_finalize:
static void
android_mtp_MtpDatabase_finalize(JNIEnv *env, jobject thiz)
{
MyMtpDatabase* database = (MyMtpDatabase *)env->GetLongField(thiz, field_context);
database->cleanup(env);
delete database;
env->SetLongField(thiz, field_context, 0);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
这里调用了MyMtpDatabase对象database的cleanup函数:
void MyMtpDatabase::cleanup(JNIEnv *env) {
env->DeleteGlobalRef(mDatabase);
env->DeleteGlobalRef(mIntBuffer);
env->DeleteGlobalRef(mLongBuffer);
env->DeleteGlobalRef(mStringBuffer);
}
这里的第一行就是释放native代码对java MtpDatabase对象的引用mDatabase.
然而java对象的finalize方法只有java对象被释放时才调用,也即此对象没有被引用时才能调finalize。
而本地代码中的引用一直持有这个java对象的引用,则它的finalize方法永远无法执行。次对象永远不会
被回收。
修改此问题的方案就是将native代码中MyMtpDatabase对象的方法cleanup独立出来通过jni暴露给java
MtpDatabase对象去在java MtpDatabase的setServer方法中调用。什么时候调用,从MtpService的代码
来看,每次丢弃一个MtpDatabase对象时都调用其setServer方法传如null参数,因此只要在MtpDatabase
的setServer方法中判断参数为null,就调用前面通过jni暴露出来的cleanup方法。