Android MVP + Dagger2 +改造+ RxJava
在本教程中,我们将使用MVP,Dagger2,Retrofit和RxJava开发应用程序。
如今,加密货币是最新的时尚。
因此,我们将使用以上内容创建一个加密货币应用程序。
说明
要进一步进行操作,您必须具有Model View Presenter,依赖注入,翻新和RxJava库的概述。
我们已经单独介绍了它们,并结合了以下指定的教程链接:
- Android MVP
- Android Dagger2
- Android改造
- Android RxJava
- Android Dagger2 +改装
- Android RxJava +改造
- MVP +匕首2
如果您了解上述内容的基础知识,那么下面的示例应该很容易。
让我们潜入吧!
创建一个新的Android Studio项目。
库
在您应用的" build.gradle"文件中添加以下库。
RxAndroid就像RxJava。
它包含Android框架的调度程序和线程。
项目结构
di软件包包含依赖项注入– Dagger2相关文件。
mvp软件包由与MVP相关的软件包组成,依此类推。
我们从其中开始,如何开始?当要求我们在项目中使用这么多不同的东西时,这可能会造成混乱。
开始总是很棘手。
一旦我们知道了过程,一切就变得容易了。
因此,让我们列出我们的要求以及我们将如何处理它们。
我们的应用程序将包含一个活动。
它将托管一个RecyclerView来显示来自API调用的数据。
对于API调用,我们将使用Retrofit和RxJava。
方法:
首先创建您的活动和
recyclerviewxml布局。
在编码之前,请锁定设计。创建APIInterface.java类。
它定义了要进行的API调用。创建POJO数据类,它将解析来自API调用的响应。
首先为MVP创建接口– MainActivityContract.java。
创建di模块,组件,范围和限定符。
现在创建PresenterImpl。
最后,编写MainActivity和RecyclerViewAdapter类,将Dependencies和MVP模式插入其中。
布局代码
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
</android.support.constraint.ConstraintLayout>
recycler_view_list_row.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:id="@+id/txtCoin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/txtCurrentPrice"
app:layout_constraintTop_toTopOf="parent"
<TextView
android:id="@+id/txtCurrentPrice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/txtCoin"
<TextView
android:id="@+id/txtOneHourChange"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
<TextView
android:id="@+id/txtOneHour"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="1H"
app:layout_constraintEnd_toStartOf="@+id/txtOneHourChange"
app:layout_constraintTop_toTopOf="@+id/txtOneHourChange"
<TextView
android:id="@+id/txt24HourChange"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/txtOneHourChange"
<TextView
android:id="@+id/txt7Day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="7D"
app:layout_constraintEnd_toStartOf="@+id/txt7DayChange"
app:layout_constraintTop_toTopOf="@+id/txt7DayChange"
<TextView
android:id="@+id/txt24Hour"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="24H"
app:layout_constraintBottom_toTopOf="@+id/txt7DayChange"
app:layout_constraintEnd_toStartOf="@+id/txt24HourChange"
app:layout_constraintTop_toTopOf="@+id/txt24HourChange"
<TextView
android:id="@+id/txt7DayChange"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/txt24HourChange"
</android.support.constraint.ConstraintLayout>
APIInterface.java
package com.theitroad.mvpdagger2retroiftrxjava.retrofit;
import com.theitroad.mvpdagger2retroiftrxjava.pojo.CryptoData;
import java.util.List;
import retrofit2.http.GET;
import retrofit2.http.Query;
import rx.Observable;
public interface APIInterface {
@GET("ticker/?")
Observable<List<CryptoData>> getData(@Query("limit") String limit);
}
基本URL将在我们的RetrofitModule中定义。
我们正在使用此API。
CryptoData.java
package com.theitroad.mvpdagger2retroiftrxjava.pojo;
import com.google.gson.annotations.SerializedName;
public class CryptoData {
@SerializedName("id")
public String id;
@SerializedName("name")
public String name;
@SerializedName("symbol")
public String symbol;
@SerializedName("rank")
public String rank;
@SerializedName("price_usd")
public String priceUsd;
@SerializedName("price_btc")
public String priceBtc;
@SerializedName("24h_volume_usd")
public String _24hVolumeUsd;
@SerializedName("market_cap_usd")
public String marketCapUsd;
@SerializedName("available_supply")
public String availableSupply;
@SerializedName("total_supply")
public String totalSupply;
@SerializedName("percent_change_1h")
public String percentChange1h;
@SerializedName("percent_change_24h")
public String percentChange24h;
@SerializedName("percent_change_7d")
public String percentChange7d;
@SerializedName("last_updated")
public String lastUpdated;
}
MainActivityContract.java这是我们的View-Model-Presenter的蓝图:
package com.theitroad.mvpdagger2retroiftrxjava.mvp;
import com.theitroad.mvpdagger2retroiftrxjava.pojo.CryptoData;
import java.util.List;
public interface MainActivityContract {
interface View {
void showData(List<CryptoData> data);
void showError(String message);
void showComplete();
void showProgress();
void hideProgress();
}
interface Presenter {
void loadData();
}
}
ActivityScope.java
package com.theitroad.mvpdagger2retroiftrxjava.di.scopes;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Scope;
@Scope
@Retention(RetentionPolicy.CLASS)
public @interface ActivityScope {
}
ApplicationScope.java
package com.theitroad.mvpdagger2retroiftrxjava.di.scopes;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Scope;
@Scope
@Retention(RetentionPolicy.CLASS)
public @interface ApplicationScope {
}
ActivityContext.java
package com.theitroad.mvpdagger2retroiftrxjava.di.qualifier;
import javax.inject.Qualifier;
@Qualifier
public @interface ActivityContext {
}
ApplicationContext.java
package com.theitroad.mvpdagger2retroiftrxjava.di.qualifier;
import javax.inject.Qualifier;
@Qualifier
public @interface ApplicationContext {
}
ContextModule.java
package com.theitroad.mvpdagger2retroiftrxjava.di.module;
import android.content.Context;
import com.theitroad.mvpdagger2retroiftrxjava.di.qualifier.ApplicationContext;
import com.theitroad.mvpdagger2retroiftrxjava.di.scopes.ApplicationScope;
import dagger.Module;
import dagger.Provides;
@Module
public class ContextModule {
private Context context;
public ContextModule(Context context) {
this.context = context;
}
@Provides
@ApplicationScope
@ApplicationContext
public Context provideContext() {
return context;
}
}
MainActivityContextModule.java
package com.theitroad.mvpdagger2retroiftrxjava.di.module;
import android.content.Context;
import com.theitroad.mvpdagger2retroiftrxjava.MainActivity;
import com.theitroad.mvpdagger2retroiftrxjava.di.qualifier.ActivityContext;
import com.theitroad.mvpdagger2retroiftrxjava.di.scopes.ActivityScope;
import dagger.Module;
import dagger.Provides;
@Module
public class MainActivityContextModule {
private MainActivity mainActivity;
public Context context;
public MainActivityContextModule(MainActivity mainActivity) {
this.mainActivity = mainActivity;
context = mainActivity;
}
@Provides
@ActivityScope
public MainActivity providesMainActivity() {
return mainActivity;
}
@Provides
@ActivityScope
@ActivityContext
public Context provideContext() {
return context;
}
}
RetrofitModule.java
package com.theitroad.mvpdagger2retroiftrxjava.di.module;
import com.theitroad.mvpdagger2retroiftrxjava.di.scopes.ApplicationScope;
import com.theitroad.mvpdagger2retroiftrxjava.retrofit.APIInterface;
import dagger.Module;
import dagger.Provides;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
@Module
public class RetrofitModule {
@Provides
@ApplicationScope
APIInterface getApiInterface(Retrofit retroFit) {
return retroFit.create(APIInterface.class);
}
@Provides
@ApplicationScope
Retrofit getRetrofit(OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.baseUrl("https://api.coinmarketcap.com/v1/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(okHttpClient)
.build();
}
@Provides
@ApplicationScope
OkHttpClient getOkHttpCleint(HttpLoggingInterceptor httpLoggingInterceptor) {
return new OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.build();
}
@Provides
@ApplicationScope
HttpLoggingInterceptor getHttpLoggingInterceptor() {
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return httpLoggingInterceptor;
}
}
MainActivityMvpModule.java
package com.theitroad.mvpdagger2retroiftrxjava.di.module;
import com.theitroad.mvpdagger2retroiftrxjava.di.scopes.ActivityScope;
import com.theitroad.mvpdagger2retroiftrxjava.mvp.MainActivityContract;
import dagger.Module;
import dagger.Provides;
@Module
public class MainActivityMvpModule {
private final MainActivityContract.View mView;
public MainActivityMvpModule(MainActivityContract.View mView) {
this.mView = mView;
}
@Provides
@ActivityScope
MainActivityContract.View provideView() {
return mView;
}
}
AdapterModule.java
package com.theitroad.mvpdagger2retroiftrxjava.di.module;
import com.theitroad.mvpdagger2retroiftrxjava.MainActivity;
import com.theitroad.mvpdagger2retroiftrxjava.RecyclerViewAdapter;
import com.theitroad.mvpdagger2retroiftrxjava.di.scopes.ActivityScope;
import dagger.Module;
import dagger.Provides;
@Module(includes = {MainActivityContextModule.class})
public class AdapterModule {
@Provides
@ActivityScope
public RecyclerViewAdapter getCoinList(RecyclerViewAdapter.ClickListener clickListener) {
return new RecyclerViewAdapter(clickListener);
}
@Provides
@ActivityScope
public RecyclerViewAdapter.ClickListener getClickListener(MainActivity mainActivity) {
return mainActivity;
}
}
组件是注入来自模块的依赖性的东西。
ApplicationComponent.java
package com.theitroad.mvpdagger2retroiftrxjava.di.component;
import android.content.Context;
import com.theitroad.mvpdagger2retroiftrxjava.MyApplication;
import com.theitroad.mvpdagger2retroiftrxjava.di.module.ContextModule;
import com.theitroad.mvpdagger2retroiftrxjava.di.module.RetrofitModule;
import com.theitroad.mvpdagger2retroiftrxjava.di.qualifier.ApplicationContext;
import com.theitroad.mvpdagger2retroiftrxjava.di.scopes.ApplicationScope;
import com.theitroad.mvpdagger2retroiftrxjava.retrofit.APIInterface;
import dagger.Component;
@ApplicationScope
@Component(modules = {ContextModule.class, RetrofitModule.class})
public interface ApplicationComponent {
APIInterface getApiInterface();
@ApplicationContext
Context getContext();
void injectApplication(MyApplication myApplication);
}
MainActivityComponent.java
package com.theitroad.mvpdagger2retroiftrxjava.di.component;
import android.content.Context;
import com.theitroad.mvpdagger2retroiftrxjava.MainActivity;
import com.theitroad.mvpdagger2retroiftrxjava.di.module.AdapterModule;
import com.theitroad.mvpdagger2retroiftrxjava.di.module.MainActivityMvpModule;
import com.theitroad.mvpdagger2retroiftrxjava.di.qualifier.ActivityContext;
import com.theitroad.mvpdagger2retroiftrxjava.di.scopes.ActivityScope;
import dagger.Component;
@ActivityScope
@Component(modules = {AdapterModule.class, MainActivityMvpModule.class}, dependencies = ApplicationComponent.class)
public interface MainActivityComponent {
@ActivityContext
Context getContext();
void injectMainActivity(MainActivity mainActivity);
}
PresenterImpl.java
package com.theitroad.mvpdagger2retroiftrxjava.mvp;
import com.theitroad.mvpdagger2retroiftrxjava.pojo.CryptoData;
import com.theitroad.mvpdagger2retroiftrxjava.retrofit.APIInterface;
import java.util.List;
import javax.inject.Inject;
import rx.Observer;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public class PresenterImpl implements MainActivityContract.Presenter {
APIInterface apiInterface;
MainActivityContract.View mView;
@Inject
public PresenterImpl(APIInterface apiInterface, MainActivityContract.View mView) {
this.apiInterface = apiInterface;
this.mView = mView;
}
@Override
public void loadData() {
mView.showProgress();
apiInterface.getData("10").subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<CryptoData>>() {
@Override
public void onCompleted() {
mView.showComplete();
mView.hideProgress();
}
@Override
public void onError(Throwable e) {
mView.showError("Error occurred");
mView.hideProgress();
}
@Override
public void onNext(List<CryptoData> data) {
mView.showData(data);
}
});
}
}
构造函数上的@Inject表示该类的对象将被注入MainActivity中。
演示者调用所需的View界面方法,这些方法将触发MainActivity中的操作。
MyApplication.java
package com.theitroad.mvpdagger2retroiftrxjava;
import android.app.Activity;
import android.app.Application;
import com.theitroad.mvpdagger2retroiftrxjava.di.component.ApplicationComponent;
import com.theitroad.mvpdagger2retroiftrxjava.di.component.DaggerApplicationComponent;
import com.theitroad.mvpdagger2retroiftrxjava.di.module.ContextModule;
public class MyApplication extends Application {
ApplicationComponent applicationComponent;
@Override
public void onCreate() {
super.onCreate();
applicationComponent = DaggerApplicationComponent.builder().contextModule(new ContextModule(this)).build();
applicationComponent.injectApplication(this);
}
public static MyApplication get(Activity activity){
return (MyApplication) activity.getApplication();
}
public ApplicationComponent getApplicationComponent() {
return applicationComponent;
}
}
MainActivity.java
package com.theitroad.mvpdagger2retroiftrxjava;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.theitroad.mvpdagger2retroiftrxjava.di.component.ApplicationComponent;
import com.theitroad.mvpdagger2retroiftrxjava.di.component.DaggerMainActivityComponent;
import com.theitroad.mvpdagger2retroiftrxjava.di.component.MainActivityComponent;
import com.theitroad.mvpdagger2retroiftrxjava.di.module.MainActivityContextModule;
import com.theitroad.mvpdagger2retroiftrxjava.di.module.MainActivityMvpModule;
import com.theitroad.mvpdagger2retroiftrxjava.di.qualifier.ActivityContext;
import com.theitroad.mvpdagger2retroiftrxjava.di.qualifier.ApplicationContext;
import com.theitroad.mvpdagger2retroiftrxjava.mvp.MainActivityContract;
import com.theitroad.mvpdagger2retroiftrxjava.mvp.PresenterImpl;
import com.theitroad.mvpdagger2retroiftrxjava.pojo.CryptoData;
import java.util.List;
import javax.inject.Inject;
public class MainActivity extends AppCompatActivity implements MainActivityContract.View, RecyclerViewAdapter.ClickListener {
private RecyclerView recyclerView;
private ProgressBar progressBar;
MainActivityComponent mainActivityComponent;
@Inject
public RecyclerViewAdapter recyclerViewAdapter;
@Inject
@ApplicationContext
public Context mContext;
@Inject
@ActivityContext
public Context activityContext;
@Inject
PresenterImpl presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ApplicationComponent applicationComponent = MyApplication.get(this).getApplicationComponent();
mainActivityComponent = DaggerMainActivityComponent.builder()
.mainActivityContextModule(new MainActivityContextModule(this))
.mainActivityMvpModule(new MainActivityMvpModule(this))
.applicationComponent(applicationComponent)
.build();
mainActivityComponent.injectMainActivity(this);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(activityContext));
recyclerView.setAdapter(recyclerViewAdapter);
progressBar = findViewById(R.id.progressBar);
presenter.loadData();
}
@Override
public void launchIntent(String name) {
Toast.makeText(mContext, name, Toast.LENGTH_SHORT).show();
//startActivity(new Intent(activityContext, DetailActivity.class).putExtra("name", name));
}
@Override
public void showData(List<CryptoData> data) {
recyclerViewAdapter.setData(data);
}
@Override
public void showError(String message) {
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
}
@Override
public void showComplete() {
}
@Override
public void showProgress() {
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideProgress() {
progressBar.setVisibility(View.GONE);
}
}
多亏了DI和MVP,我们已经注入了所有内容,并从MainActivity中分离了网络呼叫。
MainActivity.java仅按照Presenter中的说明填充视图。
下面给出了RecyclerViewAdapter.java类的代码:
package com.theitroad.mvpdagger2retroiftrxjava;
import android.support.constraint.ConstraintLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.theitroad.mvpdagger2retroiftrxjava.pojo.CryptoData;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private List<CryptoData> data;
private RecyclerViewAdapter.ClickListener clickListener;
@Inject
public RecyclerViewAdapter(ClickListener clickListener) {
this.clickListener = clickListener;
data = new ArrayList<>();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_view_list_row, parent, false));
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.txtCoin.setText(data.get(position).symbol);
holder.txtCurrentPrice.setText(data.get(position).priceUsd);
holder.txt1HourChange.setText(data.get(position).percentChange1h + "%");
holder.txt24HourChange.setText(data.get(position).percentChange24h + "%");
holder.txt7DayChange.setText(data.get(position).percentChange7d + "%");
}
@Override
public int getItemCount() {
return data.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
private TextView txtCoin;
private TextView txtCurrentPrice;
private TextView txt1HourChange;
private TextView txt24HourChange;
private TextView txt7DayChange;
private ConstraintLayout constraintLayoutContainer;
ViewHolder(View itemView) {
super(itemView);
txtCoin = itemView.findViewById(R.id.txtCoin);
txtCurrentPrice = itemView.findViewById(R.id.txtCurrentPrice);
txt1HourChange = itemView.findViewById(R.id.txtOneHourChange);
txt24HourChange = itemView.findViewById(R.id.txt24HourChange);
txt7DayChange = itemView.findViewById(R.id.txt7DayChange);
constraintLayoutContainer = itemView.findViewById(R.id.constraintLayout);
constraintLayoutContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
clickListener.launchIntent(data.get(getAdapterPosition()).name);
}
});
}
}
public interface ClickListener {
void launchIntent(String name);
}
public void setData(List<CryptoData> data) {
this.data.addAll(data);
notifyDataSetChanged();
}
}
重建项目以自动创建Dagger文件。

