반응형

자바에서 코드를 작성 시 모델을 만들다 보면 constructor와 기본 getter/setter.. 그리고 상황에 따라서 builder를 만들어 사용해야 합니다.

그런데 이런 일들을 모두 타이핑 하다 보니 보일플레이트 같은 코드들이 많이도 써야 합니다. 코드 제너레이터들이 있어서 편하게 할 수 있긴 하지만 더 편한 방법이 없을까요?

저같은 경우는 lombok을 사용해서 모델 객체들의 불필요한 보일플레이트 코드들을 줄이고 있습니다. annotation방법으로 사용하기 때문에 사용방법도 간단합니다.



1. @Getter / @Setter

기본적으로 멤버필드들에 대한 getter/setter메소드들을 만들어 줍니다.


2. @AllArgsConstructor / @NoArgsConstructor

멤버필드들이 모두 파라미터로 지정된 생성자와 빈 생성자를 만들어 줍니다.


3. @Builder

모델을 빌더 패턴으로 만들어 줍니다.


4. @ToString

toString의 override된 메소드를 만들어 줍니다.


그 외에도 여러 가지가 있지만 lombok features 에서 더 많은 것을 확인할 수 있습니다.



그럼 AndroidStudio에 적용하는 방법을 알아보도록 하죠.


1. 우선 lombok plugin을 설치해야 합니다.

Preference -> Plugins -> Browse Repositories 에서 lombok을 검색하면 Lombok Plugin이 나옵니다. 이걸 설치해 줍니다.


2. gradle에 lombok적용하기.

"""

provided "org.projectlombok:lombok:1.16.8"

"""


3. 트러블슈팅??

 - package javax.annotation does not exist

annotation does not exist...이걸 해결해 주기 위해서 gradle에 annotation을 provide해줍니다.

"""

provided 'org.glassfish:javax.annotation:10.0-b28'

"""


 - cannot find symbol class ConstructorProperties

lombok.config 설정파일을 추가해 줘야 합니다. 프로젝트와 같은 root에 lombok.config파일을 만들고

'''

lombok.anyConstructor.suppressConstructorProperties = true

'''

와 같은 내용을 추가해 줍니다.

자세한 내용은 Configuration system 에서 확인할 수 있습니다.




이와같은 과정 후에 결과를 보게 된다면

AndroidStudio의 structures view를 보게 되면

이와 같은 결과를 볼 수 있습니다.


이제 lombok을 이용해서 코드 다이어트를!!

반응형
반응형

Sqlite는 안드로이드를 사용하면서 가장 많이 사용하는 db가 아닐 까 생각됩니다. 나온지도 오래 됐고, 처음부터 안드로이드에 적용하여 사용하다보니 안정성도 보장되어 있구요. 그런데 sqlite를 안드로이드에서 이용하기 위해서는 보일플레이트 코드가 많이 들어갑니다. Helper객체와 Provier객체가 필요하죠. 상황에 따라서는 Provider객체를 만들지 않고 사용할 수도 있지만요.

이번에 사용해보면서 나름 괜찮다고 생각되는 라이브러리입니다. Square에서 나온 Sqlbrite입니다. React programming을 할 수 있도록 도와줍니다.

sqlbrite git주소에 가면 dagger를 이용한 샘플이 존재합니다. 그런데 이 샘플은 square에서 내놓은 dagger1이죠. 

그래서 전 Dagger2를 이용한 샘플을 준비해 볼 까 합니다.

이전에 Dagger2를 이용한 개발 방법을 얘기 한 적이 있습니다. 

Android 개발에서 Dagger2이용해보기.

Dagger1과 Dagger2는 DI관점에서는 차이가 없지만, 사용방법에 대해서는 조금 차이가 있습니다. 그건 

http://google.github.io/dagger/dagger-1-migration.html

의 내용을 보면 알기 쉬울 듯 합니다.

아직 library버전이 v0.x대의 버전입니다. 사실 오픈소스 라이브러리를 v1.x대가 아니라면 사용하는데 꺼려지긴 합니다. 그래서 이걸 사용하는데 믿음직한 이유가 제이크 왓슨 형님과 스퀘어에서 내놨다는 이유이지 않을 까 합니다 하하.

샘플은 

https://github.com/drcarter/Dagger2Example

