惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tenable Blog
阮一峰的网络日志
阮一峰的网络日志
S
Schneier on Security
A
Arctic Wolf
Latest news
Latest news
C
Check Point Blog
S
SegmentFault 最新的问题
T
Tor Project blog
B
Blog RSS Feed
The GitHub Blog
The GitHub Blog
爱范儿
爱范儿
腾讯CDC
C
CERT Recently Published Vulnerability Notes
A
About on SuperTechFans
U
Unit 42
L
LINUX DO - 热门话题
罗磊的独立博客
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
MyScale Blog
MyScale Blog
D
Docker
Blog — PlanetScale
Blog — PlanetScale
GbyAI
GbyAI
V
Vulnerabilities – Threatpost
月光博客
月光博客
aimingoo的专栏
aimingoo的专栏
WordPress大学
WordPress大学
Security Latest
Security Latest
博客园_首页
C
Cybersecurity and Infrastructure Security Agency CISA
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
AI
AI
H
Hacker News: Front Page
C
CXSECURITY Database RSS Feed - CXSecurity.com
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Google DeepMind News
Google DeepMind News
T
Tailwind CSS Blog
TaoSecurity Blog
TaoSecurity Blog
博客园 - 司徒正美
云风的 BLOG
云风的 BLOG
S
Securelist
Engineering at Meta
Engineering at Meta
D
DataBreaches.Net
P
Privacy & Cybersecurity Law Blog
T
Threat Research - Cisco Blogs
V2EX - 技术
V2EX - 技术
Vercel News
Vercel News
N
News and Events Feed by Topic
Security Archives - TechRepublic
Security Archives - TechRepublic
S
Secure Thoughts

音视频开发进阶

音视频教程-第三节 音视频教程-第二节 真的,AI 可能就是新时代的信息差 充值 Cursor 之后,工作有了哪些变化?🤔 个人'蒸馏'大模型能做哪些有意思的事情 DeepSeek 大模型在 Mac 上的部署和运行 音视频教程-第一节 【WebRTC 专栏】-- Android 开发集成 WebRTC 库的几种方式 【WebRTC 专栏】-- 在 Mac M1 等系列芯片编译和开发 WebRTC-Android 库 Meta Llama3 大模型在 Mac 上的部署和运行 iOS VideoToolBox 解码 HEVC Open-GOP 视频的问题排查 Flutter 状态管理之 InheritedWidget 使用和分析 用 ChatGPT 回答技术问题怎么样 ? 音视频开发系统入门大致路线 UE 4.27 添加自定义 ShadingModel 用 UE4 虚幻引擎做个捏脸小功能~~ UE4 材质练习 之 凹凸贴图偏移的使用 UE4 材质练习系列基础 OpenGL上下文创建以及共享机制 007 | 播放器系列专栏-解析 MP4 文件读取信息 006 | 播放器系列专栏-在 Mac 上查看 MP4 格式信息 干货 | 快速抽取缩略图是怎么练成的? 关于直播、WebRTC、FFmpeg 的那些事 005 | 播放器系列专栏-在 Windows 上查看 MP4 格式信息 将音视频中的花屏、绿屏、黑屏问题一网打尽 关于音视频里面的解码帧率和渲染帧率 004 | 播放器系列专栏-认识MP4视频(下) 003 | 播放器系列专栏-认识MP4视频(上) 入门或者转行音视频,应该要怎么做? H264视频文件如何缩放分辨率 002 | 播放器系列专栏-FFmpeg依赖库的配置 001 | 播放器系列专栏-关于播放器项目的一个小实践 Seek策略以及在有B帧情况下的处理 目前流媒体开发工程师工作内容主要是什么? 一个音视频领域专业问答的小圈子 干货收藏 || Vulkan Game Engine 视频教程 音视频春节假期内卷指南(实操) Vulkan 在 FFmpeg 中的支持 Windows 下 FFmpeg 和 LibX264 的编译和配置 Metal 开发 | 使用 C++ 进行接口调用 音视频开发工作经验分享 || 视频版 FFmpeg 调用 MediaCodec 硬解码到 Surface 上 代码吸猫 | 用 OpenGL 图像渲染的养猫计划 百倍变速--解码到底能不能丢 非参考帧 ?FFmpeg 有话说!!! 老生常谈-FFmpeg 的编译问题轻松搞定 FFmpeg 调用 Android MediaCodec 进行硬解码(附源码) 【WebRTC 专栏】--创建相机预览 Unity Shader 光照基础之 Half Lambert 光照模型 Unity Shader 光照基础之Lambert光照模型 Unity Shader 光照基础内容 Unity Shader 显示一张图片纹理 UnityShader 的基本概念 Unity 物体的基本操作 C++ 模板系列小结07-尾置返回类型 C++ 模板系列小结06-可变参数模板特性 C++ 中的多线程的使用和线程池建设 C++ 模板系列小结05-模板类型作为模板参数 C++ 模板系列小结04-类模板中的成员模板 C++ 模板系列小结03-在模板中指定变量类型 C++ 模板系列小结02-非类型模板参数 C++ 模板系列小结01-函数模板和类模板 从零打造渲染引擎系列01-什么是渲染引擎 iOS开发 - 在 Swift 中去调用 C/C++ 代码 2021 技术新番 - 从零打造渲染引擎系列 iOS 音视频开发的一些基础准备工作 音视频交流群又来啦~~~ 【WebRTC 专栏】WebRTC & Android 开发学习环境搭建~ 【喜大普奔】域名终于备案通过啦 Shader 优化 | OpenGL 绘制网格效果 【音视频连载-011】第二季 FFmpeg 一层一层获取文件信息 KodeLife | Shader 实时编辑预览的强大工具使用实践 推荐几个堪称教科书级别的 Android 音视频入门项目 【音视频连载-010】第二季 FFmpeg 日志打印 【音视频连载-008】基础学习篇-SDL 播放 PCM 音频文件(下) 【音视频连载-007】基础学习篇-SDL 播放 PCM 音频文件(上) 【音视频连载-006】基础学习篇-SDL 播放 YUV 视频文件 【音视频连载-005】基础学习篇-SDL 加载 YUV 文件并显示 【音视频连载-004】基础学习篇-SDL 加载图片并显示 【音视频连载-003】基础学习篇-SDL 消息循环和事件响应 【音视频连载-002】基础学习篇-SDL 创建窗口并显示颜色 【音视频连载-001】基础学习篇- SDL 介绍以及工程配置 LearnOpenGL 源码在 MAC 上的编译与调试 2019 年终总结与回顾 Android NDK 开发的免费技术视频来啦~~ OpenGL 实现视频编辑中的转场效果 OpenGL 实践之贝塞尔曲线绘制 图像库 libjpeg-turbo 编译与实践 图像库 libpng 编译与实践 rust 开发编译 Android 动态库实践 Android NDK 开发 —— 从 Assets 文件夹加载图片并上传纹理 简单易用的图像解码库介绍 —— stb_image 博客图床迁移记 进击的 Vulkan 移动开发之 SwapChain 进击的 Vulkan 移动开发之 Command Buffer 进击的 Vulkan 移动开发之 Instance & Device & Queue 进击的 Vulkan 移动开发(一)之今生前世 Java 显式锁 Lock 与条件队列 C++ 标准容器库小结 一文读懂 YUV 的采样与格式 《OpenGL ES 3.x 游戏开发》碰撞检测之 AABB 包围盒
理解使用MVP架构
音视频开发进阶 · 2018-01-31 · via 音视频开发进阶

