Android MediaProvider MtpService启动多次导致OutofMemory

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();
        }
    }

就是这个native_finalize()方法执行后才能释放native代码中对java MtpDatabase对象的引用:

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方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值