안 드 로 이 드 Rxjava 2.0 + Retrofit 2.0 + Mvp 패키지 직접 가 져 가서 간단 한 데모 로 3 분 동안 철저하게 습득

20494 단어 디자인 모드
Android 는 공식 MVC 구조 MVP 구조 MVVM 구 조 를 넘 어 일망타진 합 니 다.
https://blog.csdn.net/WHB20081815/article/details/68948856
 
mvp, MVC 의 차이 점
MVP 의 장단 점
1. MVP 구조 에서 View 와 Model 간 의 결합 을 해제 하여 서로 접근 할 수 없 게 하고 핵심 적 인 업무 논 리 는 모두 Presenter 에 집중 된다.
MVP 모드 는 Activity 의 비 즈 니스 논 리 를 모두 분리 해 Activity 가 UI 논리 로 만 처리 하도록 하고, Android API 와 무관 한 모든 비 즈 니스 논 리 는 Presenter 층 에서 수행한다.
2. 결합 감소, 유지 보수 용이
MVP 는 코드 의 간결 함 을 실현 하 는 동시에 대량의 인터페이스, 클래스 를 추가 하여 관리 하기 가 불편 하 다
 
Model 에서 한 단계 작업 을 할 때 얻 은 결 과 는 Presenter 를 통 해 View 로 전 달 될 때 View 가 인용 한 빈 포인터 이상 이 발생 합 니 다.
Presenter 와 View 는 서로 인용 을 가지 고 있 으 며, 제때에 제거 하지 않 으 면 메모리 누 출 이 발생 하기 쉽다.
 
MVP 모드 적용
2.1 모델 층 설명 과 구체 적 인 코드
view 층 에 보 여 주 려 는 데이터 와 구체 적 인 로그 인 업무 논리 처리 의 실현 을 제공 합 니 다.
 
 
package com.nsu.edu.androidmvpdemo.login;

/**
 * Created by Anthony on 2016/2/15.
 * Class Note:          ,    LoginModelImpl.   MVP    Model 
 */
public interface LoginModel {
    void login(String username, String password, OnLoginFinishedListener listener);
}

来源: https://www.jianshu.com/p/9d40b298eca9

 

 

 

package com.nsu.edu.androidmvpdemo.login;

import android.os.Handler;
import android.text.TextUtils;
/**
 * Created by Anthony on 2016/2/15.
 * Class Note:      (2s),               ,      
 */
public class LoginModelImpl implements LoginModel {

    @Override
    public void login(final String username, final String password, final OnLoginFinishedListener listener) {

        new Handler().postDelayed(new Runnable() {
            @Override public void run() {
                boolean error = false;
                if (TextUtils.isEmpty(username)){
                    listener.onUsernameError();//model     listener
                    error = true;
                }
                if (TextUtils.isEmpty(password)){
                    listener.onPasswordError();
                    error = true;
                }
                if (!error){
                    listener.onSuccess();
                }
            }
        }, 2000);
    }
}

 
 
2.2 view 계층 설명 과 구체 적 인 코드
데 이 터 를 표시 하고 우호 적 인 인터페이스 를 제공 하여 사용자 와 상호작용 을 하면 됩 니 다.MVP 에서 Activity 와 Fragment, View 의 하위 클래스 는 이 층 에 나타난다. Activity 는 보통 UI 보 기 를 불 러 오고 감청 을 설정 한 다음 에 Presenter 가 처리 하 는 작업 을 하기 때문에 해당 Presenter 의 인용 을 가 져 야 한다.이 층 에서 해 야 할 작업 은 매번 상응하는 상호작용 이 있 을 때마다 presenter 와 관련 된 방법 을 호출 하면 된다.(예 를 들 어 button 클릭)
 
package com.nsu.edu.androidmvpdemo.login;

/**
 * Created by Anthony on 2016/2/15.
 * Class Note:  View   ,         activity
 */
public interface LoginView {
    void showProgress();

    void hideProgress();

