728x90

RxAndroid and Retrofit 를 활용하여 파일을 다운로드하고 PDF Viewer 처리하는 코드 예제이다.

 

build.gradle:

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // 코틀린 혼용 사용을 위해 추가
 
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.4.0'
    implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.9.0'
 
    // ReactiveX : Observable에서 보낸 이벤트를 Operator에서 가공하고 Subscribe에서 받는 것
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    implementation 'io.reactivex.rxjava3:rxjava:3.1.5'
    implementation "com.github.akarnokd:rxjava3-retrofit-adapter:3.0.0"
 
}

 

RetrofitService.java:

import io.reactivex.rxjava3.core.Observable;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Streaming;
import retrofit2.http.Url;
 
public interface RetrofitService {
    // POST 방식으로 데이터를 주고 받을 때 넘기는 변수는 Field 라고 해야 한다.
 
    @FormUrlEncoded
    @POST(RetrofitUrl.PdfData)
    Call<PdfResult> getPdfData(
            @Field("keyword"String keyword,
            @Field("search"String search
    );
 
    @Streaming
    @GET
    Observable<Response<ResponseBody>> downloadFile(@Url String fileUrl);
 
}
 

 

RetrofitAdapter.java:

import hu.akarnokd.rxjava3.retrofit.RxJava3CallAdapterFactory;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
 
public class RetrofitAdapter {
    static Retrofit retrofit = null;
    static Retrofit retrofit_service = null;
 
    public static Retrofit getClient() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(RetrofitUrl.BASE_URL)
                    .client(createOkHttpClient())
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }
 
    public static OkHttpClient createOkHttpClient() {
        // 네트워크 통신 로그(서버로 보내는 파라미터 및 받는 파라미터) 보기
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        //builder.cookieJar(new RetrofitCookieJar()); // Session 통신을 위한 기능
        // 네트워크 통신 로그(서버로 보내는 파라미터 및 받는 파라미터) 보기
        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        builder.addInterceptor(httpLoggingInterceptor);
        builder.connectTimeout(15, TimeUnit.SECONDS);
        builder.readTimeout(20, TimeUnit.SECONDS);
        builder.writeTimeout(20, TimeUnit.SECONDS);
        builder.retryOnConnectionFailure(true);
        return builder.build();
    }
 
    public static <T> T createService(Class<T> serviceClass) {
        // implementation "com.github.akarnokd:rxjava3-retrofit-adapter:3.0.0"
        if (retrofit_service == null) {
            retrofit_service = new Retrofit.Builder()
                    .baseUrl(RetrofitUrl.BASE_URL)
                    .client(createOkHttpClient())
                    .addCallAdapterFactory(RxJava3CallAdapterFactory.create()).build();
        }
        return retrofit_service.create(serviceClass);
    }
 
}
 

 

RetrofitUrl.java:

public class RetrofitUrl {
    public static final String BASE_URL = "http://www.abc.com/";
 
    public static final String PdfData = "androidSample/getPDFData.php";
}

 

 

MainActivity.java:

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.ObservableEmitter;
import io.reactivex.rxjava3.core.ObservableOnSubscribe;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Function;
import io.reactivex.rxjava3.schedulers.Schedulers;
import okhttp3.ResponseBody;
import okio.BufferedSink;
import okio.Okio;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
 
public class MainActivity extends AppCompatActivity implements BindPdfViewListAdapter.OnItemClickListener {
    private final String TAG = this.getClass().getSimpleName();
    Context mContext;
    
    @Override
    public void onItemClicked(View view, Pdf_Item item, int position) {
        Toast.makeText(getApplicationContext(), "잠시 기다리시면 "+item.getTitle()+" PDF 파일 열람이 가능합니다", Toast.LENGTH_LONG).show();
        String PDFUrl = RetrofitUrl.BASE_URL+item.getPdfurl();
        downloadPDFFile(item.getPdfurl());
    }
 
    public void downloadPDFFile(String fileUrl) {
        RetrofitService downloadService = RetrofitAdapter.createService(RetrofitService.class);
        downloadService.downloadFile(fileUrl)
                .flatMap(processResponse())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(handleResult());
    }
 
    public Function<Response<ResponseBody>, Observable<File>> processResponse(){
        return new Function<Response<ResponseBody>, Observable<File>>() {
            @Override
            public Observable<File> apply(Response<ResponseBody> responseBodyResponse) throws Exception {
                return saveToDiskRx(responseBodyResponse);
            }
        };
    }
 
    private Observable<File> saveToDiskRx(final Response<ResponseBody> response){
        return Observable.create(new ObservableOnSubscribe<File>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<File> subscriber) throws Throwable {
                File filePath = new File(Environment.getExternalStorageDirectory() + "/download");
                File outputFile = new File(filePath, "tempPDF.pdf");
                if (outputFile.exists()) { // 기존 파일 존재시 삭제하고 다운로드
                    outputFile.delete();
                }
 
                BufferedSink bufferedSink = Okio.buffer(Okio.sink(outputFile));
                bufferedSink.writeAll(response.body().source());
                bufferedSink.close();
 
                subscriber.onNext(outputFile);
                subscriber.onComplete();
            }
        });
    }
 
    private Observer<File> handleResult() {
        return new Observer<File>() {
 
            @Override
            public void onSubscribe(@NonNull Disposable d) {
                Log.d(TAG, "OnSubscribe");
            }
 
            @Override
            public void onNext(@NonNull File file) {
                Log.d(TAG, "File downloaded to " + file.getAbsolutePath());
                // 다운로드한 파일 실행하여 PDF 보기 진행하는 코드
                if (Build.VERSION.SDK_INT >= 24) {
                    openPDF(file);
                } else {
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    Uri apkUri = Uri.fromFile(file);
                    intent.setDataAndType(apkUri, "application/pdf");
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    getApplicationContext().startActivity(intent);
                }
            }
 
            @Override
            public void onError(@NonNull Throwable e) {
                e.printStackTrace();
                Log.d(TAG, "Error " + e.getMessage());
            }
 
            @Override
            public void onComplete() {
                Log.d(TAG, "onCompleted");
            }
        };
    }
 
    void openPDF(File file) {
        Uri fileUri = FileProvider.getUriForFile(mContext, mContext.getApplicationContext().getPackageName() + ".fileprovider",file);
 
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(fileUri, "application/pdf");
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        try {
            startActivity(intent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
 

 

전체 소스코드

https://github.com/jsk005/JavaProjects/tree/master/pdfviewer

 

GitHub - jsk005/JavaProjects: 자바 기능 테스트

자바 기능 테스트. Contribute to jsk005/JavaProjects development by creating an account on GitHub.

github.com

 

참고자료

https://androiderrors.com/rxandroid-and-retrofit-unable-to-create-call-adapter-for-io-reactivex-observableretrofit2-responseokhttp3-responsebody/

https://www.codexpedia.com/android/retrofit-2-and-rxjava-for-file-downloading-in-android/

 

이 자료는 참고하지 않았지만 나중에 보면 도움되는 것이 있을 수도 있겠다 싶어 URL를 적어둔다.

https://www.digitalocean.com/community/tutorials/android-rxjava-retrofit

 

 

 

 

 

 

 

블로그 이미지

Link2Me

,