关于Camera$Parameters的BUG历险记

最近在开发系统的相机应用,今天发现一个奇怪的BUG,Camera.java里面报的一个关于LinkedHashMap的空指针的错误。。。

java.lang.NullPointerException: Attempt to write to field 'java.util.LinkedHashMap$LinkedEntry java.util.LinkedHashMap$LinkedEntry.prv' on a null object reference
at java.util.LinkedHashMap.postRemove(LinkedHashMap.java:292)
at java.util.HashMap.remove(HashMap.java:629)
at android.hardware.Camera$Parameters.put(Camera.java:4139)
at android.hardware.Camera$Parameters.set(Camera.java:4119)
at android.hardware.Camera$Parameters.setColorEffect(Camera.java:4876)
at com.xxx.camera.c.a.c(DynamicFilterManager.java:148)
at com.xxx.camera.c.a.f(DynamicFilterManager.java:158)
at com.xxx.camera.o.br(CamModule.java:3392)
at com.xxx.camera.o.l(CamModule.java:2193)
at com.xxx.camera.o.aQ(CamModule.java:706)
at com.xxx.camera.o.t(CamModule.java:91)
at com.xxx.camera.o$n.run(CamModule.java:3283)
复制代码

这。。。很明显不是APP的锅。。。看了代码就是在AsyncTask里面调了一个方法而已。。。难道真的是AsyncTask有毒吗!?相关的代码如下:

new AsyncTaskEx() {
    @Override
    protected Void doInBackground(Void... params) {
    	//...省略
        mParamsManager.getParams().setColorEffect(filterName);
        mParamsManager.setParameters();
        return null;
    }
}.execute();
复制代码

参考了下面这篇文章之后:

http://longyi-java.iteye.com/blog/1678574
复制代码

得出的结论如下:

  1. Camera对象中含有一个内部类Camera.Parameters。厂商可以针对该类,可以对Camera的特性进行定制。
  2. 在Parameters中设置完成后,需要调用Camera.setParameters()方法,相应的设置才会生效。
  3. 由于不同的设备,Camera的特性是不同的,所以在设置时,需要首先判断设备对应的特性,再加以设置。比如在调用setColorEffects之前最好先调用getSupportedColorEffects。如果设备不支持颜色特性,那么该方法的调用将抛出一个空指针异常。

下面继续分析Camera的setColorEffect方法:

public void setColorEffect(String value) {
    set(KEY_EFFECT, value);
}
复制代码

setColorEffect实际调用了set方法:

public void set(String key, String value) {
    if (key.indexOf('=') != -1 || key.indexOf(';') != -1 || key.indexOf(0) != -1) {
        Log.e(TAG, "Key \"" + key + "\" contains invalid character (= or ; or \\0)");
        return;
    }
    if (value.indexOf('=') != -1 || value.indexOf(';') != -1 || value.indexOf(0) != -1) {
        Log.e(TAG, "Value \"" + value + "\" contains invalid character (= or ; or \\0)");
        return;
    }

    put(key, value);
}
复制代码

然后是put方法:

private void put(String key, String value) {
    /*
     * Remove the key if it already exists.
     *
     * This way setting a new value for an already existing key will always move
     * that key to be ordered the latest in the map.
     */
    mMap.remove(key);
    mMap.put(key, value);
}
复制代码

这里我们可以看到,mMap就是一个LinkedHashMap,就是我们需要分析的目标。在进行put操作之前,先执行了remove(key),由于不同厂商定制的Camera不一样,这个key在原生Camera中是这样的:

private static final String KEY_EFFECT = "effect";
复制代码

但是,定制过之后的机器可能就不是这个值了,例如下面的filter-mode,虽然一般的厂商不会这样干。下面是我自己的解决办法,就是根不同据机器设置不同的值:

if (DeviceHelper.DEVICE_IS_XXX) {
    mParamsManager.getParams().set("filter-mode", filterName);
}
复制代码

另外,在设置Camera的setParameters之前,最好先判断一下机器的支持属性,例如本例子中的ColorEffects的设置:

List supportedColorEffects = mParamsManager.getParams().getSupportedColorEffects();
if (supportedColorEffects.contains(filterName)) {
    mParamsManager.getParams().setColorEffect(filterName);
}
复制代码

最后,我想说的是,AsyncTask我错怪你了,不过AsyncTask的坑切实很多,鉴于老项目喜欢用这个,因此有时间的话还是有必要去学习研究一下AsyncTask。

如果觉得我的文字对你有所帮助的话,欢迎关注我的公众号:

我的群欢迎大家进来探讨各种技术与非技术的话题,有兴趣的朋友们加我私人微信huannan88,我拉你进群交(♂)流(♀)

你可能感兴趣的:(关于Camera$Parameters的BUG历险记)