2015年11月

【谜之RxJava (二) —— Magic Lift】

Rxjava -- 一个异步库

RxJava最迷人的是什么?
答案就是把异步序列写到一个工作流里!javascriptPromise/A如出一辙。
OK,在java中做异步的事情在我们传统理解过来可不方便,而且,如果要让异步按照我们的工作流来,就更困难了。

但是在RxJava中,我们只要调用调用
subscribOn()observeOn()就能切换我们的工作线程,是不是让小伙伴都惊呆了?

然后结合RxJavaOperator,写异步的时候,想切换线程就是一行代码的事情,整个workflow还非常清晰:

Observable.create()
// do something on io thread
.work() // work.. work..
.subscribeOn(Schedulers.io())
// observeOn android main thread
.observeOn(AndroidSchedulers.mainThread())
.subscribe();

我们再也不用去写什么见鬼的new ThreadHandler了,在这么几行代码里,我们实现了在io线程上做我们的工作(work),在main线程上,更新UI

Subscribe On

先看下subscribeOn干了什么

 public final Observable<T> subscribeOn(Scheduler scheduler) {
    if (this instanceof ScalarSynchronousObservable) {
        return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
    }
    return nest().lift(new OperatorSubscribeOn<T>(scheduler));
}

啊,原来也是个lift,就是从一个Observable生成另外一个Observable咯,这个nest是干嘛用?

 public final Observable<Observable<T>> nest() {
    return just(this);
}

这里返回类型告诉我们,它是产生一个Observable<Observable<T>>
讲到这里,会有点晕,先记着这个,然后我们看OperatorSubscribeOn这个操作符,

构造函数是

public OperatorSubscribeOn(Scheduler scheduler) {
    this.scheduler = scheduler;
}

OK,这里保存了scheduler对象,然后就是我们前一章说过的转换方法。

 @Override
public Subscriber<? super Observable<T>> call(final Subscriber<? super T> subscriber) {
    final Worker inner = scheduler.createWorker();
    subscriber.add(inner);
    return new Subscriber<Observable<T>>(subscriber) {

        @Override
        public void onCompleted() {
            // ignore because this is a nested Observable and we expect only 1 Observable<T> emitted to onNext
        }

        @Override
        public void onError(Throwable e) {
            subscriber.onError(e);
        }

        @Override
        public void onNext(final Observable<T> o) {
            inner.schedule(new Action0() {

                @Override
                public void call() {
                    final Thread t = Thread.currentThread();
                    o.unsafeSubscribe(new Subscriber<T>(subscriber) {

                        @Override
                        public void onCompleted() {
                            subscriber.onCompleted();
                        }

                        @Override
                        public void onError(Throwable e) {
                            subscriber.onError(e);
                        }

                        @Override
                        public void onNext(T t) {
                            subscriber.onNext(t);
                        }

                        @Override
                        public void setProducer(final Producer producer) {
                            subscriber.setProducer(new Producer() {

                                @Override
                                public void request(final long n) {
                                    if (Thread.currentThread() == t) {
                                        // don't schedule if we're already on the thread (primarily for first setProducer call)
                                        // see unit test 'testSetProducerSynchronousRequest' for more context on this
                                        producer.request(n);
                                    } else {
                                        inner.schedule(new Action0() {

                                            @Override
                                            public void call() {
                                                producer.request(n);
                                            }
                                        });
                                    }
                                }

                            });
                        }

                    });
                }
            });
        }

    };
}

让人纠结的类模板

看完这段又臭又长的,先深呼吸一口气,我们慢慢分析下。
首先要注意RxJava里面最让人头疼的模板问题,那么OperatorMap这个类的声明是

public final class OperatorMap<T, R> implements Operator<R, T>

Operator这个接口继承Func1

public interface Func1<T, R> extends Function {
    R call(T t);
}

我们这里不要记TR,记住传入左边的模板是形参,传入右边的模板是返回值

好了,那么这里的call就是从一个T转换成一个Observable<T>的过程了。

总结一下,我们这一次调用subscribeOn,做了两件事

