MVP是什么?

Model-View-Presenter架构,由MVC演化而来,相比于MVC,View与Model不再直接通信,两者之间的所有交互均发生在Presenter
在这里插入图片描述

  • Model是数据模型层,负责数据的存储和管理
  • View是UI显示层,负责从界面获取输入数据并更新系统处理后的数据。
  • Presenter是逻辑处理层,负责系统逻辑功能的处理,起到View和Model之间的桥梁作用。

MVP应用

在这里插入图片描述
Activity(Fragment)组成View层含有对Presenter的引用,用于将UI输入的数据传递到逻辑层处理

IPresenter接口定义了系统功能,含有ICallback接口的引用实现对View层的回调,当页面需要更新时通知View层进行更新操作。

Presenter层实现了IPresenter接口,对Model层数据进行操作,实现具体的代码功能逻辑

基于Activity、Service实现——播放器实例

权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

activity_main.xml布局

布局有一个进度条和两个按钮(播放/暂停、停止)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <SeekBar
        android:id="@+id/progress"
        android:max="100"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/play_or_puase"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="播放" />

        <Button
            android:id="@+id/stop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="停止" />
    </LinearLayout>

</LinearLayout>

View层接口及实现

创建播放状态回调接口,用于通知UI更新

public interface IPlayViewCallBack {

    void onPlayStateChange(int state);

    void onSeekChange(int seek);
}

Activity作为View层更新根据状态更新UI

  • 通过bindService获取P层引用
  • 将V层回调接口注册到P层
  • V层通过点击事件将数据传给P层处理
  • P层处理完数据后,利用回调返回结果让V层更新UI
public class MainActivity extends AppCompatActivity implements IPlayViewCallBack {

    private static final String TAG = "MainActivity";
    private SeekBar mSeekBar;
    private Button mPlayOrPause;
    private Button mStop;
    private PlayConnection mPlayConnection;
    private IPlayPresenter mPlayPresenter;
    private boolean isUserTouchProgress;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initEvent();
        initService();
    }

    private void initService() {
        startService(new Intent(this, PlayService.class));
        Intent intent = new Intent(this, PlayService.class);
        mPlayConnection = new PlayConnection();
        bindService(intent, mPlayConnection, BIND_AUTO_CREATE);
    }

    private class PlayConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mPlayPresenter = (IPlayPresenter) service;
            mPlayPresenter.registerCallback(MainActivity.this);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mPlayPresenter = null;
        }
    }

    private void initEvent() {
        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                isUserTouchProgress = true;
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                int touchProgress = seekBar.getProgress();
                if (mPlayPresenter != null) {
                    mPlayPresenter.seekTo(touchProgress);
                }
                isUserTouchProgress = false;
            }
        });
        mPlayOrPause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if ((ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) {
                    ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
                }
                if (mPlayPresenter != null) {
                    mPlayPresenter.playOrPause();
                }
            }
        });
        mStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mPlayPresenter != null) {
                    mPlayPresenter.stop();
                }
            }
        });
    }

    private void initView() {
        mSeekBar = (SeekBar) findViewById(R.id.progress);
        mPlayOrPause = (Button) findViewById(R.id.play_or_puase);
        mStop = (Button) findViewById(R.id.stop);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mPlayPresenter.unregisterCallback();
        unbindService(mPlayConnection);
        mPlayConnection = null;
    }

    @Override
    public void onPlayStateChange(int state) {
        switch (state) {
            case PLAY_STATE_PLAY:
                mPlayOrPause.setText("暂停");
                break;
            case PLAY_STATE_PAUSE:
            case PLAY_STATE_STOP:
                mPlayOrPause.setText("播放");
                break;
        }
    }

    @Override
    public void onSeekChange(int seek) {
        Log.d(TAG, "Thread.currentThread().getName( = " + Thread.currentThread().getName());
        Log.d(TAG, "seek = " + seek);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (!isUserTouchProgress) {
                    mSeekBar.setProgress(seek);
                }
            }
        });
    }
}

Tips:

  • isUserTouchProgress变量避免边拖动边更新引起抖动,当用户触摸时为true不更新进度条, 拖动完了先更新拖动进度,再置false让其自动更新播放进度
  • onSeekChange不调用runOnUiThread也不会崩溃,因为Android允许progressBar和surface在子线程中更新