에 있습니다. Sqlbrite에 있는 샘플과 같은 내용이지만 단지 Dagger2로 변경한 부분입니다.

반응형
반응형

얼마전 제 블로그의 포스트에서 Dagger2를 이용하는 방법을 간단하게 적어보았습니다.

Android 개발에서 Dagger2이용해보기.

그런데 여기서 @Scope에 대한 설명이 없었던 것 같습니다.

@Scope ... 실질적으로는 Dagger2에서는 @Singleton annotation을 많이 사용합니다. Singleton은 굳이 자세히 설명을 안해도 다들 잘 알고 계시리라 봅니다. 객체 주입에서 @Singleton annotation을 적용해 두면.. 전체 App의 Scope안에서 Singleton객체가 생성되어 주입 됩니다. 그렇다면 다른 custom scope는 없을까요? 물론 @Scope annotation의 custom을 만들어서 사용합니다. 예를 들어서 Activity life cycle에서만 존재할 수 있는 객체 주입을 위해선 @PerActivity라던지 아니면 fragment life cycle에서만 객체 주입이 유지될 수 잇는 @PerFragment같은 custome scope annotation을 만들어 사용할 수 있습니다.



http://fernandocejas.com/wp-content/uploads/2015/04/composed_dagger_graph1.png


그럼 @Scope 를 이용한 예를 들어 볼 까 합니다.

이전에 예를 들었던 포스트인

Fragment에서 onActivityResult 결과 받기.

의 내용을 Dagger2를 이용한 방법으로 변경할려고 합니다. 주된 내용은 기존의 내용에서 EventBus객체를 Singleton pattern으로 생성해서 사용했습니다. 그럼 Dagger2를 이용해서 해당 샘플을 변경하면 어떻게 될까요?

우선 EventBus.java 입니다.

public class EventBus extends Bus {
private Handler handler = new Handler(Looper.getMainLooper());
@Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
super.post(event);
} else {
handler.post(new Runnable() {
@Override
public void run() {
EventBus.super.post(event);
}
});
}
}
}

보시는 것과 같이 일반 객체와 별 다를 게 없습니다.


그럼 EventBusModule.java 입니다.

@Module
public class EventBusModule {
@Singleton
@Provides
EventBus provideEventBus() {
return new EventBus();
}
}

해당 모듈에서 EventBus를 제공해줄 때 @Singleton annotation이 붙었습니다. 이건 해당 객체를 Singleton으로 제공해 준다는 겁니다.


그리고 EventBusModule의 객체를 Inject를 하기 위한 Bridge역활을 해주는 ApplicationComponent.java 입니다.

@Singleton
@Component(
modules = {
ApplicationModule.class,
EventBusModule.class
}
)
public interface ApplicationComponent {
EventBus getEventBus();
}

해당 Component도 @Singletone으로 적용해두었습니다.


해당 Component자체도 Singleton scope에서 사용하겠다는 내용 입니다.


만약 Activity life cycle에서만 사용할 수 있는 scope를 만든다면

@Scope
@Retention(RUNTIME)
public @interface PerActivity {
}

로 만든다면 원하는 scope에다가 @PerActivity annotation을 적용하면 됩니다.


예제는..

https://github.com/drcarter/RetriveActivityResult 에서 retrive_activity_result_dagger 브랜치를 보시면 됩니다.

https://github.com/drcarter/RetriveActivityResult/tree/retrive_activity_result_dagger

반응형
반응형

개발을 여러 사람과 같이 하게 되면 각자만의 코드 스타일이 다르기 때문에 코딩 컨벤션은 어느정도 필요하다는 것을 알 수 있습니다. 그렇다고 누구 한명의 기준에 맞출 수도 없는 거죠.

안드로이드 개발자 사이트에 가면 자세히도 코딩 가이드라인이 존재합니다. 사실 개발하다보면 잘 안지키긴 하죠

Code Style Guidelines for Contributors

그럼 이 가이드라인을 지킬 수 있는 설정파일도 존재합니다.

https://github.com/android/platform_development/blob/master/ide/intellij/codestyles/AndroidStyle.xml

그럼 이 설정파일을 적용하는 방법입니다.


1. https://github.com/android/platform_development/blob/master/ide/intellij/codestyles/AndroidStyle.xml에 있는 파일을 다운로드 한다.