    void setUsernameError();

    void setPasswordError();

    void navigateToHome();
}

 
 
package com.nsu.edu.androidmvpdemo.login;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.nsu.edu.androidmvpdemo.R;

/**
 * Created by Anthony on 2016/2/15.
 * Class Note:MVP   View     activity,      activity
 */
public class LoginActivity extends Activity implements LoginView, View.OnClickListener {

    private ProgressBar progressBar;
    private EditText username;
    private EditText password;
    private LoginPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        progressBar = (ProgressBar) findViewById(R.id.progress);
        username = (EditText) findViewById(R.id.username);
        password = (EditText) findViewById(R.id.password);
        findViewById(R.id.button).setOnClickListener(this);

        presenter = new LoginPresenterImpl(this);
    }

    @Override
    protected void onDestroy() {
        presenter.onDestroy();
        super.onDestroy();
    }

    @Override
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideProgress() {
        progressBar.setVisibility(View.GONE);
    }

    @Override
    public void setUsernameError() {
        username.setError(getString(R.string.username_error));
    }

    @Override
    public void setPasswordError() {
        password.setError(getString(R.string.password_error));
    }

    @Override
    public void navigateToHome() {
// TODO       startActivity(new Intent(this, MainActivity.class));
        Toast.makeText(this,"login success",Toast.LENGTH_SHORT).show();
//        finish();
    }

    @Override
    public void onClick(View v) {
        presenter.validateCredentials(username.getText().toString(), password.getText().toString());
    }

}

 
2.3 presenter 계층 설명 과 구체 적 인 코드
Presenter 는 view 와 model 의 중간 층 역할 을 하고 있다.model 층 의 데 이 터 를 가 져 와 view 층 을 구축 합 니 다.view 층 UI 의 피드백 명령 을 받 은 후 처리 논 리 를 나 누 어 model 층 에 업무 수행 을 맡 길 수도 있 습 니 다.그것 도 View 층 의 각종 조작 을 결정 할 수 있다.
 
 
package com.nsu.edu.androidmvpdemo.login;

/**
 * Created by Anthony on 2016/2/15.
 * Class Note:   Presenter    ,    LoginPresenterImpl,       ,      view
 */
public interface LoginPresenter {
    void validateCredentials(String username, String password);

    void onDestroy();
}
package com.nsu.edu.androidmvpdemo.login;

/**
 * Created by Anthony on 2016/2/15.
 * Class Note:
 * 1   presenter   。      Model  View       。
 * 2  presenter     OnLoginFinishedListener,
 *   Presenter   , Model   ,  View    ,
 *    Model      View 。         LoginPresenterImpl    ,
 * LoginPresenterImpl   View Model     Model       View ?
 */
public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener {
    private LoginView loginView;
    private LoginModel loginModel;

    public LoginPresenterImpl(LoginView loginView) {
        this.loginView = loginView;
        this.loginModel = new LoginModelImpl();
    }

    @Override
    public void validateCredentials(String username, String password) {
        if (loginView != null) {
            loginView.showProgress();
        }

        loginModel.login(username, password, this);
    }

    @Override
    public void onDestroy() {
        loginView = null;
    }

    @Override
    public void onUsernameError() {
        if (loginView != null) {
            loginView.setUsernameError();
            loginView.hideProgress();
        }
    }

    @Override
    public void onPasswordError() {
        if (loginView != null) {
            loginView.setPasswordError();
            loginView.hideProgress();
        }
    }

    @Override
    public void onSuccess() {
        if (loginView != null) {
            loginView.navigateToHome();
        }
    }
}

 
 