1、nest()Observable<T>生成了一个Observable<Observable<T>>
2、lift()Observalbe<Observalbe<T>>进行一个变化,变回Observable<T>

因为lift是一个模板函数,它的返回值的类型是参照它的形参来,而他的形参是Operator<T, Observable<T>> 这个结论非常重要!!
OK,到这里我们已经存储了所有的序列,等着我们调用了。

调用链

首先,记录我们在调用这条指令之前的Observable<T>,记为Observable$1
然后,经过lift生成的Observable<T>记为Observable$2

好了,现在我们拿到的依然是Observable<T>这个对象,但是它不是原始的Observable$1,要深深记住这一点,它是由lift生成的Observable$2,这时候进行subscribe,那看到首先调用的就是OnSubscribe.call方法,好,直接进入lift当中生成的那个地方。

我们知道这一层liftoperator就是刚刚的OperatorSubscribOn,那么调用它的call方法,生成的是一个Subscriber<Observable<T>>

Subscriber<? super T> st = hook.onLift(operator).call(o);
try {
    // new Subscriber created and being subscribed with so 'onStart' it
    st.onStart();
    onSubscribe.call(st);
} catch (Throwable e) {
...
}

好,还记得我们调用过nest么?,这里的onSubscribe可是nest上下文中的噢,每一次,到这个地方,这个onSubscribe就是上一层ObservableonSubscribe,即Observable<Observable<T>>onSubscribe,相当于栈弹出了一层。它的call直接在SubscriberonNext中给出了最开始的Observable<T>,我们这里就要看下刚刚在OperatorSubscribeOn中生成的Subscriber

new Subscriber<Observable<T>>(subscriber) {

    @Override
    public void onCompleted() {
        // ignore because this is a nested Observable and we expect only 1 Observable<T> emitted to onNext
    }
    
    @Override
    public void onError(Throwable e) {
        subscriber.onError(e);
    }
    
    @Override
    public void onNext(final Observable<T> o) {
        inner.schedule(new Action0() {
    
            @Override
            public void call() {
                final Thread t = Thread.currentThread();
                o.unsafeSubscribe(new Subscriber<T>(subscriber) {
    
                    @Override
                    public void onCompleted() {
                        subscriber.onCompleted();
                    }
    
                    @Override
                    public void onError(Throwable e) {
                        subscriber.onError(e);
                    }
    
                    @Override
                    public void onNext(T t) {
                        subscriber.onNext(t);
                    }
                });
            }
        });
    }
}

对,就是它,这里要注意,这里的subscriber就是我们在lift中,传入的o

Subscriber<? super T> st = hook.onLift(operator).call(o);

对,就是它,其实它就是SafeSubscriber

回过头,看看刚刚的onNext()方法,inner.schedule() 这个函数,我们可以认为就是postRun()类似的方法,而onNext()中传入的o是我们之前生成的Observable$1,是从Observable.just封装出来的Observable<Observable<T>>中产生的,这里调用了Observable$1.unsafeSubscribe方法,我们暂时不关心它和subscribe有什么不同,但是我们知道最终功能是一样的就好了。

注意它运行时的线程!!在inner这个Worker上!于是它的运行线程已经被改了!!

好,这里的unsafeSubscribe调用的方法就是调用原先Observable$1.onSubscribe中的call方法:
这个Observable$1就是我们之前自己定义的Observable了。

综上所述,如果我们需要我们的Observable$1在一个别的线程上运行的时候,只需要在后面跟一个subscribeOn即可。结合扔物线大大的图如下:
rxjavarxjava_12.png

总结

这里逻辑着实不好理解。如果还没有理解的朋友,可以按照我前文说的顺序,细致的看下来,我把逻辑过一遍之后,发现lift的陷阱实在太大,内部类用的风生水起,一不小心,就不知道一个变量的上下文是什么,需要特别小心。

迷之RxJava(四)—— Retrofit和RxJava的基情

本文在不停更新中,如果有不明白的地方(可能会有很多),请大家给出意见,拍砖请轻点= =