2. codestyle에 복사하기.

 - MacOS : ~/Library/Preferences/AndroidStudio{version}/codestyles/AndroidStyle.xml

 - Linux : ~/.AndroidStudio{version}/config/codestyles/AndroidStyle.xml

 - Windows : C:\Users\XXX\.AndroidStudio{version}\config\codestyles\AndroidStyle.xml


3. Preferences (or Settings) -> Editor -> Code Style -> Scheme 



이렇게 하면 코드 스타일이 미리 적용 됩니다.


반응형
반응형

Guava: Google Core Libraries for Java

  • Base

 - Objects.equal() : equal 비교시 null 체크를 하지 않아도 된다.

 - Objects.hashCode() : hash코드 생성을 보다 쉽게 만들 수 있다.

 - Objects.toStringHelper() : toString객체를 보다 쉽게 만들 수 잇다.

  -> 일반적인 코드 :

public class Book {
private String title;
private String writer;
private int price;

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getWriter() {
return writer;
}

public void setWriter(String writer) {
this.writer = writer;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

Book book = (Book) o;

if (price != book.price) return false;
if (title != null ? !title.equals(book.title) : book.title != null) return false;
return !(writer != null ? !writer.equals(book.writer) : book.writer != null);

}

@Override
public int hashCode() {
int result = title != null ? title.hashCode() : 0;
result = 31 * result + (writer != null ? writer.hashCode() : 0);
result = 31 * result + price;
return result;
}

@Override
public String toString() {
return "Book{" +
"title='" + title + '\'' +
", writer='" + writer + '\'' +
", price=" + price +
'}';
}
}

  -> Guava를 이용한 코드

public class Book {
private String title;
private String writer;
private int price;

public Book(String title, String writer, int price) {
this.title = title;
this.writer = writer;
this.price = price;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
Preconditions.checkNotNull(title);
this.title = title;
}

public String getWriter() {
return writer;
}

public void setWriter(String writer) {
this.writer = writer;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return Objects.equal(this.title, book.title) && Objects.equal(this.writer, book.writer)
&& this.price == book.price;

}

@Override
public int hashCode() {
return Objects.hashCode(this.title, this.writer, this.price);
}

@Override
public String toString() {
return Objects.toStringHelper(this)
.add("title", title)
.add("writer", writer)
.add("price", price)
.toString();
}
}

작성하다보니 Objects.toStringHelper 가 deprecated가 되었네요.. 이건 MoreObject.toStringHelper()를 이용하면 됩니다.

 - Preconditions : 객체 정보의 null과 같은 정보가 있는지를 미리 확인하여 exception을 낸다.

  -> 일반적인 코드

public void setTitle(String title) {

if(title == null) {
throw new NullPointerException("title must not be null!");
}

this.title = title;
}

  -> Preconditions를 이용

public void setTitle(String title) {
Preconditions.checkNotNull(title);
this.title = title;
}

 - Joiner : Collections객체들이나 array객체들의 정보들을 string으로 변경해준다.

List names = new ArrayList<>();
names.add("test1");
names.add("test2");
names.add("test3");

Joiner.on(", ").join(names); // test1, test2, test3

 - Splitter : Joiner와 반대로 split 캐릭터를 기준으로 Iterator객체로 변환하여 준다.

Iterator names = Splitter.on(",")
.trimResults()
.omitEmptyStrings()
.split("test1, test2, test3").iterator();

names.next(); //test1
names.next(); //test2
names.next(); //test3

  • Collections
 - Collections객체를 생각하면 기본적으로 List, Set, Map등을 생각합니다. 그리고 이런 Collection 객체들을 생성할 때 new 를 통해 객체 생성을 하죠.
기본적으로 List 를 본다면 new ArrayList<Strig>();와 같이 합니다. 하지만 Guava에서는 static factory 생성자를 이용합니다.

Lists.newArrayList();
Sets.newHashSet();
Maps.newHashMap();

 - Immutable Collections : 변경 불가능한 Collection.. 객체들에 대한 의도하지 않은 변경을 막고 Thread-Safe 한 객체를 만들 수 있다.

Set<String> nameSet = Collections.unmodifiableSet(new LinkedHashSet<String>(Arrays.asList("test1", "test2", "test3")));
Set<String> nameSet = ImmutableSet.of("test1", "test2", "test3");