1 Activity 는 UI 초기 화 된 것 을 만 들 었 고 LoginPresenter 의 인용 과 LoginView 의 인 터 페 이 스 를 예화 해 야 합 니 다. 감청 인터페이스 동작 2 로그 인 단 추 를 누 르 면 로그 인 이 벤트 를 받 을 수 있 습 니 다. onClick 에서 받 으 면 LoginPresenter 의 인용 을 통 해 LoginPresenter 에 맡 깁 니 다.Login Presenter 는 로그 인 논 리 를 받 아들 이면 로그 인 3 을 알 수 있 습 니 다. 그리고 Login Presenter 는 진행 상황 을 표시 하고 논 리 를 우리 의 Model 에 맡 깁 니 다. 즉, 이 안의 Login Model 입 니 다. (Login Model 의 실현 류 Login Model Impl) 은 OnLogin Finished Listener, 즉 Login Presenter 자체 가 우리 에 게 전달 하 는 Model (Login Model) 입 니 다.4 LoginModel 이 논 리 를 처리 한 후, 결 과 는 OnLoginFinished Listener 리 셋 을 통 해 LoginPresenter 5 LoginPresenter 에 결 과 를 view 층 의 Activity 에 되 돌려 주 고, 마지막 activity 는 결 과 를 표시 합 니 다.
MVP 총괄:
presenter 패키지 view 와 Model
Activity 에는 presenter 와 view 가 포함 되 어 있 습 니 다.
 
화면 음악 p 더 좋 은 쓰기:
https://www.jianshu.com/p/063126f807d0
RxJava 2.0 + Retrofit 2.0 결합 사용
1 //rxjava +retrofit
    implementation 'io.reactivex.rxjava2:rxjava:2.1.1'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
    implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
    implementation 'com. squareup. retrofit 2: adapter - rxjava 2: 2.3.0' / / 협조 rxjava 2
    implementation 'com. squareup. okhtp 3: logging - interceptor: 3.8.1' / / 차단기
1. RetrofitClient 클 라 이언 트 구축.
public static RetrofitClient getInstance(Context context, String url) {
    if (context != null) {
        mContext = context;
    }
    sNewInstance = new RetrofitClient(context, url);
    return sNewInstance;
}

private RetrofitClient(Context context) {

    this(context, null);
}

private RetrofitClient(Context context, String url) {

    if (TextUtils.isEmpty(url)) {
        url = baseUrl;
    }
    okHttpClient = new OkHttpClient.Builder()
            .addNetworkInterceptor(
                    new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))
            .cookieJar(new NovateCookieManger(context))
            .addInterceptor(new BaseInterceptor(mContext))
            .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
            .build();
    Retrofit retrofit = new Retrofit.Builder()
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .baseUrl(url)
            .build();
    apiService = retrofit.create(ApiService.class);
}

 
2.
ApiService

     API   ,               ,             。

/**
 * Created by Tamic on 2016-07-08.
 */
public interface ApiService {

    public static final String Base_URL = "http://ip.taobao.com/";
    /**
     *    
     */
    @GET("service/getIpInfo.php/")
    Observable getData(@Query("ip") String ip);

    @GET("{url}")
    Observable executeGet(
            @Path("url") String url,
            @QueryMap Map maps);


    @POST("{url}")
    Observable executePost(
            @Path("url") String url,
            @QueryMap Map maps);

    @Multipart
    @POST("{url}")
    Observable upLoadFile(
            @Path("url") String url,
            @Part("image\\\\"; filename=\\"image.jpg") RequestBody avatar);


    @POST("{url}")
    Observable uploadFiles(
            @Path("url") String url,
            @Path("headers") Map headers,
            @Part("filename") String description,
            @PartMap()  Map maps);

    @Streaming
    @GET
    Observable downloadFile(@Url String fileUrl);

}

 
public void getData(Subscriber subscriber, String ip) {
    apiService.getData(ip)
            .subscribeOn(Schedulers.io())
            .unsubscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(subscriber);
}

public void get(String url, Map headers, Map parameters, Subscriber subscriber) {
    apiService.executeGet(url, headers, parameters)
            .subscribeOn(Schedulers.io())
            .unsubscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(subscriber);
}