一个专注音视频领域的小圈子

在安卓开发中使用 MVP 模式已经非常普遍了,网上关于 MVP 的讲解也相当多了,不过看得再多还是自己写一遍比较熟练。 MVP 分别代表了:M(Model)、V(View)、P(Presenter)的缩写,代表了三个不同的模块。

  • Model:负责处理数据的加载或者存储,比如从网络中或者本地数据库中获取数据。
  • View:负责界面数据的展示,以及与用户行为的交互。
  • Presenter:是模型和视图的桥梁,将模型和视图分离开来,主要负责处理事务逻辑。

如果所示,MVP 与 MVC 的不同之处在于,MVP 中的 View 层和 Model 层之间不进行交互,是通过中间的 Presenter 中来进行交互的。这样的好处就是能够解耦,降低 View 层和 Model 层之间的耦合度。

谷歌官方也给出了一个 MVP 模式的示例:https://github.com/googlesamples/android-architecture

用 MVP 模式简单实现了一个登陆功能,项目的包结构如下,根据模块层次来分层的。

接口定义

在 LoginContract 接口中定义了 M 层、V 层、P 层的接口,以及 Model 层数据传递到 Presenter 时的回调接口,将所有的接口放到一个总的接口中,这样不会那么的散乱,便于管理。

而且, M 层、V 层、P 层的实现类都将实现这些接口,这样的好处就是便于替换。

因为 Java 中的向上转型,当我们如下声明一个类时,LoginPresenter 类就向上转型成了 LoginContract.Presenter 类,这时,LoginPresenter 只能调用向上转型的类,也就是接口类中的定义的方法,不能再调用自己实现的其他方法,这样一样,就方便我们在需要的时候对 M 层、V 层、P 层 的类进行替换,可以替换内部的实现方法,而调用的方法名不变。

private LoginContract.Presenter mPresenter = new LoginPresenter()
public interface LoginContract {

    /**
     * View 层的接口
     */
    interface View extends BaseView<Presenter> {
        void showLoading();

        void hideLoading();

        void setEmail(String email);

        void setPassword(String password);

        void toActivity();

        boolean isActive();
    }

    /**
     * Presenter 层的接口
     */
    interface Presenter extends BasePresenter {
        void showLoadingDialog();

        void cancelLoadingDialog();

        void checkUserInfo(String email,String  password);
    }

    /**
     * Model 层的接口
     */
    interface Model {

        List<String> LoaderInitData();

        void checkUserInfo(String email,String password,LoginContract.CallBack callBack);

    }