이와 같이 변경불가능 객체를 만들 때 기존 코드보다 간결하게 만들 수 있다. Set 이와에 Map에 대해서도 알아보면.

Map<String, Integer> mapValue1 = new LinkedHashMap<>();
mapValue1.put("test1", 1);
mapValue1.put("test2", 2);
mapValue1.put("test3", 3);
Map<String, Integer> unModifiedMap = Collections.unmodifiableMap(mapValue1);

Map<String, Integer> mapValue2 = ImmutableMap.<String, Integer>builder()
.put("test1", 1).put("test2", 2).put("test3", 3)
.build();

Map<String, Integer> mapValue3 = ImmutableMap.of("test1", 1, "test2", 2, "test3", 3);

이와 Immutable Collection이 있는 것들은
  • ImmutableCollection
  • ImmutableList
  • ImmutableSet
  • ImmutableSortedSet
  • ImmutableMap
  • ImmutableSortedMap
  • ImmutableMultiset
  • ImmutableSortedMultiset
  • ImmutableMultimap
  • ImmutableListMultimap
  • ImmutableSetMultimap
  • ImmutableBiMap
  • ImmutableClassToInstanceMap
  • ImmutableTable
이렇게 존재한다. 기존 java 코드보다는 훨씬 간결한 코드를 이용할 수 있다.
 - BiMap : key에 대한 unique한 값을 보장하는게 아니라 value에 대한 unique 성을 보장한다.
 - MultiSet : element의 중복을 허용한다.
 - MultiMap : Map이 Key의 중복을 허용하지 않지만,  MultiMap은 Key의 중복을 허용한다. MultiMap은 Map<K, Collection<V>>의 특성을 가지고 있다.

  • Functional programming

public interface Function {
@Nullable T apply(@Nullable F input);
}

이와 같이 F를 입력 받아서 T의 결과를 리턴한다.
예를 든다면 마일을 입력받아서 킬로미터로 출력한다.

Function<Integer, Double> calcMileToKillometer = new Function<Integer, Double>() {
@Override
public Double apply(@Nullable Integer input) {
return input * 1.60934;
}
};

double miles = calcMileToKillometer.apply(1);

- filter / transform
 객체에 있는 특정 값들이 속한 객체들만 골라낸 Collection을 만들거나, 특정 값들만 뽑아낸 새로운 Collection을 만들어 낼 수 있다.

List<Book> bookList = ImmutableList.of(new Book("Android", "test", 100), new Book("Java", "test2", 200), new Book("Object", "test3", 300));
List<Book> androidBookList = ImmutableList.copyOf(Collections2.filter(bookList, new Predicate<Book>() {
@Override
public boolean apply(@Nullable Book input) {
return "Android".equals(input.getTitle());
}
}));

List<String> titleList = ImmutableList.copyOf(Collections2.transform(bookList, new Function<Book, String>() {
@Override
public String apply(@Nullable Book input) {
return input.getTitle();
}
}));


우선 제가 자주 사용하는 것들로만 정리..
내부적으로 아직 @Beta annotation이 된 것들도 많아서.. 아직 그것들에 대해서는 적어두진 않았습니다. 


반응형
반응형


안드로이드 개발하는 개발 패턴으로는 MVC( Model - View, Controller)와, MVVM(Model - View, ViewModel), 그리고 MVP(Model - View - Presenter)방법이 있습니다. 이 세가지 방법에는 각자만의 스타일과 장단점을 가지고 있구요. 안드로이드 개발자들이 많이 늘어나고 좀더 편한 개발 방법들을 추가하면서 나오는 방법들이지 않을 까 합니다.

1. MVC ( Model - View - Controller )

 - Model : 안드로이드에서 사용될 데이터들.

 -  View : activity_main.xml 과 같은 view layout

 - Controller : Activity, Fragment 들...

알게 모르게 가장 많이 사용된 패턴이지 않을 까 생각됩니다. 그런데 View와 Controller의 coupling이 너무 강합니다. 누구 하나 땔래야 땔 수 없는 구조이죠. 우리가 알고 있는 좋은 구조는 loose coupling이 많아야 좋은 구조라고 배웠을 겁니다. 그래서 장기적으로 봤을 대 어떤 구조로 가야 좋을 지 생각해 볼 필요가 있습니다.


