通过定位,发现在hwui中,当绘制带阴影的字体的时候,使用了renderScript,由于renderScript中绘制阴影时,会开启3-4个线程来处理阴影文字。
void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) { #ifdef ANDROID_ENABLE_RENDERSCRIPT if (width * height * radius >= RS_MIN_INPUT_CUTOFF) { uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); if (mRs == 0) { mRs = new RSC::RS(); if (!mRs->init(RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) { ALOGE("blur RS failed to init"); } mRsElement = RSC::Element::A_8(mRs); mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement); } RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image); RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage); mRsScript->setRadius(radius); mRsScript->setInput(ain); mRsScript->forEach(aout); // replace the original image's pointer, avoiding a copy back to the original buffer free(*image); *image = outImage; return; } #endif当内存不足时,会调用GammaFontRenderer的clear函数,也就是会将当前的FontRenderer对象删除,由于rs对象是以智能指针的形式来使用,所以正常情况在FontRenderer的时候会将当前的rs指针对象都删除,通过写一些测试用例发现,rs使用的智能指针在析构的时候并没有将rs的对象删除,这也就是导致每次创建了rs对象没删除,导致一直会创建大量的线程的原因。
通过查看renderScript的代码,发现rs的对象都是使用强指针引用(sp)的方式来使用:
struct { sp<const Element> U8; sp<const Element> U8_2; sp<const Element> U8_3; sp<const Element> U8_4; ………… sp<const Element> A_8; sp<const Element> RGB_565; sp<const Element> RGB_888; sp<const Element> RGBA_5551; sp<const Element> RGBA_4444; sp<const Element> RGBA_8888; sp<const Element> YUV; sp<const Element> MATRIX_4X4; sp<const Element> MATRIX_3X3; sp<const Element> MATRIX_2X2; } mElements;这样导致mRsElement中使用了mRs的强引用,当mRsElement也保存了mRs的强引用,当mRsElement生命周期结束时,由于不等1,所以只能将mRsElement的引用计数减1,mRs还是为2,这样也就会导致最后出现循环引用问题,无法将任何一个对象删除,这就导致了申请的线程没有释放,出现了fb socket泄露的问题。
将FontRenderer的rs对象都设置为静态对象,这样就能避免出现socket泄露的问题:
#ifdef ANDROID_ENABLE_RENDERSCRIPT // RS constructs static RSC::sp<RSC::RS> mRs; static RSC::sp<const RSC::Element> mRsElement; static RSC::sp<RSC::ScriptIntrinsicBlur> mRsScript; #endif
2. 修改renderScript:
这种修改方式比较麻烦,需要将rs中存在强智能指针的对象都改为普通指针的方式,所幸的是,后面google已经在这方便做了修改,所以可以参考google官网的修改:https://android-review.googlesource.com