Android MVP + Dagger2 +改造+ RxJava

时间:2020-02-23 14:29:05  来源:igfitidea点击:

在本教程中,我们将使用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文件。