上一篇文章 讲了ObservableOnSubscribeSubscriber之间的关系。 我们知道,Observable的具体工作都是在OnSubscribe中完成的。从这个类名我们也知道,如果生成了一个Observable对象,而不进行subscribe,那么什么都不会发生!

OK,RxJava最让人兴奋的就是它有各种各样的操作符,什么map呀,flatMap呀各种,我们今天要知其然知其所以然,那么他们是如何实现功能的呢?

例子

Observable.create(new Observable.OnSubscribe<String>() {
    @Override
    public void call(Subscriber<? super String> subscriber) {
        subscriber.onNext("hello");
    }
})
.map(new Func1<String, String>() {
    @Override
    public String call(String s) {
        return s + "word";
    }
})
.subscribe(new Subscriber<String>() {
    @Override
    public void onCompleted() {

    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onNext(String s) {
        Log.d("rx", s);
    }
});

lift

我们先看下进行链式调用map之后,发生了什么。

public final <R> Observable<R> map(Func1<? super T, ? extends R> func) {
    return lift(new OperatorMap<T, R>(func));
}

对,就是调用了lift函数!,然后把我们的转换器(Transfomer,我好想翻译成变形金刚)传入进去,看下它做了什么事。

public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {
    return new Observable<R>(new OnSubscribe<R>() {
        @Override
        public void call(Subscriber<? super R> o) {
            try {
                Subscriber<? super T> st = hook.onLift(operator).call(o);
                try {
                    // new Subscriber created and being subscribed with so 'onStart' it
                    st.onStart();
                    onSubscribe.call(st);
                } catch (Throwable e) {
                    // localized capture of errors rather than it skipping all operators 
                    // and ending up in the try/catch of the subscribe method which then
                    // prevents onErrorResumeNext and other similar approaches to error handling
                    if (e instanceof OnErrorNotImplementedException) {
                        throw (OnErrorNotImplementedException) e;
                    }
                    st.onError(e);
                }
            } catch (Throwable e) {
                if (e instanceof OnErrorNotImplementedException) {
                    throw (OnErrorNotImplementedException) e;
                }
                // if the lift function failed all we can do is pass the error to the final Subscriber
                // as we don't have the operator available to us
                o.onError(e);
            }
        }
    });
}

来,我来简化一下

public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {
    return new Observable<R>(...);
}

返回了一个新的Observable对象,这才是重点! 这种链式调用看起来特别熟悉?有没有像javascript中的Promise/A,在then中返回一个Promise对象进行链式调用?

OK,那么我们要看下它是如何工作的啦。

map()调用之后,我们操作的就是新的Observable对象,我们可以把它取名为Observable$2,OK,我们这里调用subscribe,完整的就是Observable$2.subscribe,继续看到subscribe里,重要的几个调用:

hook.onSubscribeStart(observable, observable.onSubscribe).call(subscriber);
return hook.onSubscribeReturn(subscriber);

注意注意 ! 这里的observableObservable$2!!也就是说,这里的onSubscribe是,lift中定义的!!

OK,我们追踪下去,回到lift的定义中。

return new Observable<R>(new OnSubscribe<R>() {
    @Override
    public void call(Subscriber<? super R> o) {
        try {
            Subscriber<? super T> st = hook.onLift(operator).call(o);
            try {
                // new Subscriber created and being subscribed with so 'onStart' it
                st.onStart();
                onSubscribe.call(st); //请注意我!! 这个onSubscribe是原始的OnSubScribe对象!!
            } catch (Throwable e) {
                // localized capture of errors rather than it skipping all operators 
                // and ending up in the try/catch of the subscribe method which then
                // prevents onErrorResumeNext and other similar approaches to error handling
                if (e instanceof OnErrorNotImplementedException) {
                    throw (OnErrorNotImplementedException) e;
                }
                st.onError(e);
            }
        } catch (Throwable e) {
            if (e instanceof OnErrorNotImplementedException) {
                throw (OnErrorNotImplementedException) e;
            }
            // if the lift function failed all we can do is pass the error to the final Subscriber
            // as we don't have the operator available to us
            o.onError(e);
        }
    }
});

一定一定要注意这段函数执行的上下文!,这段函数中的onSubscribe对象指向的是外部类,也就是第一个ObservableonSubScribe!而不是Observable$2中的onSubscribe,OK,谨记这一点之后,看看

Subscriber<? super T> st = hook.onLift(operator).call(o);

这行代码,就是定义operator,生成一个经过operator操作过的Subscriber,看下OperatorMap这个类中的call方法

@Override
public Subscriber<? super T> call(final Subscriber<? super R> o) {
    return new Subscriber<T>(o) {

        @Override
        public void onCompleted() {
            o.onCompleted();
        }

        @Override
        public void onError(Throwable e) {
            o.onError(e);
        }

        @Override
        public void onNext(T t) {
            try {
                o.onNext(transformer.call(t));
            } catch (Throwable e) {
                Exceptions.throwIfFatal(e);
                onError(OnErrorThrowable.addValueAsLastCause(e, t));
            }
        }

    };
}

没错,对传入的Subscriber做了一个代理,把转换后的值传入。
这样就生成了一个代理的Subscriber

最后我们最外层的OnSubscribe对象对我们代理的Subscriber进行了调用。。
也就是

 @Override
public void call(Subscriber<? super String> subscriber) {
    //此处的subscriber就是被map包裹(wrapper)后的对象。
    subscriber.onNext("hello");
}

然后这个subscriber传入到内部,链式的通知,最后通知到我们在subscribe函数中定义的对象。

这时候要盗下扔物线大大文章的图
rxjavarxjava_8_d.gif

还不明白的各位,可以自己写一个Demo试一下。

下一章讲下RxJava中很重要的线程切换。

【迷之RxJava(三)—— 线程切换】

欢迎关注我Github 以及 weibo

最近在Android界,最火的framework大概就是RxJava了。
扔物线大大之前写了一篇文章 《给 Android 开发者的 RxJava 详解》,在我学习RxJava的过程中受益匪浅。经过阅读这篇文章后,我们来看下RxJava的源码,揭开它神秘的面纱。

这里准备分几篇文章写,为了能让自己有个喘口气的机会。

先来上个最最简单的,经典的Demo。

Demo

Observable.create(new Observable.OnSubscribe<String>() {
    @Override
    public void call(Subscriber<? super String> subscriber) {
        subscriber.onNext("hello");
    }
}).subscribe(new Subscriber<String>() {
    @Override
    public void onCompleted() {

    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onNext(String s) {
        Log.d("rx", s);
    }
});

这段代码产生的最终结果就是在Log里会出现hello。

看下这段代码的具体流程吧。
这里有2个函数createsubscribe,我们看看create里面干了啥。

OnSubscribe对象

public final static <T> Observable<T> create(OnSubscribe<T> f) {
    return new Observable<T>(hook.onCreate(f));
}
// constructor
protected Observable(OnSubscribe<T> f) {
    this.onSubscribe = f;
}

这里的hook是一个默认实现,里面不做任何事,就是返回f。我们看见create只是给ObservableonSubscribe赋值了我们定义的OnSubscribe

Subscriber对象

来看下subscribe这个函数做了什么事

public final Subscription subscribe(Subscriber<? super T> subscriber) {
    return Observable.subscribe(subscriber, this);
}

private static <T> Subscription subscribe(Subscriber<? super T> subscriber, Observable<T> observable) {
 // validate and proceed
    if (subscriber == null) {
        throw new IllegalArgumentException("observer can not be null");
    }
    if (observable.onSubscribe == null) {
        throw new IllegalStateException("onSubscribe function can not be null.");
        /*
         * the subscribe function can also be overridden but generally that's not the appropriate approach
         * so I won't mention that in the exception
         */
    }
    
    // new Subscriber so onStart it
    subscriber.onStart();
    
    /*
     * See https://github.com/ReactiveX/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls
     * to user code from within an Observer"
     */
    // if not already wrapped
    if (!(subscriber instanceof SafeSubscriber)) {
        // assign to `observer` so we return the protected version
        subscriber = new SafeSubscriber<T>(subscriber);
    }

    // The code below is exactly the same an unsafeSubscribe but not used because it would add a sigificent depth to alreay huge call stacks.
    try {
        // allow the hook to intercept and/or decorate
        hook.onSubscribeStart(observable, observable.onSubscribe).call(subscriber);
        return hook.onSubscribeReturn(subscriber);
    } catch (Throwable e) {
        // special handling for certain Throwable/Error/Exception types
        Exceptions.throwIfFatal(e);
        // if an unhandled error occurs executing the onSubscribe we will propagate it
        try {
            subscriber.onError(hook.onSubscribeError(e));
        } catch (OnErrorNotImplementedException e2) {
            // special handling when onError is not implemented ... we just rethrow
            throw e2;
        } catch (Throwable e2) {
            // if this happens it means the onError itself failed (perhaps an invalid function implementation)
            // so we are unable to propagate the error correctly and will just throw
            RuntimeException r = new RuntimeException("Error occurred attempting to subscribe [" + e.getMessage() + "] and then again while trying to pass to onError.", e2);
            // TODO could the hook be the cause of the error in the on error handling.
            hook.onSubscribeError(r);
            // TODO why aren't we throwing the hook's return value.
            throw r;
        }
        return Subscriptions.unsubscribed();
    }
}

我们看到,这里我们的subscriberSafeSubscriber包裹了一层。

if (!(subscriber instanceof SafeSubscriber)) {
    // assign to `observer` so we return the protected version
    subscriber = new SafeSubscriber<T>(subscriber);
}

然后开始执行工作流

hook.onSubscribeStart(observable, observable.onSubscribe).call(subscriber);
return hook.onSubscribeReturn(subscriber);

默认的hook只是返回我们之前定义的onSubscribe,这里调用的call方法就是我们在外面定义的

new Observable.OnSubscribe<String>() {
    @Override
    public void call(Subscriber<? super String> subscriber) {
        subscriber.onNext("hello");
    }
})

我们调用传入的subscriber对象的onNext方法,这里的subscriberSafeSubscriber
SafeScriber

public void onNext(T args) {
    try {
        if (!done) {
            actual.onNext(args);
        }
    } catch (Throwable e) {
        // we handle here instead of another method so we don't add stacks to the frame
        // which can prevent it from being able to handle StackOverflow
        Exceptions.throwIfFatal(e);
        // handle errors if the onNext implementation fails, not just if the Observable fails
        onError(e);
    }
}

actual就是我们自己定义的subscriber。 原来SafeSubscriber只是为了帮我们处理好异常,以及防止工作流的重复。

这是RxJava最最基本的工作流,让我们认识到他是怎么工作的。之后我们来讲讲其中的细节和其他神奇的内容。

【谜之RxJava (二) —— Magic Lift】

欢迎关注我Github 以及 weibo

哎呀呀,在杭州2015 Hackthon的现场,因为没有二维码签到功能,被吐槽low!这是我近期最丢脸的事啦~
于是回来就开始着手开发二维码相关的东西了。

一搜索Google和我们SegmentFault,发现在Android上,Googlezxing这个二维码的库比较受欢迎,好,那就是它了(我就是这么任性= =)

OK,看下zxing这个项目https://github.com/zxing/zxing
好像非常大啊,如何快速用起来呢?

答案就是

简化!

简化的话,带来的副作用就是适用性降低,比如在这个场景里面,我们不考虑横屏的情况,不考虑对摄像头进行过多的配置,不存在截图。

简化过程

我们看下这个项目有一个android目录,它里面其实就是条码扫描器这个app的开源代码,因为我们暂时不需要深究它是如何进行图像识别,如何帮我们从图像里解析出二维码的,所以我们就不研究core相关的内容(但是里面都是干货啊!图像识别的干货啊!)

clipboard.png
我们最主要就是参考这个里面的源码啦。

先把整个项目clone下来,然后把android这个源码导入到Android Studio里面去,经过小小的配置,就能搞定啦,直接运行我们就能看见这个App了。

先看下我们要做的工作是啥:

  1. 对摄像头进行管理(为了兼容老设备,我们要用即将被Google舍弃的android.hardware.camera类)。
  2. 对获取的一帧图片进行解析。
  3. 对解析的结果进行处理。

如果不带着问题或者目的去看源码的话,会非常没头绪,所以我们一定要记得我们想要做什么。

OK,先看第一个问题,我们需要对摄像头进行管理。我们可以看到目录中有三个文件和摄像机有关

clipboard.png
就是这三个文件啦。

用途
CameraConfigurationManager这个类主要用于对摄像头进行一些配置,包括旋转角度、预览图尺寸等等
CameraConfigurationUtils工具类,在CameraConfigurationManager调用,
CameraManager控制摄像头的生命周期,获取预览尺寸,生成原始数据发送给解析器

在这里,我把CameraConfigurationUtils拷贝过来,另外两个类合成到CameraManager里面,在初始化的时候,做好配置。

摄像头的管理(横屏改竖屏)

因为原先zxing给的demo是横屏的,这里要改成竖屏,就需要做几个配置。

getFramingRectInPreview

getFramingRectInPreview这个函数中,cameraResolutionscreenResolution方向不对,所以

rect.left = rect.left * cameraResolution.y / screenResolution.x;
rect.right = rect.right * cameraResolution.y / screenResolution.x;
rect.top = rect.top * cameraResolution.x / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;

这里需要把方向换一下。

binary data

还需要更改的地方,就是在获取帧预览中的原始数据,需要进行一个旋转,因为zxing原先是对横向的图片一行一行读取的,如果我们给予纵向的数据,就必须旋转数据,或者更改读取算法。 这里更改数据可能会更加方便,
buildLuminanceSource中,更改:

byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++)
        rotatedData[x * height + height - y - 1] = data[x + y * width];
}
int tmp = width;
width = height;
height = tmp;

