RxJava2, RxAndroid2

[RxAndroid2] 스마트폰에 설치된 앱 목록 보기

알통몬_ 2018. 10. 10. 10:17
반응형


공감 및 댓글은 포스팅 하는데

 아주아주 큰 힘이 됩니다!!

포스팅 내용이 찾아주신 분들께 

도움이 되길 바라며

더 깔끔하고 좋은 포스팅을 

만들어 나가겠습니다^^

 



이번 포스팅에서는 RxAndroid2를 사용한 간단한 예제를 보려고 합니다.


스마트폰에 설치된 애플리케이션 목록을 보는 예제인데요.


앱 레벨 gradle

android {} 블록에 

dataBinding {
enabled true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

를 추가해주시고,


 dependencies {} 블록에

implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
kapt 'com.android.databinding.compiler:3.2.0'

implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'com.trello.rxlifecycle2:rxlifecycle:2.2.2'
implementation 'com.trello.rxlifecycle2:rxlifecycle-android:2.2.2'
implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.2'

를 추가해주세요.

저는 코틀린도 사용해서 위에 2개를 추가해주었는데,

코틀린을 안쓰시면 아래 5개만 추가하시면 됩니다.


AppInfo.kt 데이터 클래스입니다.

package park.sunggyun.thomas.rxandroidex

import android.graphics.drawable.Drawable

data class AppInfo(val name : String, val icon : Drawable )


recyclerView에 사용될 item_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imgView"
android:contentDescription="@string/app_name"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/txtView"
/>

<TextView
app:layout_constraintStart_toEndOf="@+id/imgView"
app:layout_constraintEnd_toEndOf="parent"
android:id="@+id/txtView"
android:textColor="@android:color/black"
android:textSize="20sp"
android:layout_width="0dp"
android:layout_height="64dp"
android:gravity="center|start"
tools:text="asd"/>

</android.support.constraint.ConstraintLayout>
</layout>


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<android.support.v7.widget.RecyclerView
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/rxRecyclerView"
android:layout_width="0dp"
android:layout_height="0dp"/>

</android.support.constraint.ConstraintLayout>
</layout>


xml 부분은 그냥 UI를 구성하는 부분이니 그냥 넘어가겠씁니다.


MainAdapter.java

package park.sunggyun.thomas.rxandroidex;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.Vector;

import io.reactivex.Observable;
import io.reactivex.subjects.PublishSubject;
import park.sunggyun.thomas.rxandroidex.databinding.ItemMainBinding;

public class MainAdapter extends RecyclerView.Adapter<MainAdapter.MainViewHolder> {

private Vector<AppInfo> appInfoVector = new Vector<>();
Context context;

MainAdapter(Context context) {
this.context = context;
this.publishSubject = PublishSubject.create();
}

private PublishSubject<AppInfo> publishSubject;

@NonNull
@Override
public MainViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
ItemMainBinding binding = ItemMainBinding.inflate(LayoutInflater.from(context), viewGroup, false);
return new MainViewHolder(binding);
}

@Override
public void onBindViewHolder(@NonNull MainViewHolder mainViewHolder, int position) {
ItemMainBinding binding = mainViewHolder.binding;

AppInfo appInfo = appInfoVector.get(position);
Log.e("getName", appInfo.getName());
binding.imgView.setImageDrawable(appInfo.getIcon());
binding.txtView.setText(appInfo.getName());

mainViewHolder.getClickObserver(appInfo).subscribe(publishSubject);

}

@Override
public int getItemCount() {
return appInfoVector.size();
}

void update(AppInfo appInfo) {
appInfoVector.add(appInfo);
}

PublishSubject<AppInfo> getPublishSubject() {
return this.publishSubject;
}

class MainViewHolder extends RecyclerView.ViewHolder {
ItemMainBinding binding;
View view;
MainViewHolder(ItemMainBinding binding) {
super(binding.getRoot());
view = binding.getRoot();
this.binding = binding;
}

Observable<AppInfo> getClickObserver(AppInfo appInfo) {
return Observable.create(event -> view.setOnClickListener(view -> event.onNext(appInfo)));
}
}
}

PublishISubject 로 구독자가 구독을 했을 때(subscribe()를 호출하면)

데이터를 발행해줍니다.

Adapter 클래스에는 데이터를 받았을 때 어떻게 처리할 것인지만

정의되어있고, update(AppInfo appInfo) 를 통해

설치된 애플리케이션의 정보를 추가하게 됩니다.


MainActivity.java

package park.sunggyun.thomas.rxandroidex;


import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.databinding.DataBindingUtil;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.util.Log;
import android.widget.Toast;

import com.trello.rxlifecycle2.components.support.RxAppCompatActivity;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import park.sunggyun.thomas.rxandroidex.databinding.ActivityMainBinding;


public class MainActivity extends RxAppCompatActivity {

MainAdapter adapter;


@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.rxRecyclerView.setLayoutManager(new LinearLayoutManager(this));
Log.e("onCreate()", "application started!");
adapter = new MainAdapter(this);
binding.rxRecyclerView.setAdapter(adapter);
adapter.getPublishSubject()
.subscribe(s ->
Toast.makeText(getApplicationContext(), s.getName(), Toast.LENGTH_SHORT).show()
).isDisposed();

} catch (Exception e) {
e.printStackTrace();
}
}

@Override
protected void onStart() {
super.onStart();
if (adapter == null) {
return;
}

getAppInfo()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(item -> {
Log.e("getAppInfo", item.getName());
adapter.update(item);
adapter.notifyDataSetChanged();
}).isDisposed();
}

private Observable<AppInfo> getAppInfo() {
final PackageManager manager = this.getPackageManager();
Intent i = new Intent(Intent.ACTION_MAIN, null);
i.addCategory(Intent.CATEGORY_LAUNCHER);

return Observable.fromIterable(manager.queryIntentActivities(i, 0))
.sorted(new ResolveInfo.DisplayNameComparator(manager))
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.map(item -> {
Drawable img = item.activityInfo.loadIcon(manager);
String title = item.activityInfo.loadLabel(manager).toString();
return new AppInfo(title, img);
});
}
}

getAppInfo() 메서드에서 Observable<AppInfo> 를 반환합니다.

설치된 애플리케이션들의 이름과 아이콘을 가져와서 AppInfo 클래스에 담고,

Observable<AppInfo> 형태로 반환해주는 것이죠.

받은 정보를 가지고 onStart() 메서드에서 구독을 합니다.

그리고 AppInfo 데이터를 하나씩 구독할 때마다

apdater.update(item);

adapter.notifyDataSetChanged(); 

로 RecyclerView를 갱신해줍니다.

이렇게 간단하게 RxAndroid를 사용한 예제를 보았는데요.
깃허브에 소스를 공유해두었습니다.

다음 포스팅에서는 안드로이드의 스레드를 대체하는 RxAndroid에 대해 공부합니다.


반응형