public void post(String url, Map headers, Map parameters, Subscriber subscriber) {
    apiService.executePost(url, headers, parameters)
            .subscribeOn(Schedulers.io())
            .unsubscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(subscriber);
}

 
                             ,  api        ,      rxJava       Transformer
    Observable.Transformer schedulersTransformer() {
        return new Observable.Transformer() {


            @Override
            public Object call(Object observable) {
                return ((Observable)  observable).subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }
1

      api       :

    public Subscription getData(Subscriber subscriber, String ip) {
        return apiService.getData(ip)
                .compose(schedulersTransformer())
                .subscribe(subscriber);
    }

RX 의 Transformer 용법 이 바로 위 에 있 습 니 다.
 
우 리 는 결 과 를 되 돌려 주 는 것 에 대해 예비 처 리 를 해 야 합 니 다. Default Transformer 를 새로 만 들 면 Observable. Transformer 를 계승 합 니 다. 예비 처 리 는 status 일 뿐 입 니 다.code 는 판단 과 해석 을 하고 서로 다른 오 류 는 서로 다른 오류 정 보 를 되 돌려 줍 니 다.연산 자 copose 가 있 습 니 다.우 리 는 모든 요청 에서 status 를 처리 하기 때문이다.code 와 일부 조작 자 를 사용 합 니 다. 예 를 들 어 observeon 과 subscribeOn 으로 스 레 드 를 전환 합 니 다.RxJava 는 Transformer (변환기) 를 제공 합 니 다. 일반적인 상황 에서 연산 자 copose () 를 사용 하여 이 루어 집 니 다.
 
여기 서 우 리 는 map 연산 자 를 사용 하여 Observable > 을 Observable 로 내부 에서 status 로 변환 합 니 다.코드 가 사전 처 리 됐 습 니 다.여기 서 상태 코드 가 10000 과 같 지 않 으 면 요청 오류 가 발생 하여 이상 을 던 집 니 다.이 곳 의 ApiException 은 서버 가 되 돌아 오 는 이상 을 처리 하기 위해 사용자 정의 이상 클래스 입 니 다.
 
public class DefaultTransformer implements Observable.Transformer {
    @Override
    public Observable call(Observable tObservable) {
        return tObservable
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .map(new Func1() {//       ,  code
                    @Override
                    public T call(T t) {
                        if (((BaseEntity)t).getStatus_code() != 10000) {
                            throw new ApiException(((BaseEntity)t).getStatus_code(), ((BaseEntity)t).getError_msg());
                        }
                        return t;
                    }
                });
    }

    public static  DefaultTransformer create() {
        return new DefaultTransformer<>();
    }
}

来源: https://www.jianshu.com/p/9364a9577c41

 

3.构建基础拦截器

 

责任链模式:很明显,在okhttp中的拦截器模块,执行过程用到。OkHttp3 的拦截器链中, 内置了5个默认的拦截器,分别用于重试、请求对象转换、缓存、链接、网络读写(责任链模式:为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。)

 

Interceptor 的设计是一种分层的思想,每个 Interceptor 就是一层。分层简化了每一层的逻辑,每层只需要关注自己的责任,而各层之间通过约定的接口/协议进行合作(面向接口编程思想),共同完成复杂的任务。

--------------------- 

本来

RetryAndFollowUpInterceptor:负责重定向。

BridgeInterceptor:负责把用户构造的请求转换为发送给服务器的请求,把服务器返回的响应转换为对用户友好的响应。

CacheInterceptor:负责读取缓存以及更新缓存。

ConnectInterceptor:负责与服务器建立连接。

CallServerInterceptor:负责从服务器读取响应的数据。

 

 

包含cookies的管理还有拦截器(自定义拦截器)

1.日志拦截器

2.缓存拦截器

3.网络拦截器:添加下载的头。拦截器

okHttpClient = new OkHttpClient.Builder()
        .addNetworkInterceptor(
                new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
        .cookieJar(new NovateCookieManger(context))
        .cache(cache)
        .addInterceptor(new BaseInterceptor(headers))
        .addInterceptor(new CaheInterceptor(context))
        .addNetworkInterceptor(new CaheInterceptor(context))

 
 
       

      header,     MAP      , heder   Request 。

/**
 * BaseInterceptor,use set okhttp call header
 * Created by Tamic on 2016-06-30.
 */
public class BaseInterceptor implements Interceptor {

    private Map headers;

    public BaseInterceptor(Map headers) {
        this.headers = headers;
    }

    @Override
    public Response intercept(Chain chain) throws    IOException {

        Request.Builder builder = chain.request()
                .newBuilder();
        if (headers != null && headers.size() > 0) {
            Set keys = headers.keySet();
            for (String headerKey : keys) {
                builder.addHeader(headerKey,   headers.get(headerKey)).build();
            }
        }
        return chain.proceed(builder.build());

    }
}

마지막 호출 시 다음 과 같 습 니 다:
btn_get.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

        Map maps = new HashMap();
        maps.put("ip", "21.22.11.33");

        //"http://ip.taobao.com/service/getIpInfo.php?ip=21.22.11.33";
        RetrofitClient.getInstance(MainActivity.this).createBaseApi().get("service/getIpInfo.php"
                , maps, new BaseSubscriber(MainActivity.this) {


                    @Override
                    public void onError(ResponeThrowable e) {


                        Log.e("Lyk", e.getMessage());
                        Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();

                    }

                    @Override
                    public void onNext(IpResult responseBody) {

                        Toast.makeText(MainActivity.this, responseBody.toString(), Toast.LENGTH_LONG).show();
                    }
                });
    }
});