最后记得把摄像头的预览旋转改成90°即可。

 camera.setDisplayOrientation(90);

附上这部分代码(比较长)

public class CameraManager {

    private static final int MIN_FRAME_WIDTH = 240;
    private static final int MIN_FRAME_HEIGHT = 240;

    // 这里修正扫描大小
    private static final int MAX_FRAME_WIDTH = 675; // = 5/8 * 1920
    private static final int MAX_FRAME_HEIGHT = 1200; // = 5/8 * 1080

    private Context mContext;
    private Point mScreenResolution;
    private Point mCameraResolution;
    private Point mBestPreviewSize;
    private Point mPreviewSizeOnScreen;

    private Rect mFramingRectInPreview;
    private Rect mFramingRect;

    private Camera mCamera;
    private PreviewCallback mPreviewCallback;
    private AutoFocusManager mAutoFocusManager;

    public CameraManager(Context context) {
        mContext = context;
        mPreviewCallback = new PreviewCallback(this);
    }

    public void openDriver(Camera camera, SurfaceHolder holder) {
        mCamera = camera;

        Camera.Parameters parameters = camera.getParameters();

        CameraConfigurationUtils.setBarcodeSceneMode(parameters);
        CameraConfigurationUtils.setFocus(parameters,
                true,
                true,
                false);

        Point theScreenResolution = new Point();
        Rect rect = holder.getSurfaceFrame();
        theScreenResolution.set(rect.height(), rect.width());
        mScreenResolution = theScreenResolution;
        mCameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, mScreenResolution);
        mBestPreviewSize = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, mScreenResolution);

        boolean isScreenPortrait = mScreenResolution.x < mScreenResolution.y;
        boolean isPreviewSizePortrait = mBestPreviewSize.x < mBestPreviewSize.y;

        if (isScreenPortrait == isPreviewSizePortrait) {
            mPreviewSizeOnScreen = mBestPreviewSize;
        } else {
            mPreviewSizeOnScreen = new Point(mBestPreviewSize.y, mBestPreviewSize.x);
        }

        parameters.setPreviewSize(mBestPreviewSize.x, mBestPreviewSize.y);
        camera.setParameters(parameters);
        camera.setDisplayOrientation(90);

        Camera.Parameters afterParameters = camera.getParameters();
        Camera.Size afterSize = afterParameters.getPreviewSize();
        if (afterSize != null && (mBestPreviewSize.x != afterSize.width || mBestPreviewSize.y != afterSize.height)) {
            mBestPreviewSize.x = afterSize.width;
            mBestPreviewSize.y = afterSize.height;
        }

    }

    public synchronized Rect getFramingRect() {
        if (mFramingRect == null) {
            Point screenResolution = mScreenResolution;
            if (screenResolution == null) {
                // Called early, before init even finished
                return null;
            }

            int width = findDesiredDimensionInRange(screenResolution.x, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
            int height = findDesiredDimensionInRange(screenResolution.y, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);

            int leftOffset = (screenResolution.x - width) / 2;
            int topOffset = (screenResolution.y - height) / 2;
            mFramingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
        }
        return mFramingRect;
    }

    public Point getCameraResolution() {
        return mCameraResolution;
    }

    public synchronized Rect getFramingRectInPreview() {
        if (mFramingRectInPreview == null) {
            Rect framingRect = getFramingRect();
            if (framingRect == null) {
                return null;
            }
            Rect rect = new Rect(framingRect);
            Point cameraResolution = mCameraResolution;
            Point screenResolution = mScreenResolution;
            if (cameraResolution == null || screenResolution == null) {
                // Called early, before init even finished
                return null;
            }
            rect.left = rect.left * cameraResolution.y / screenResolution.x;
            rect.right = rect.right * cameraResolution.y / screenResolution.x;
            rect.top = rect.top * cameraResolution.x / screenResolution.y;
            rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
            mFramingRectInPreview = rect;
        }
        return mFramingRectInPreview;
    }

    private static int findDesiredDimensionInRange(int resolution, int hardMin, int hardMax) {
        int dim = 5 * resolution / 8; // Target 5/8 of each dimension
        if (dim < hardMin) {
            return hardMin;
        }
        if (dim > hardMax) {
            return hardMax;
        }
        return dim;
    }

    /**
     * A factory method to build the appropriate LuminanceSource object based on the format
     * of the preview buffers, as described by Camera.Parameters.
     *
     * @param data A preview frame.
     * @param width The width of the image.
     * @param height The height of the image.
     * @return A PlanarYUVLuminanceSource instance.
     */
    public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
        Rect rect = getFramingRectInPreview();
        if (rect == null) {
            return null;
        }

        byte[] rotatedData = new byte[data.length];
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++)
                rotatedData[x * height + height - y - 1] = data[x + y * width];
        }
        int tmp = width;
        width = height;
        height = tmp;

        // Go ahead and assume it's YUV rather than die.
        return new PlanarYUVLuminanceSource(rotatedData, width, height, rect.left, rect.top,
                rect.width(), rect.height(), false);
    }

    /**
     * A single preview frame will be returned to the handler supplied. The data will arrive as byte[]
     * in the message.obj field, with width and height encoded as message.arg1 and message.arg2,
     * respectively.
     *
     * @param handler The handler to send the message to.
     * @param message The what field of the message to be sent.
     */
    public synchronized void requestPreviewFrame(Handler handler, int message) {
        if (mCamera != null) {
            mPreviewCallback.setHandler(handler, message);
            mCamera.setOneShotPreviewCallback(mPreviewCallback);
        }
    }

    public synchronized void startPreview() {
        mCamera.startPreview();
        mAutoFocusManager = new AutoFocusManager(mContext, mCamera);
    }

    public synchronized void stopPreview() {
        if (mAutoFocusManager != null) {
            mAutoFocusManager.stop();
            mAutoFocusManager = null;
        }
        if (mCamera != null) {
            mCamera.stopPreview();
        }
        mPreviewCallback.setHandler(null, 0);

    }

    public synchronized void closeDriver() {
        mCamera.release();
        mCamera = null;
    }
}