Presenter层接口及实现

创建播放控制接口,用来处理逻辑事件

public interface IPlayPresenter {

    int PLAY_STATE_PLAY = 1;
    int PLAY_STATE_PAUSE = 2;
    int PLAY_STATE_STOP = 3;

    void registerCallback(IPlayViewCallBack callBack);

    void unregisterCallback();

    void playOrPause();

    void stop();

    void seekTo(int seek);
}

PlayPresenter继承Binder实现IPlayPresenter,用于在服务中返回

public class PlayPresenterImpl extends Binder implements IPlayPresenter {

    private static final String TAG = "PlayPresenterImpl";
    private IPlayViewCallBack mCallback;
    private int mCurrentState = PLAY_STATE_STOP;
    private MediaPlayer mMediaPlayer;
    private Timer mTimer;
    private SeekTimeTask mSeekTimeTask;

    @Override
    public void registerCallback(IPlayViewCallBack callBack) {
        this.mCallback = callBack;
    }

    @Override
    public void unregisterCallback() {
        mCallback = null;
    }

    @Override
    public void playOrPause() {
        if (mCurrentState == PLAY_STATE_STOP) {
            try {
                if (mMediaPlayer == null) {
                    mMediaPlayer = new MediaPlayer();
                    mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                }
                mMediaPlayer.setDataSource("/sdcard/Music/1.mp3");
                mMediaPlayer.prepare();
                mMediaPlayer.start();
                mCurrentState = PLAY_STATE_PLAY;
                startTimer();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if (mCurrentState == PLAY_STATE_PLAY) {
            if (mMediaPlayer != null) {
                mMediaPlayer.pause();
                mCurrentState = PLAY_STATE_PAUSE;
                stopTimer();
            }
        } else if (mCurrentState == PLAY_STATE_PAUSE) {
            if (mMediaPlayer != null) {
                mMediaPlayer.start();
                mCurrentState = PLAY_STATE_PLAY;
                startTimer();
            }
        }
        if (mCallback != null) {
            mCallback.onPlayStateChange(mCurrentState);
        }
    }

    @Override
    public void stop() {
        if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
            mMediaPlayer.stop();
            mCurrentState = PLAY_STATE_STOP;
            stopTimer();
            if (mCallback != null) {
                mCallback.onPlayStateChange(mCurrentState);
            }
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    @Override
    public void seekTo(int seek) {
        if (mMediaPlayer != null) {
            int targetSeek = (int) (seek * 1.0f / 100 * mMediaPlayer.getDuration());
            mMediaPlayer.seekTo(targetSeek);
        }
    }

    private void startTimer() {
        if (mTimer == null) {
            mTimer = new Timer();
        }
        if (mSeekTimeTask == null) {
            mSeekTimeTask = new SeekTimeTask();
        }
        mTimer.schedule(mSeekTimeTask, 0, 1000);
    }

    private void stopTimer() {
        if (mTimer != null) {
            mTimer.cancel();
            mTimer = null;
        }
        if (mSeekTimeTask != null) {
            mSeekTimeTask.cancel();
            mSeekTimeTask = null;
        }
    }

    private class SeekTimeTask extends TimerTask {

        @Override
        public void run() {
            if (mMediaPlayer != null) {
                int currentPosition = mMediaPlayer.getCurrentPosition();
                Log.d(TAG, "run: currentPosition = " + currentPosition);
                Log.d(TAG, "run: mMediaPlayer.getDuration() = " + mMediaPlayer.getDuration());
                int duration = (int) (currentPosition * 1.0f / mMediaPlayer.getDuration() * 100);
                Log.d(TAG, "run: duration = " + duration);
                if (mCallback != null) {
                    mCallback.onSeekChange(duration);
                }
            }
        }
    }
}

Tips:

  • 创建SeekTimeTask开启线程获取当前进度,传入Timer每隔1000ms更新进度条

View-Presenter桥梁

将View层和Presenter层通过Service绑定返回

public class PlayService extends Service {

    private PlayPresenterImpl mPlayPresenterImpl;

    @Override
    public void onCreate() {
        super.onCreate();
        if (mPlayPresenterImpl == null) {
            mPlayPresenterImpl = new PlayPresenterImpl();
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mPlayPresenterImpl;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mPlayPresenterImpl = null;
    }
}
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