본 포스팅은 droidkaigi2016 by funnelbit 을 기본으로 번역하여 작성했습니다
제 일본어 실력으로 인하여 오역이나 오타가 발생할 수 있습니다.
실제 슬라이드의 일본어
부분을 번역했다는 점 양해바랍니다.
Dagger2와 Realm을 이용한 모던 개발
자기소개
Hatena Bookmaker
교토
오늘 주제
프레임워크로부터 로직을 분리
프레임워크
로직을 어디에 작성할 것인가
그 이외의 장소?
로직의 추상 Instance를 어디에 만들 것인가?
소박한 DI
public EntryManager(APIClient apiClient) {
this.mAPIClient = apiClient;
...
}
소박한 DI를 사용할 수 없는 문제
DI Container
어떤 DI Container를 사용할 것인가?
Dagger2
Dagger2
Dagger2 이점
DI 구성
Module
@Module
public class MainModule {
...
@Provides
EntryManager providedEntryManager() {
return new EntryManager(mService);
}
...
Component
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity mainActivity)
...
Inject
publc class MainActivity extends AppCompatActivity {
@Inject EntryManager mEntryManager;
@Override
protected void onCreate(Bundle b) {
...
((App)) getApplication())
.component()
.inject(this);
...
Test용 Module
@Module
public class MainTestModule {
...
@Provides
EntryManager providedEntryManager() {
return new EntryManager(mStubService);
}
...
테스트용 Component
@Component(modules = MainTestModule.class)
public interface MainTestComponent {
void inject(EntryManagerTestCase testCase)
...
Inject(테스트)
public class EntryManagerTestCase extends InstrumentationTestCase {
...
@Inject
EntryManager mEntryManager;
...
public testGetEntries() {
assertEquals(
"Entry 취득할 수 있다",
false,
mEntryManager.getEntries().isEmpty());
}
}
Component의 분리방법
SubComponent
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(App app);
// Activities
void inject(RootActivity rootActivity);
// Fragments
void inject(MainSettingsFragment mainSettingsFragment);
// Subcomponents
UserComponent userComponent(UserModule userModule);
}
SubComponent
@Subcomponent(modules = UserModule.class)
public interface UserComponent {
// Fragments
void inject(MyFragment myFragment);
UserController userController();
}
Component 취득
public class MyFragment extends Fragment {
MainComponent mainComponent =
App.get(getActivity())
.getMainComponent();
UserComponent userComponent =
mainComponent.userComponent(
new UserModule(
mainComponent.getRealm(),
args.getString(ARGS_USER_ID)));
userComponent.inject(this);
}
영속화
영속화하는 목적
영속화하는 수단
SQLite
Realm
Realm을 사용
Realm
Realm
Realm을 사용
new RealmConfiguration.Builder(context)
.name("realm")
.schemaVersion(1L)
.build();
Realm을 사용
RealmResults<Entry> followingEntries =
mRealm.where(Entry.class)
.equalTo("isFollowing", true)
.findAllSortedAsync(
"createdAt",
Sort.DESCENDING);
Activity 생성 시에 꺼내기
public class MainActivity extends AppCompatActivity {
...
public void onCreate(Bundle saveInstanceState) {
mRealmResults = mRealm.where().findAllAsync()
...
}
Adapter에 RealmResult
public class EntriesAdapter {
public EntriesAdapter(RealmResults<Entry> entries) {
mEntries = entries;
mRealmChangeListener = new RealmChangeListener() {
@Override
public void onChange() {
mListItems = getItems();
EpisodesAdapter.this.notifyDataSetChanged();
}
};
...
}
...
Adapter에 RealmResult
public class EntriesAdapter {
...
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
mResults.addChangeListener(mRealmChangeListener);
}
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
mResults.removeChangeListener(mRealmChangeListener);
super.onDetachedFromRecyclerView(recyclerView);
}
...
Activity간 데이터를 전달
Intent intent = new Intent(context, EpisodeActivity.class);
intent.putExtra(EXTRA_ID, id);
Realm을 Dagger2로 Thread마다 Inject
스레드 간을 넘을 수 없는 문제
Dagger2의 Provider를 사용
...
@Inject public EntryPrefetcher(Provider<Realm> realmProvider) extends ThreadPoolExecutor {
mRealmProvider = realmProvider;
...
private static class Task implements Callable<String> {
@Override
public String call() throws Exception {
Realm realm = mRealmProvider.get();
...
}
...
서버 사이드와 병행한 개발
서버 사이드와 병행한 개발
Stub Server
Stub Server
Stub Server
resource login_request => {
type => 'object',
description => '로그인 Request',
properties => {
email_address => {
type => 'string',
description => '메일주소',
example => 'abc@example.com',
},
password => {
type => 'string',
description => '패스워드',
example => 'PasswordOrd!',
},
},
requeired => ['email_address', 'password'],
};
테스트에서도 StubServer를 사용
제품 체크
제품 체크
BetaByCrashlytics + jenkins + fastlane
Beta
jenkins
fastlane
Github 연동
data = {satate: state, target_url: target_url, context: context, description: ""}
headers = {Authorization: "token #{token}"}
Excon.post(url, headers: headers, body: data.to_json)
fastlane
Github 연동
slack(
message: ":rocket: 빌드가 완료했습니다 :rocket:",
channel: "#app",
success: true,
payload: {
'Build Data' => Time.new.to_s,
},
default_payloads: [:git_branch, :git_author]
)
테스트 실행
desc "Runs all the tests"
lane :test do
gradle(task: "cC")
end
정리
comments powered by Disqus
Subscribe to this blog via RSS.