获取图像数据

Camera提供这么一个函数:setOneShotPreviewCallback,看名字就知道,它是会在某个时间点,回调你给的接口,然后传入一个二进制的图像数组,让你去解析,这正是我们想要的东西,所以我们看下这个PreviewCallback,它只有一个函数,

@Override
public void onPreviewFrame(byte[] data, Camera camera) {
}

很显然,第一个参数就是图像数组了,我们看看zxing里面是怎么处理这个数据的。

解析数据

@Override
public void onPreviewFrame(byte[] data, Camera camera) {
    Point cameraResolution = mCameraManager.getCameraResolution();
    Handler thePreviewHandler = mPreviewHandler;
    if (cameraResolution != null && thePreviewHandler != null) {
        Message message = thePreviewHandler.obtainMessage(mPreviewMessage, cameraResolution.x,
                cameraResolution.y, data);
        message.sendToTarget();
        mPreviewHandler = null;
    } else {
    }
}

zxing是发送到另外一个handler,也就是说,这个解析数据的过程比较浪费时间,所以要开线程来解决这个事。
我们跟踪这个previewHanlder可以发现,它是一个DecodeHandler,其中有个重要的decode函数

private void decode(byte[] data, int width, int height) {
    long start = System.currentTimeMillis();
    Result rawResult = null;
    PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);
    if (source != null) {
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        try {
            rawResult = multiFormatReader.decodeWithState(bitmap);
        } catch (ReaderException re) {
            // continue
        } finally {
            multiFormatReader.reset();
        }
    }

    Handler handler = activity.getHandler();
    if (rawResult != null) {
        // Don't log the barcode contents for security.
        long end = System.currentTimeMillis();
        Log.d(TAG, "Found barcode in " + (end - start) + " ms");
        if (handler != null) {
            Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult);
            Bundle bundle = new Bundle();
            bundleThumbnail(source, bundle);
            message.setData(bundle);
            message.sendToTarget();
        }
    } else {
        if (handler != null) {
            Message message = Message.obtain(handler, R.id.decode_failed);
            message.sendToTarget();
        }
    }
}

