项目用到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。