使用 RenderScript 时的一个bug

项目用到RenderScript,调用 RenderScript.create(getContext()) 时抛出异常 RenderScript code cache directory uninitialized,这个调用在7.1上正常,在7.0上却会导致应用 crash,打开 RenderScript.java,追溯 create 方法,在internalCreate 中调用了 getCachePath

    /**
     * Gets the path to the code cache.
     */
    static synchronized String getCachePath() {
        if (mCachePath == null) {
            final String CACHE_PATH = "com.android.renderscript.cache";
            if (RenderScriptCacheDir.mCacheDir == null) {
                throw new RSRuntimeException("RenderScript code cache directory uninitialized.");
            }
            File f = new File(RenderScriptCacheDir.mCacheDir, CACHE_PATH);
            mCachePath = f.getAbsolutePath();
            f.mkdirs();
        }
        return mCachePath;
    }

复制代码

当RenderScriptCacheDir.mCacheDir == null 时,抛出了这个异常,查看 RenderScriptCacheDir 的代码:

/**
 * Used only for tracking the RenderScript cache directory.
 * @hide
 */
public class RenderScriptCacheDir {
     /**
     * Sets the directory to use as a persistent storage for the
     * renderscript object file cache.
     *
     * @hide
     * @param cacheDir A directory the current process can write to
     */
    public static void setupDiskCache(File cacheDir) {
        // Defer creation of cache path to nScriptCCreate().
        mCacheDir = cacheDir;
    }

    static File mCacheDir;

}
复制代码

实现很简单,就是管理一个变量 mCacheDir,在安卓7.0 和 7.1的源码里面全局搜索setupDiskCache这个方法的调用信息,发现只有一处调用,在ActivityThread 的 setupGraphicsSupport 里,这个方法在handleBindApplication 方法里被调用,这里只需明白这个方法的调用在应用java层的早期,在应用刚刚把ApplicationThread 注册到AMS之后,查看7.0 的实现:

int uid = Process.myUid();
String[] packages = getPackageManager().getPackagesForUid(uid);
// If there are several packages in this application we won't
// initialize the graphics disk caches
if (packages != null && packages.length == 1) {
  	ThreadedRenderer.setupDiskCache(cacheDir);
  	RenderScriptCacheDir.setupDiskCache(cacheDir);
}
复制代码

而以下是7.1 的实现:

int uid = Process.myUid();
String[] packages = getPackageManager().getPackagesForUid(uid);

if (packages != null) {
 	 ThreadedRenderer.setupDiskCache(cacheDir);
 	 RenderScriptCacheDir.setupDiskCache(cacheDir);
}
复制代码

减少了一个判断 packages.length == 1 ,从Uid读出对应包名,在7.0 上若包名不为1就跳过赋值的代码,而我们的应用为系统应用,Uid为android.uid.system,对应几十个应用,所以导致RenderScriptCacheDir.mCacheDir == null ,RenderScript.create 抛出异常,7.1的代码中去除了这个判断。

所以应用想兼容7.1之前的版本,可以在RenderScript.create 之前 先为RenderScriptCacheDir.mCacheDir 赋值,根据 ActivityThread 里面的赋值逻辑,这个赋值应该是 Context.getCodeCacheDir();RenderScriptCacheDir类为隐藏类,所以需要反射赋值。

另外,可以使用兼容包绕开这个bug。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值