btn_post.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

        Map maps = new HashMap();

        maps.put("ip", "21.22.11.33");
        //"http://ip.taobao.com/service/getIpInfo.php?ip=21.22.11.33";
        RetrofitClient.getInstance(MainActivity.this).createBaseApi().post("service/getIpInfo.php"
                , maps, new BaseSubscriber(MainActivity.this) {

                    @Override
                    public void onError(ResponeThrowable e) {
                        Log.e("Lyk", e.getMessage());
                        Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();

                    }

                    @Override
                    public void onNext(ResponseBody responseBody) {
                        Toast.makeText(MainActivity.this, responseBody.toString(), Toast.LENGTH_LONG).show();
                    }
                });
    }
});

 
다음은 생명 주 기 를 관리 하 는 거 야.라 이 프 사이클 을 관리 할 수 있 는 전문 라 이브 러 리 가 있 습 니 다.
Rx Lifecycle, 가 보 셔 도 됩 니 다.하지만 우 리 는 이 걸 쓰 지 않 습 니 다. BaseActivity 에 쓰 겠 습 니 다.
 
public abstract class BaseActivity extends AppCompatActivity {

    private CompositeSubscription mCompositeSubscription;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        onUnsubscribe();
    }
    /**
     *   rx   ,       ,   detachView     
     */
    protected void addSubscription(Subscription subscribe) {
        if (mCompositeSubscription == null)
            mCompositeSubscription = new CompositeSubscription();
        mCompositeSubscription.add(subscribe);
    }

    /**
     *          
     */
    protected void onUnsubscribe() {
        if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
            mCompositeSubscription.unsubscribe();
        }
    }

}

데 이 터 를 요청 하 는 곳 에서 addSubscription (Subscription subscribe) 방법 을 호출 합 니 다. activity 가 onDestroy () 에 있 을 때 구독 을 취소 합 니 다.
 
RxJava + Retrofit 2.0 프로젝트 실전 완벽 패키지
참고 블 로그:
https://blog.csdn.net/silenceoo/article/details/75037576
 
베 스 트
https://blog.csdn.net/u013651026/article/details/79294396
 
 
사실 Retrofit 의 전통 적 인 API 호출 은 okHTTP 기능 과 사용 에 있어 본질 적 인 차이 가 없고 RxJava 와 결합 하여 사용 하 는 것 이 강하 다.

좋은 웹페이지 즐겨찾기