Feb 20

ReactNative的BackAndroid使用

Lrdcq , 2016/02/20 11:39 , 程序 , 閱讀(4613) , Via 本站原創
BackAndroid是在ReactNative中专门处理安卓的物理按键back的类。类的用法很简单...但是感觉api设计不咋地合理,无论用什么姿势使用坑都挺大的。

官方文档(http://facebook.github.io/react-native/docs/backandroid.html#content)给的例子,这个类包含了3个api,分别是:
- static exitApp()//退出app
- static addEventListener(eventName: BackPressEventName, handler: Function)//添加事件监听
- static removeEventListener(eventName: BackPressEventName, handler: Function)//移除事件监听

一段典型的BackAndroid使用是这样的:
BackAndroid.addEventListener('hardwareBackPress', function() {
     if (!this.onMainScreen()) {
       this.goBack();
       return true;
     }
     return false;
});

然而很遗憾,这一段代码本身就是有bug的。这是一段运行在一个组件中的代码,如果自己在主屏上,者尝试调用this.goBack,否则不拦截back操作。而显然,这个时候,把自己给back掉,然而这个回调事件并没有注销掉,BackAndroid持有了这个匿名函数,匿名函数的上下文持有了this这个组件,控制不当,内存泄漏。因此,大坑,请务必记得在必要的时候,removeEventListener掉之前注册的handler。

好吧,那我们就remove掉把,看看这个api,老觉得很奇怪,为何remove也要传个handler呢?几个意思?嗯,不管了,我就先给空的吧。于是,代码写成了这样:
BackAndroid.addEventListener('hardwareBackPress', function() {
     //do something
     BackAndroid.removeEventListener('hardwareBackPress',null);
     return true;
});

泡泡看,草,没什么卵用,这api到底是几个意思。翻翻代码一看,我们才知道,在这个类的内部实现,是直接以handler作为关键字储存在集合(set)里面的,eventName压根没有用到~~~~。什么鬼,所以说,又是大坑,我们需要用相同的handler来添加和删除listener。只有写成这样了:
_handler() {
     //do something
     this.dosomething();
     BackAndroid.removeEventListener('hardwareBackPress', this._handler);
     return true;
}
BackAndroid.addEventListener('hardwareBackPress', this._handler);

作为一个私有方法放在类里边,然后在进行调用,这样总可以了吧。...擦,this.dosomething()报错,找不到this,想想在回调里面上下文并没有this,需要强行绑定上去,于是代码又改成了这样:
_handler() {
     //do something
     this.dosomething();
     BackAndroid.removeEventListener('hardwareBackPress', this._handler.bind(this));
     return true;
}
BackAndroid.addEventListener('hardwareBackPress', this._handler.bind(this));

。。。。不对,运行虽然完全正确,但是handler确实remove不掉。仔细检查数据后发现,对方法执行bind操作后吐出来的新东西总是不一样的(仔细想想这是理所当然的咯)。所以在set进行移除判断的时候会出错....最终,我们的代码变成了这样:
_handler=_dealHandler.bind(this)
_dealHandler() {
     //do something
     this.dosomething();
     BackAndroid.removeEventListener('hardwareBackPress', this._handler);
     return true;
}
BackAndroid.addEventListener('hardwareBackPress', this._handler);

总之,我们要做的要包括:
1.添加和移除监听器成对的出现。
2.添加和移除使用完完全全相同的handler。
3.注意handler闭包。

处理好这些坑,使用这个sb一样的BackAndroid才能游刃有余。
logo