这里用到我们刚刚重写的buildLuminanceSource这个函数了,可以理解这个函数是对我们的原始数据做一个包装,来给解析器读取。

解析器的配置我们可以看看DecodeThread这个类(专门用于解析图像的线程)

...
 decodeFormats = EnumSet.noneOf(BarcodeFormat.class);
decodeFormats.addAll(DecodeFormatManager.PRODUCT_FORMATS);
decodeFormats.addAll(DecodeFormatManager.INDUSTRIAL_FORMATS);
decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
mHints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);

这里我们可以为我们的扫描器加更多格式的支持(比如条形码、二维码、XX码)

zxing是使用List来管理这些解析器,然后每次读取数据,都经过这些解析器过滤一遍,如果解析成功就有结果,否则就没有结果。

最后我们跟踪R.id.decode_success找到CaptureActivityHandler
这里的handleMessage中有

  case R.id.decode_succeeded:
        state = State.SUCCESS;
        Bundle bundle = message.getData();
        Bitmap barcode = null;
        float scaleFactor = 1.0f;
        if (bundle != null) {
            byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP);
            if (compressedBitmap != null) {
                barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, null);
                // Mutable copy:
                barcode = barcode.copy(Bitmap.Config.ARGB_8888, true);
            }
            scaleFactor = bundle.getFloat(DecodeThread.BARCODE_SCALED_FACTOR);
        }
        activity.handleDecode((Result) message.obj, barcode, scaleFactor);
        break;

我们读取Result对象的text成员变量就是我们想要的二维码信息了!最后的工作当然是解析我们的二维码中的URI,然后进行后续的工作啦~

PS: 附带二维码扫描的SegmentFault for Android版本 即将上线!!

欢迎关注我Github 以及 weibo