2. MVVM ( Model - View - ViewModel )

ViewModel은 Controller와 View의 구조를 통합적으로 가지고 있다고 보시면 될 거 같습니다. ViewModel을 독립적으로 가져갈 수 있지만, 나중에 View가 변경된다면 ViewModel을 한번에 다 변경해야 할 필요도 있어 보입니다. 가끔 Git에서 안드로이드 ui 라이브러리나 componet를 보면 mvvm으로 구현된 것들이 많았던 것으로 기억됩니다. 이 방법이 그나마 View에 대한 가장 독립적인 방법이지 않았을 까 생각됩니다. 


3. MVP ( Model - View - Presenter )

전체적인 패턴이 MVC와 비슷합니다. Controller가 Presenter로 보시면 될 듯 하지만 틀린 점이라면 View에 대한 관리와 접근은 Activity에서 하고, Model에 대한 컨트롤러 역활은 Presenter에서 하게 됩니다. 


이 세 방법에 대한 생각은 제 주관적인 생각입니다. MVC, MVVM, MVP 뭐 하나 나쁘다 좋다 할 방법은 없다고 생각 됩니다.  각자만의 장단점이 있고 누가 어떻게 사용하느냐 그리고 어떤 프로젝트 인지에 따라서도 많이 틀려질 것으로 보입니다. 저는 최근에 프로젝트를 MVP방법으로 진행했습니다. MVP를 사용하면서 경험을 해봤을 대 각각 구조에 대해서 loose coupling적인 방법으로 구현을 할 수 있었지만, 너무 독립적으로 진행하다보니 해당 interface에 대한 구현이 많아지는 것을 느낄 수 있었습니다. 다른 프로젝트를 진행한다면 다시 MVP방법을 사용해 볼려고는 합니다. 단지 저는 MVC, MVVM방법보다는 그래도 가장 느슨한 결합도를 가졌다고 보여집니다.

공부하면서 만든 샘플은 git에 올려 둡니다. 그냥 참고 자료에요.

https://github.com/drcarter/AndroidDevPattern

반응형
반응형

Dagger2

url : http://google.github.io/dagger/

요즘 안드로이드 개발에서 Dependency Injection방법을 이용한 개발 방법이 이슈로 자리잡고 있는 듯 합니다. 더군다나 네임드 개발자로 알려진 Jake Wharton 형님이 발표한 자료도 있습니다.

https://speakerdeck.com/jakewharton/dependency-injection-with-dagger-2-devoxx-2014

2라는 숫자가 있는 것을 보면 Dagger1이 있다는 얘기가 됩니다. 처음 Dagger는 Square에서 나왔습니다. 

http://square.github.io/dagger/

이 사이트에 가시면 자세한 설명을 보실 수 있어요. 나중에 Dagger에 대한 샘플을 작성해 볼 생각입니다.


Dagger2를 얘기한 이유는 프로젝트가 google github에 있다는 것 때문입니다. 믿고 써보는 google??이라서?


Dagger2에서 중요한 Annotation은  @Module, @Provides, @Component, @Inject 정도 일 것 같습니다.

@Module : Module로 구성되고, 내부에서 주입될 객체들을 provide해주는 것.

@Provides : 객체 주입에 필요한 내용을 리턴해주는것

@Component : Module과 Inject간의 Bridge같은 역활.

@Inject : 객체의 주입.

대충 이런정도의 설명... 저는 참 설명을 못하네요 ㅡ.ㅡ


Dagger2를 이용하기 위한 전반적인 순서 입니다.

gradle 스크립트에 아래와 같은 내용을 추가 해 줍니다.

apply plugin: 'com.neenbedankt.android-apt'

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
    }
}

android {
    ........

    dependencies {
        .......
        provided 'javax.annotation:jsr250-api:1.0'
        apt 'com.google.dagger:dagger-compiler:2.0'
        compile 'com.google.dagger:dagger:2.0'
    }
}


그리고 사용할 모델들을 정의 해 줍니다. 저는 아래와 같은 예제를 만들어 보았습니다.

Motor.java

public class Motor {

    private int speed;

    public Motor() {
        this.speed = 0;
    }

    public int getSpeed() {
        return speed;
    }

    public void accelerate(int value) {
        speed = speed + value;
    }

    public void setSpeed(int value) {
        speed = value;
    }

