我正在重构MVP中的一个旧应用程序。我已经重构了大部分逻辑,现在我坚持跟随一个。 在我的一个活动中,我已经实现了PhoneStateLister,如下所示。
private class CallStateListener extends PhoneStateListener {
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
deactivateSpeaker();
if (callCount == 0) {
doCall();
} else {
checkForHangUp();
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
checkAutoSpeaker();
break;
case TelephonyManager.CALL_STATE_RINGING:
break;
default:
break;
}
}
private void checkAutoSpeaker() {
if (preferenceManager.isAutoSpeaker()) activateSpeaker();
}
private void activateSpeaker() {
final Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
audioManager.setMode(AudioManager.MODE_IN_CALL);
audioManager.setSpeakerphoneOn(true);
}
}, 1000);
}
private void deactivateSpeaker() {
audioManager.setMode(AudioManager.MODE_NORMAL); //Deactivate loudspeaker
}
}
我很难决定将这个逻辑放在MVP中的哪个位置。 Activity应该处理这个PhoneStateListener还是应该让presenter处理这个? 请帮我解决这个问题。感谢。
答案 0 :(得分:0)
在 MVP 模式中设置侦听器总是有点棘手。我很确定您的问题没有“正确”或“错误”的答案,因为它在很大程度上取决于您的个人偏好和架构风格。但按照我的一般规则,将尽可能多的业务逻辑移动到演示者并保持其他所有内容(这也包括侦听器)尽可能愚蠢总是很明智。
这就是为什么我建议您在侦听器类中添加界面,并在那里保留对演示者的(弱)引用。 侦听器在该示例中唯一做的就是侦听状态更改,然后将信息转发到演示者(分发的地方)。
public class CallStateListener extends PhoneStateListener {
private final WeakReference<CallStateListenerInterface> presenterInterface;
public CallStateListener(CallStateListenerInterface presenterInterface){
this.presenterInterface = new WeakReference<>(presenterInterface);
}
public void onCallStateChanged(int state, String incomingNumber){
presenterInterface.get().onCallStateListenerCallStateChanged(state, incomingNumber);
}
public interface CallStateListenerInterface {
void onCallStateListenerCallStateChanged(int state, String incomingNumber);
}
}
演示者需要实现 CallStateListenerInterface (当然),并在调用状态发生变化时处理所有业务逻辑。
public class myPresenter implements MVPInterface, CallStateListenerInterface {
/*your business logic*/
public void onCallStateListenerCallStateChanged(int state, String incomingNumber){
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
deactivateSpeaker();
if (callCount == 0) {
doCall();
} else {
checkForHangUp();
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
checkAutoSpeaker();
break;
case TelephonyManager.CALL_STATE_RINGING:
break;
default:
break;
}
}
}
此处需要与视图进行交互的所有内容都会被调用,但请将这些调用保留为简单的一行说明,并为演示者决定是否复杂。
如果您不想为每个侦听器类添加新的接口,您也可以将整个 MVPInterface 传递给 CallStateChangedListener (并在合同的中心位置声明所有内容。)
如果以这种方式实现监听器,那么您将拥有一个非常干净的架构,应该可以轻松测试。 我希望这是有帮助的,如果您有任何问题,请告诉我:))
小额替代实施:
正如评论者所指出:在我的原始建议中,演示者从TelephonyManager类访问静态电话状态整数。这不是最干净的设计,因为演示者应该与任何Android特定代码完全分开。 由于没有真正的超级干净方法来解决这个问题,我只能建议从TelephonyManager状态到保留在演示者中的(自定义)电话状态的映射(或者更好的是:静态常量文件)。 基本上这意味着CallStateListener中的onCallStateChanged看起来像这样:
public void onCallStateChanged(int state, String incomingNumber){
int stateValue = myPresenter.PRESENTER_CALL_STATE_IDLE;
if (state == TelephonyManager.CALL_STATE_RINGING){
stateValue = myPresenter.PRESENTER_CALL_STATE_RINGING;
} else if (state == TelephonyManager.CALL_STATE_OFFHOOK){
stateValue = myPresenter.PRESENTER_CALL_STATE_OFFHOOK;
}
presenterInterface.get().onCallStateListenerCallStateChanged(stateValue , incomingNumber);
}
通过此实现,您可以将TelephonyManager状态移出演示者并使用您自己定义的状态。 (再一次:我真的不确定这是否值得付出努力,但我想至少提供替代方案)
答案 1 :(得分:0)
我相信你应该把听众留在你的片段/活动部分。 我想到了两个解决方案。第一个是将开关/案例留在听众中,并将一些逻辑移到演示者中:
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
mPresenter.onCallStateIDLE();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
mPresenter.onCallStateOffHook();
break;
case TelephonyManager.CALL_STATE_RINGING:
mPresenter.onCallStateRinging();
break;
default:
break;
}
}
并在您的演示者中:
void onCallStateIDLE(){
mView.deactivateSpeaker();
if (callCount == 0) {
mView.doCall();
} else{
mView.checkForHangUp();
}
}
另一个选项应该是将开关/盒子移动到演示者中。 棘手的部分应该是如何处理TelephonyManager常量。如您所知,您应该将演示者保留为纯Java代码。
你应该有一个View界面,你可以做这样的事情
interface MyActivityContract{
interface View{
int CALL_STATE_IDLE = TelephonyManager.CALL_STATE_IDLE;
//etc etc the same with the rest of constants
}
interface Presenter{}
}
然后在你的听众中,你应该引用你的演示者
private class CallStateListener extends PhoneStateListener {
public void onCallStateChanged(int state, String incomingNumber) {
mPresenter.onCallStateChanged(state, incomingNumber);
}
}
最后你的演示者可能是这样的
public void onCallStateChanged(int state, int incomingNumber){
switch (state) {
case MyActivityContract.View.CALL_STATE_IDLE:
mView.deactivateSpeaker();
if (callCount == 0) {
mView.doCall();
} else {
mView.checkForHangUp();
}
break;
case MyActivityContract.View.CALL_STATE_OFFHOOK:
if (preferenceManager.isAutoSpeaker()){
mView.activateSpeaker();
}
break;
case MyActivityContract.View.CALL_STATE_RINGING:
break;
default:
break;
}
}
通过这种方式,您的演示者中没有任何Android引用,但是您的合同中有它...