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。
方法:
首先创建您的活动和
recyclerview
xml布局。
在编码之前,请锁定设计。创建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文件。