    public void brake() {
        speed = 0;
    }
}


Bike.java

public class Bike {

    private Motor motor;

    public Bike(@NonNull Motor motor) {
        this.motor = motor;
    }

    public void increaseSpped() {
        this.motor.setSpeed(this.motor.getSpeed() + 1);
    }

    public void decreaseSpeed() {
        if (this.motor.getSpeed() > 0) {
            this.motor.setSpeed(this.motor.getSpeed() - 1);
        }
    }

    public int getSpeed() {
        return this.motor.getSpeed();
    }
}


그리고 Module을 정의 합니다.

우선 MotorModule.java 입니다. 이건 단순히 Motor객체를  제공해 주는 역활만 합니다.

@Module
public class MotorModule {

    @Provides
    Motor provideMotor() {
        return new Motor();
    }
}


BikeModule.java입니다.

@Module(
        includes = MotorModule.class
)
public class BikeModule {

    @Provides
    Bike provideBike(Motor motor) {
        return new Bike(motor);
    }
}


여기서 보면 Bike객체를 제공해주는 @Provides을 보면 Motor객체가 파라미터로 존재합니다. 과연 이 파라미터는 어디서 제공받고 있을까 라는 의문이 듭니다. 이건 @Module의 includes부분에 보면 MotorModule.class를 포함하고 있고, MotorModule에서 Motor객체를 provides하고 있기에 가능합니다.

이렇게 되면 BikeModule안에서 Motor객체를 필요로 하는 곳에서는 모두 파라미터로 정의하면 Motor객체를 제공받을 수 있습니다.

그리고 Provides된 것을 Inject 하여 사용할 수 있는데 Bike객체를 Inject하여 사용해 볼까 합니다.

그럼 Bike는 어디에서 사용할 까요? 위에서 중요 Annotation중에 @Component를 알려드렸습니다. Module과 Inject간의 Brige같은 역활을 한다고 했는데 사용할 Component는 아래와 같습니다.

MainActivityComponent.java

@Component(
        ...
        modules = {
                ...
                BikeModule.class
        }
)
public interface MainActivityComponent extends ActivityComponent {
    void inject(@NonNull MainActivity mainActivity);
}


이와 같이 Component를 정의했습니다.

이렇게 Component를 정의하고 컴파일 하면 "DaggerMainActivityComponent가" 만들어 집니다. 이걸 가지고 실질적으로 객체 주입을 진행할 수 있죠.

DaggerMainActivityComponent.builder()
        ...
        .bikeModule(new BikeModule())
        .build();


이와 같이 하면 사용할 Component에 대해서는 준비가 끝납니다. 그럼 객체 주입은 어떻게 할까요?  몇가지 방법이 있지만 저는 객체에 @Inject anootation방법을 이용해서 사용 할려고 준비했습니다.


@Inject
Bike bike;

이와 같이 하여 Bike객체 사용에 대한 준비를 합니다.

이건 component에서 inejct라는 메소드를 정의해서 상용할 수 있는 부분입니다 그럼  inject라는 것을 하지 않고 사용할 방법은 있을까요? Component에서 getBike()라는 메소드를 만들어서 사용할 방법도 있긴 합니다.

이 예제에 대한 소스는

https://github.com/drcarter/Dagger2Example

에 올려둿습니다..

저도 Dagger2에 대해서 공부하고 정리하다 보니 설명이 명확하진 않은 듯 합니다.

오히려 맨 위에 Jake Wharton형님이 pt로 설명한 내용이 더 좋을지도요 ㅎㅎㅎ


반응형
반응형

안드로이드에서 onActivityResult는 이전 activity에서의 결과를 intent를 통해서 받을 수 있습니다. 개발문서 참고

당연히 Fragment안에서도 onActivityResult가 있고, fragment안에서 startActivityForResult를 통해서 해당 결과를 받을 수 있습니다.


/**
     * Call {@link Activity#startActivityForResult(Intent, int)} from the fragment's
     * containing Activity.
     */
    public void startActivityForResult(Intent intent, int requestCode) {
        if (mHost == null) {
            throw new IllegalStateException("Fragment " + this + " not attached to Activity");
        }
        mHost.onStartActivityFromFragment(this /*fragment*/, intent, requestCode);
    }