    /**
     * Model 层传递数据到 Presenter 层时的回调接口
     */
    interface CallBack {
        void LoginSuccess();
        void LoginFailed();
    }
}

V 层实现

将 Fragment 作为 MVP 中的 V 层,而 Activity 则是作为一个总的控制类,并没有做具体的业务逻辑操作,这里是仿照着谷歌官方的例子来编写的,当然也可以将 Activity 作为 V 层来实现。

在 V 层中需要绑定 P 层的 Presenter ,而 V 层则处理数据的展示和与用户的 UI 交互。

而 P 层则是负责处理具体的逻辑。

所以,V 层是将用户的事件都交给了 P 层去处理,而 P 层通过调用 M 层处理的数据,再将它展示给 V 层去。

/**
 * 给 View 来传入 Presenter 来绑定 presenter
 *
 * @param presenter
 */

@Override
public void setPresenter(LoginContract.Presenter presenter) {
    this.mPresenter = presenter;
}

P 层实现

P 层是 MVP 中最重要的负责具体逻辑处理的一层,它持有 V 层和 M 层的引用,毕竟它需要调用 V 层和 M 层的方法。

由于 P 层持有了 V 层的引用,当 presenter 有后台异步的耗时操作时,如果这时退出了 V 层,而后台异步线程的操作并不会立即停止,这时候就会引发内存泄漏了。

/**
 * 构造方法中通过 View 的 setPresenter 让 View 获得了 Presenter 的实例
 * 这样,在 View 中就可以对 Presenter 中的方法进行操作了
 *
 * @param view
 */
public LoginPresenter(LoginContract.View view) {
    mView = view;
    /**
     *  Presenter 绑定 View
     */
    mView.setPresenter(this);

    /**
     * Presenter 引用数据层 Model
     */

    mModel = new LoginModel();

    /**
     * 得到主线程,也就是 UI 线程的消息循环 Looper
     */
    UiHandler = new Handler(Looper.getMainLooper());
}

M 层实现

M 层也实现了 LoginContract 中的 Model 接口,M 层主要负责从网络或者本地数据库中加载数据,因为安卓的主线程并不是线程安全的,而且主线程中不允许进行网络和耗时操作,所以 M 层中的操作都是在异步的子线程中进行的,这样就需要向主线程的消息循环中发送消息,获得主线程中的消息循环(Looper.getMainLooper()),从而更新主线程的 UI 。

public class LoginModel implements LoginContract.Model {

    public LoginModel() {
    }
}

具体的代码实现都可以在 Github 上找到,项目地址是:https://github.com/glumes/MVPLogin

注意事项

防止内存泄漏

在这里,我用的是 Fragment 作为 V 层,为了防止在 P 层中的异步耗时操作执行的时候,V 层已经退出了,所以在仿照谷歌官方的例子,在 V 层接口中定义了 isActive() 方法:

/**
 * 防止 P 层中持有 V 层导致内存泄漏
 * 通过 Fragment 中的 isAdded() 方法判断当前 Fragment 是否已经添加到 Activity 当中去
 * @return
 */
@Override
public boolean isActive() {
    return isAdded();
}

如果是采用 Activity 作为 V 层,则可以在 P 层添加一个销毁 V 层的方法,P 层增加了类似的生命周期的方法,用来在退出 Activity 的时候取消持有的 V 层引用。

//presenter中添加mvpView 置为null的方法public void onDestroy(){    
    mvpView = null;
}

//退出时销毁持有Activity@Overrideprotected void onDestroy() {    
    mvpPresenter.onDestroy();    
    super.onDestroy();
}

声明 Presenter 并绑定 V 层

在 Activity 中有这样一段代码,通过 new 创建了一个 Presenter,但却没有赋值给任何对象。

在谷歌的官方例子中也是这样创建的,这是因为将 Fragment 作为 V 层,并不需要在 Activity 中处理什么逻辑操作。

private LoginFragment mFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mFragment = LoginFragment.newInstance();

    FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(R.id.content,mFragment);
    transaction.commit();

    /**
     * 声明 Presenter 的同时,将 View 和 Presenter 绑定起来
     * 因为 Activity 是一个总控制类,所以 New 方法创建了一个 Presenter 却不需要赋值给 Activity 中的变量
     */
    new LoginPresenter(mFragment);
}

通过查看代码可以得知,在 Presenter 的构造函数中,通过 mView.setPresenter(this); 将 Presenter 自身又传入了 V 层中,这样 V 层通过 setPresenter() 方法持有了 P 层的引用,这样一来,P 层和 V 层就互相持有了各自的引用 。

public LoginPresenter(LoginContract.View view) {
    mView = view;
    /**
     *  Presenter 绑定 View
     */
    mView.setPresenter(this);

参考

  1. http://www.cnblogs.com/liuling/archive/2015/12/23/mvp-pattern-android.html
  2. http://blog.csdn.net/dantestones/article/details/51445208
  3. http://blog.csdn.net/lmj623565791/article/details/46596109

原创文章,转载请注明来源:    理解使用MVP架构