android - 如何用 MVP 实现 PhoneStateListener

标签 android android-activity mvp android-mvp presenter

我正在用 MVP 重构我的一个旧应用。我已经重构了大部分逻辑,现在我坚持使用以下逻辑。 在我的一项 Activity 中,我实现了 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 应该处理这个? 请帮我解决问题。谢谢。

最佳答案

MVP 模式中设置listeners 总是有点棘手。我很确定您的问题没有“正确”“错误”的答案,因为这在很大程度上取决于您的个人喜好和架构风格。但正如我的一般规则,将尽可能多的业务逻辑转移到 presenter 并让其他一切(这也包括 listeners)尽可能地愚蠢总是明智的。

这就是为什么我建议您向您的listener 类添加一个interface 并在那里保留对presenter 的(弱)引用.在该示例中,listener 所做的唯一一件事就是监听状态变化,然后将信息转发给 presenter(信息在此处分发)。

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);
    }
}

presenter 需要实现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; 
        }
    }
}

从这里开始,所有需要与view 交互的东西都将被调用,但是将这些调用保持在简单的一行指令中,让所有复杂的事情留给presenter 来决定。

如果您不想为每个监听器类添加新的接口(interface),您也可以将整个MVPInterface 传递给CallStateChangedListener (并在您的契约(Contract)的中心位置声明所有内容)。

如果您以这种方式实现监听器,您将获得一个非常干净的架构,应该很容易测试。 我希望这对您有所帮助,如果您有任何问题,请告诉我:)

小型替代实现:

正如评论者所指出的:在我最初的建议中,演示者从 TelephonyManager 类访问静态电话状态整数。这不是最干净的设计,因为演示者应该与任何 Android 特定代码完全分开。 由于没有真正 super 干净的方法来解决这个问题,我只能建议从 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 状态移出演示器并使用您自己定义的状态。 (再一次:我真的不确定这是否值得付出努力,但我想至少提供替代方案)

关于android - 如何用 MVP 实现 PhoneStateListener,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49165710/

相关文章:

java - 服务在 android 8 (oreo) 下意外停止

java - Android Studio 其他Activity打不开

java - 在获取 NPE 的 Activity 中调用 Fragment 方法

model-view-controller - 为数据库开发人员解释 MVVM、MVC、MVP

iOS - MVP 设计中的 UITableView 委托(delegate)和数据源

android - 如何将一些 "metadata"附加到 Android Listview 项目?

android - 如何使用自定义列表适配器显示 listView 为空

java - 开始新 Activity 时应用程序崩溃

MVVM - View 是否应该引用 Presenter/ViewModel?

android - 在 Android 的 ViewPager2 中检测越界滑动