이와 같이 fragment에서 startActivityForResult를 하기 위해선 fragment가 activity에 attach되어 있어야 합니다.

fragment에서 startActivityForResult를 Activity에서 보낸 것처럼 할려면

getActivity().startActivityForResult(.......)

로 해서 보내면 됩니다. 이렇게 해서 보내면 onActivityResult로 받게 되는 결과가 fragment에서 바로 받는 것이 아니라 activity에 있는 onActivityResult에서 받게 되어서 이 결과를 바로 fragment에 전달해 줘야 하죠.... 이 부분이 조금 껄끄러운 면이 있습니다. Lollipop에서 material design을 적용하여 Activity Transition이나 Fragment Transition을 적용할려면, onStartActivityForResult의 호출지점이 fragment가 아닌 activity에서 하는 것처럼 보내야 합니다.

그럼 Activity의 onActivityResult로 받은 결과를 어떻게 fragment에 전달하는 것이 좋을까요?

여러가지 방법이 있을 듯 합니다. 가장 쉬운 방법으로는 Activity에서 해당 fragment의 instance를 두고서 전달할 public method를 만들어 전달 할 방법이 있을 것 같습니다. 

제가 이번에 소개해볼 방법은 event bus[Otto - An event bus by Square]를 이용한 방법을 소개해 볼까 합니다. 

ActivityResultEvent.java 로 만든 class 입니다.


public class ActivityResultEvent {

    private int requestCode;
    private int resultCode;
    private Intent data;

    public static ActivityResultEvent create(int requestCode, int resultCode, Intent intent) {
        return new ActivityResultEvent(requestCode, resultCode, intent);
    }

    private ActivityResultEvent(int requestCode, int resultCode, Intent data) {
        this.requestCode = requestCode;
        this.resultCode = resultCode;
        this.data = data;
    }

    public int getRequestCode() {
        return requestCode;
    }

    public int getResultCode() {
        return resultCode;
    }

    public Intent getData() {
        return data;
    }
}

이건 단순히 onActivityResult로 받는 requestCode, resultCode, intent정보를 가지고 fragment에 전달해 주는 용도 입니다.

EventBus.java


public class EventBus extends Bus {

    private static EventBus instance;

    public static EventBus getInstance() {
        if (instance == null) {
            synchronized (EventBus.class) {
                if (instance == null) {
                    instance = new EventBus();
                }
            }
        }
        return instance;
    }

    private Handler handler = new Handler(Looper.getMainLooper());

    @Override
    public void post(final Object event) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            super.post(event);
        } else {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    EventBus.super.post(event);
                }
            });
        }
    }
}

이건 ActivityResultEvent를 보내줄 Bus입니다.. 이 Bus의 이용 방법은 위에 링크 걸린 홈페이지에 자세히 설명 되어 있어요 

해당 EventBus를 Singleton으로 만들었습니다. 


이 부분의 사용 방법은

activity의 onActivityResult에서


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//        super.onActivityResult(requestCode, resultCode, data);
        EventBus.getInstance().post(ActivityResultEvent.create(requestCode, resultCode, data));
    }

이와 같이 호출하면 되고,

fragment에서는 

    @SuppressWarnings("unused")
    @Subscribe
    public void onActivityResultEvent(@NonNull ActivityResultEvent event) {
        onActivityResult(event.getRequestCode(), event.getResultCode(), event.getData());
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
//        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case Constants.RequestCode.REQUEST_CODE_NAME_INPUT: {
                if (resultCode == Activity.RESULT_OK) {
                    String text = data.getStringExtra(KeySets.KEY_NAME_INPUT);
                    if (!TextUtils.isEmpty(text)) {
                        textResult.setText(text);
                    }
                }
                break;
            }
        }
    }

이와 같이 Bus를 통해 받은 정보를 fragment의 onActivityResult로 전달하면 됩니다.

Bus를 통해서 정보 공유는 이요할 곳이 참 많아 보입니다. Service에서 activity나 fragment에 결과의 내용을 전달 할 때 사용해도 되구요.

결과를 만들어 내는 방법에는 여러 방법이 있지만, 사용하는건 개발자의 선택인 듯 합니다. 방법에는 정답이 없으니까요.

source : https://github.com/drcarter/RetriveActivityResult

샘플 소스는 제 git에 올려둡니다.

반응형

+ Recent posts