作者:MrTkinG | 来源:互联网 | 2023-05-16 16:50
先挖个坑,这里会有两篇整理后的dagger2和MVP的博客链接
dagger2 入门 MVP示例
Dagger2 通过注解的方式,来实现对象的统一管理(简化初始化);
MVP 主要是减少Activity/Fragment的代码量,只留下清晰的数据和界面的衔接(或者说数据填充);通过使用接口搭建关系,将耦合甩给抽象出来的接口,表现上就是界面逻辑、数据交接集中到了P层;数据的计算、处理是在M层;
mvp有一个明显的好处:即便没有界面,也可通过“单纯的Java”将业务处理完毕,以等带界面衔接。这一点尤其适用于美工瞎几把改图不断完善设计的情况。
在我没有将这两个东西结合起来使用的时候,我一直认为这是一件很复杂的事:
1. P层持有的V的句柄是从界面传过来的,而界面持有P的实例来调用触发方法,就这么来看,一个PV的逻辑就要提供两个Component…..好恐怖的感觉。
2. MVP已经比普通的类数量增加了2~3倍,再加上dagger2的接近两倍,我的天呐!
3. 没试过,劳资的内心就是抵触,怎么滴吧!
接下来直接来个例子吧,废话少说(因为废话都在前两篇说够了),以登录作为典型案例来处理。
V:
/** * Created by ShuaiZhang on 2016/9/19. * 定义接口包括四个方法,分别是获取用户名;获取密码; * 显示登陆提示;显示登陆结果 */ public interface ILoginView { String getName(); String getPwd(); void showTip(String tip); void setResult(String result); }
M:
/** * Created by ShuaiZhang on 2016/9/19. * 我不想说话,你么也别打我 */ public class LoginBiz { //把用户名和密码连接成一个串假,假装是登录 public String login(String name, String pwd){ String result = "UserName:" + name +"\nPassword:" + pwd; return result; } }
P:
/** * Created by ShuaiZhang on 2016/9/19. * MVP,我还是啥也不想说 */ public class LoginPresenter { private ILoginView loginView; private LoginBiz loginBiz; public LoginPresenter(ILoginView loginView) { this.loginView = loginView; loginBiz = new LoginBiz(); } public void doLogin(){ loginView.showTip("Loading。。。"); String result = loginBiz.login(loginView.getName(), loginView.getPwd()); loginView.showTip("Success!"); loginView.setResult(result); } }
为了增加篇幅,activity_login.xml:
android:orientation="vertical" android:layout_ android:layout_> android:id="@+id/edt_name" android:layout_ android:layout_ /> android:id="@+id/edt_pwd" android:layout_ android:layout_ /> android:id="@+id/btn_login" android:layout_ android:layout_ android:text="Login"/> android:id="@+id/txt_show" android:layout_marginTop="20dp" android:layout_ android:layout_ />
LoginActivity.class:
/** * Created by ShuaiZhang on 2016/9/19. * 登陆界面,实现登陆的抽象View ILoginView接口 */ public class LoginActivity extends AppCompatActivity implements ILoginView { LoginPresenter presenter; @BindView(R.id.edt_name) EditText edtName; // 用户名 @BindView(R.id.edt_pwd) EditText edtPwd; // 密码 @BindView(R.id.btn_login) Button btnLogin; //登录按钮 @BindView(R.id.txt_show) TextView txtShow; //显示登录结果 @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); ButterKnife.bind(this); presenter = new LoginPresenter(this);//初始化presenter //装X的拉姆达表达式 btnLogin.setOnClickListener(v -> {if(checkInput()) presenter.doLogin();}); } //输入检查 private boolean checkInput(){ if (edtName.getText().toString().equals("")){ showTip("请输入姓名"); return false; } if (edtPwd.getText().toString().equals("")){ showTip("请输入密码"); return false; } return true; } //传递用户名 @Override public String getName() { return edtName.getText().toString(); } //传递密码 @Override public String getPwd() { return edtPwd.getText().toString(); } //显示提示 @Override public void showTip(String tip) { Toast.makeText(this,tip,Toast.LENGTH_SHORT).show(); } //显示结果 @Override public void setResult(String result) { txtShow.setText(result); } }
到这里,一个以Login为逻辑的MVP基本示例算是都有了。
那么接下来,就是Dagger2了。
想想前面三个疑问,握草,算了,还是慢慢写吧,先写个module试试?
/** * Created by ShuaiZhang on 2016/9/19. * 先写着试试 */ @Module //为登录提供点东西 public class LoginModule { //因为LoginPresenter构造器需要一个这玩意,所以放进了该Module的构造参数 //这样,在LoginActivity初始化时就必然传递该参数 private ILoginView loginView; //有参数的module构造器要求必须显式的传入本实体 必须携带参数 public LoginModule(ILoginView loginView) { this.loginView = loginView; } @Provides //这个注解表名会被自动处理 public LoginPresenter getLoginPresenter() { return new LoginPresenter(loginView); } }
Module有了,来个Component?
/** * Created by ShuaiZhang on 2016/9/19. * 这里决定怎么“组装”Component * 1.决定伴随生命周期(inject方法和参数), * 2.将会包含哪些实体(各module类中的@Provide返回类型) */ @Component(modules=LoginModule.class)//这里还可以有一个依赖,我没搞懂,没添加 public interface LoginActivityComponent { void inject(LoginActivity activity);//有LoginActivity实现初始化,并绑定它的生命周期 }
按照dagger2的套路下一步Build一下:
注意:就是菜单栏的Build àRebuild Project;而Sync是没有用的,有时候这两个效果一样,但是这里不一样。
接下来就是修改LoginActivity的代码了(只提供修改部分,因为其与的没差别):
@Inject LoginPresenter presenter;
为LoginPresenter添加@Inject注解,是它能自动获取dagger生成的对象。
//dagger不需要这里手动实现了 //presenter = new LoginPresenter(this);//初始化presenter DaggerLoginActivityComponent.builder()//套路要求 .loginModule(new LoginModule(this))//显式传入,LoginPresenter必要的参数ILoginView .build()//套路要求 .inject(this);//绑定生命周期
这里注释掉不在需要的presenter的初始化,然后添加DaggerLoginActivityComponent的初始化(名字好特么长)。
Dagger2对MVP的影响只是在关系建立的"方式"上,事实上不对MVP的逻辑关系造成任何影响。它甚至不改变连接的结构,而是以一种新的衔接形式。
一句话描述:dagger2为mvp提供了一种新的、统配的方式来建立相互的联系。
没了,写完了,dagger2+mvp就这样搞定了,我擦,那我前面的顾虑呢?好吧,回头再看一下:
问题:P层持有的V的句柄是从界面传过来的,而界面持有P的实例来调用触发方法,就这么来看,一个PV的逻辑就要提供两个Component…..好恐怖的感觉。
其实这是个误解,Presenter的实例化只是需要一个参数,这个参数是作为presenter的一部分,也就是presenter不再需要独立的Component。之所以我会有这个疑问,其实还是我自己对MVP的理解没有更好的深入,这个….
问题:MVP已经比普通的类数量增加了2~3倍,再加上dagger2的接近两倍,我的天呐!
这个是逻辑分块,清晰化后的代价,换句话说:吃好的,就要多花钱;其实逻辑方面的简洁是优于代码量增加带来的不便的,如果你还在纠结代码量增加了,那么我只能说:兄弟,你代码量还不够,继续加油吧!
问题:没试过,劳资的内心就是抵触,怎么滴吧!
两个字:呵呵!
你们以为到这就完了?图样森破!
现在来看,LoginBiz类是不需要参数的,好像prenter里面那样写,没啥问题,也对;但是既然我们有了dagger,那么我们为什么不试着让它“更加dagger2”一些呢?
修改presenter的构造方法:
public LoginPresenter(ILoginView loginView, LoginBiz loginBiz) { this.loginView = loginView; this.loginBiz = loginBiz; }
这个时候LoginModule也需要修改:
@Provides //这个注解表名会被自动处理 public LoginPresenter getLoginPresenter() { return new LoginPresenter(loginView,new LoginBiz()); }
No!这不够dagger2!
再改LoginModule:
@Provides //这个注解表名会被自动处理 public LoginPresenter getLoginPresenter(LoginBiz loginBiz) { return new LoginPresenter(loginView,loginBiz); } @Provides public LoginBiz getLoginBiz(){ return new LoginBiz(); }
这里的getLoginPresenter的参数LoginBiz将会由下面的方法提供。
那么,如何更加更加dagger2,接下来就自己开发吧,水平所限,我只能装逼到这了……
提交的时候发现少个图,算了,哪天心情好哪天补上吧