ã¡ãã£ã¢ãããã¯ãéçºé¨ã®å®é¨(@STAR_ZERO)ã§ãã
Android cookpadLiveã§æ¡ç¨ãã¦ãæè¡ã«ã¤ãã¦ç´¹ä»ãããã¨æãã¾ãã
cookpadLiveã¨ã¯
cookpadLive ã¯ãæçä¸æãªæå人ãæç家ãã¯ããã³ã°Liveãçé ä¿¡ãã¦ãã¾ããä¸ç·ã«ãLiveé ä¿¡ã§ãªã¢ã«ã¿ã¤ã ã«æçã楽ãããã¢ããªã§ãã
ãã¦ã³ãã¼ã: Android ã¢ã㪠iOS ã¢ããª
ãã²ããã¦ã³ãã¼ããã¦Liveé ä¿¡ãè¦ã¦ãã ããï¼
åºæ¬ç°å¢
åºæ¬ã¨ãªãç°å¢ã§ã
- Kotlin
- minSdkVersion 21
- targetSdkVersion 28
- AndroidX
ç¹å¥ãªç®æã¯ãªãã§ãããææ°ã«è¿½éããããã«åªãã¦ãã¾ãã
æ¯è¼çæ°ããã¢ããªãªã®ã§ãæåãããã¹ã¦Kotlinã§è¨è¿°ããã¦ãã¾ãã
targetSdkVersionã«ã¤ãã¦ã¯ãããã29ã«å¯¾å¿ããäºå®ã§ãã29ã«ãããã¨ã§ã®å½±é¿ã調æ»ãã¦ããç¶æ³ã§ãã
Android Studio 3.5
Android Studio 3.5ã¯betaã®æ®µéããå°å ¥ãã¦ãã¾ããçç±ã¨ãã¦ã¯Incremental annotation processingã使ãããã£ãããã§ãã
cookpadLiveã§ã¯å ¨é¢çã«DataBindingãæ¡ç¨ãã¦ãããããããã®æ©æµã¯é常ã«å¤§ãããã®ã«ãªãã¾ãã ããã¾ã§ã¯ãã¬ã¤ã¢ã¦ããã¡ã¤ã«ãå¤æ´ãã¦ãã«ããç´ããªããã°ã³ã¼ããçæãããå¹çãããããã¾ããã§ããã3.5ããã¯ã¬ã¤ã¢ã¦ããã¡ã¤ã«ãå¤æ´ããã¨åæã«ã³ã¼ãçæãè¡ãããã®ã§ãã«ãã«ããå¾ ã¡æéãæ¸ãå¹çããéçºã§ããããã«ãªãã¾ããã
Jetpack
ç¾å¨ãcookpadLiveã§ã¯Jetpackãç©æ¥µçã«æ¡ç¨ãã¦ãã¾ãã
DataBindingãLiveDataãViewModelã«ã¤ãã¦ã¯ãæåã¯ä½¿ç¨ããã¦ããªãã£ãã®ã§ãããå¾ã ã«å°å ¥ãé²ãã¦ä»ã§ã¯å ¨é¢çã«ä½¿ç¨ãã¦ãã¾ãã
æå¤ã¨ä¾¿å©ã ã£ãã®ããViewModelãActivityã«é¢é£ä»ãããã¨ã§Activityã¨Fragmentéãããããã®Fragmentéã§ã®ãã¼ã¿ãã¤ãã³ãã®ããã¨ããå¯è½ã«ãªãæ©è½ã§ãããã®æ©è½ã®ãããå®è£ ãã ãã¶æ¥½ã«ãªã£ããã¨ãããã¾ããã ä¾ãã°ã以ä¸ã®ããã«Activityã®ã¤ãã³ããFragmentã§ãåãåããã¨ãç°¡åã«ã§ãã¾ãã
class HogeActivity: AppCompatActivity() { private val viewModel by lazy { ViewModelProviders.of(this).get(HogeViewModel::class.java) } fun someEvent() { // ã¤ãã³ãçºè¡ viewModel.somaEvent.value = someValue // someEventã¯LiveData } } class HogeFragment: Fragment() { private val activityViewModel by lazy { // thisã§ã¯ãªããActivityãæå®ãããã¨ã§å ±éã®ViewModelã使ç¨ã§ãã ViewModelProviders.of(requireActivity()).get(HogeViewModel::class.java) } override fun onActivityCreated(savedInstanceState: Bundle?) { activityViewModel.somaEvent.observe(viewLifecycleOwner) { // Activityã®ã¤ãã³ããåä¿¡ } } }
Pagingã¯ã ãã¶çãããã©ã¤ãã©ãªã§ãããã¡ããã¨ç解ãã¦ä½¿ãé常ã«ä¾¿å©ã§ãããããæ©ã段éããå°å ¥ãã¦ãã¾ããç¾ç¶ã§ã¯ãããã¯ã¼ã¯ã«ã¯ã©ã¤ãã©ãªå´ã§å¯¾å¿ã¯ããã¦ãªãã§ãããä»å¾ãããäºå®ãããã®ã§ã楽ãã¿ã«ãã¦ãã¾ãã
Navigationã«ã¤ãã¦ã¯ããã¾ã§æ´»ç¨ã§ããªãã§ãããé¨åçã«ä½¿ã£ã¦ãã¾ããå ¨é¢çã«Single Activityã«ãããã¨ã¯èãã¦ãªãã§ãããåºæ¥ãç®æã¯Fragmentã«ç§»è¡ãã¦ããããã¨èãã¦ãã¾ããã¾ããSafeArgsã¯ç»é¢éã®å¤ã®åã渡ãã便å©ã«ãªãã®ã§ç©æ¥µçã«ä½¿ã£ã¦ãããã¨æãã¾ãã
Roomã¯DBã¾ããã®å¦çã«ã¯æ¬ ãããªãããã便å©ã§ããRoomã¯LivaDataãRxJavaã¨ç°¡åã«é£æºãããã¨ãã§ãããããæ¢åã®å®è£ ã«çµã¿åããããã¨ãç°¡åã«ã§ãã¾ãããSQLãè£å®ã¨ã·ã³ã¿ãã¯ã¹ãã¤ã©ã¤ããå¹ãã®ã§é常ã«å©ããã¾ãã
DI
DIã«ã¤ã㦠Dagger ã使ç¨ãã¦ãã¾ãããã¡ããæåã¯ä½¿ç¨ããã¦ãªãã£ãã®ã§ãããå¾ã ã«å°å ¥ããããã¾ããã
Daggerã«ã¤ãã¦ã¯é常ã«é£ããå°è±¡ããã人ãå¤ãã¨æãã¾ãããä¸åº¦ä½¿ãã¨ä¾¿å©ããã¦ææ¾ãã¾ããã Daggerã§Repositoryã¯ã©ã¹ãªã©çæããããã«ãã¦ããã¨ã¯ä½¿ããç®æã§Injectããã ãã§ããViewModelãªã©ã§å¿ è¦ãªRepositoryãå¢ããå ´åããçæããã³ã¼ããæèãããã©ã¡ã¼ã¿ã«è¿½å ããã ãæ¸ã¿ã¾ãã æåã®è¨å®ãããã¾ããã£ã¦ãã¾ãã°ããã¨ã¯æ¥½ã«ãªãã¯ãã§ãã
以åãé¨å
ã§ãã£ãDaggeråå¼·ä¼ã®ãã¥ã¼ããªã¢ã«ã³ã¼ãã®ãªã³ã¯ãè²¼ã£ã¦ããã¾ããï¼ã¾ã @Component.Factory
ã«ã¯å¯¾å¿ãã¦ãªãã§ãâ¦ï¼
AppSync
cookpadLiveã§ã¯ã©ã¤ãä¸ã®ã³ã¡ã³ãããã¼ããªã©ã®ãªã¢ã«ã¿ã¤ã éä¿¡ã«AWSã® AppSync ã使ç¨ãã¦ãã¾ãã
ãã®é¨åãä¸çªç¹å¾´çããããã¾ããã
AppSyncã¯AWSã®ããã¼ã¸ãåGraphQLãµã¼ãã¹ã§ãã ã¦ã¼ã¶ã¼ãéä¿¡ããã³ã¡ã³ãããã¼ããAppSync(GraphQL)ã®Subscriptionã®æ©è½ã使ãåä¿¡ããããã«ãã¦ãã¾ãã
ã©ã¤ãã«ãã£ã¦é常ã«å¤ãã®ã³ã¡ã³ãããã¼ããåä¿¡ãããã¨ã«ãªãã¾ãããåä¿¡ã®ãã³ã«ç»é¢ã«æç»ããã®ã§ã¯ãªãRxJavaã®bufferã使ã£ã¦ããç¨åº¦ã¾ã¨ãã¦ç»é¢ã«æç»ããããã«ãã¦ãã¾ãããã®ãããã¯ãã¾ãRxJavaã¨çµã¿åããã¦å®è£ ãã¦ãã¾ãã
AppSyncã®è©±ã¯ä»¥ä¸ã®è¨äºã®å¾åé¨åãè¦ã¦ããã ããã°ã¨æãã¾ãã
CookpadTVã®CTOãèªããæçåç»ãµã¼ãã¹éçºã®èª²é¡ã¨å®è£ - ãã°ãã¼Tech
ãã®ä»ã©ã¤ãã©ãª
ãã®ããããã使ããã¦ãã©ã¤ãã©ãªã§ããããããããã¡ããæ´»ç¨ãã¦ãã¾ãã
è¨è¨
MVVM + Repositoryãã¿ã¼ã³ãæ¡ç¨ãã¦ãã¾ããGoogleãå ¬éãã¦ã Guide to app architecture ã¨ã»ã¼åãã§ãã
å ã ã¯VIPERã ã£ãã®ã§ãããDataBindingãLiveDataã¨ViewModelãå°å ¥ãã¦ããã¨åæã«MVVMã«ç§»è¡ãã¦ããã¾ãããä»ã§ã¯ãã¹ã¦MVVMã§å®è£ ããã¦ãã¾ãã ç§å人ã®çµé¨ãããJetpackãå°å ¥ãããã¨ã§ãéçºå¹çã¨å質ã«å¤§ããè²¢ç®ãããã¨ã¯æ確ã ã£ãã®ã§ãããããå°å ¥ãã¾ããã
VIPERã¯Androidã§ã¯ãã¾ãã®é¦´æã¿ããªãããããã¾ããããMVPãã¿ã¼ã³ã®ããã«Interfaceã使ã£ã¦åã¬ã¤ã¤ã¼éã®å¦çãå¼ã³åºãã¾ãã ãããç°¡åãªä¾ã§ããã以ä¸ã®ãããªæãã§ããï¼ä¾ã§ã¯Viewã¨Presenterããç»å ´ãã¦ãªãã§ãï¼
// HogeView.kt interface HogeView { fun show() fun hide() } // HogeFragment.kt class HogeFragment: Fragment(), HogeView { private val presenter by lazy { HogePresenter(this) } override fun onCreateView(/** */): View? { // ... } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) presenter.fetch() } override fun show() { // View.VISIBLEã«ãã } override fun hide() { // View.GONEã«ãã } } // HogePresenter.kt class HogePresenter(private val view: HogeView) { fun fetch() { val data == // ... if (data != null) { view.show() } else { view.hide() } } }
ãã®ããã«Viewã¸ã®å¦çãå¼ã³åºãã®ã«Interfaceã使ã£ã¦PresenterããViewã¸ã®å¦çãå¼ã³åºãã¦ãã¾ãã
ä¸è¦ãåé¡ããªãããã§ããããã®æç¹ã§æ¢ã«åé¡ãããã¾ããä¾ãã°ãPresenterã§ãã¼ã¿åå¾ä¸ã«Actiivty/Fragmentãç ´æ£ãããå ´åã¯ã©ããªãã§ããããç ´æ£ããã¦ããªãã¸ã§ã¯ãã«ã¢ã¯ã»ã¹ãããã¨ã«ãªããå ´åã«ãã£ã¦ã¯ã¯ã©ãã·ã¥ãã¾ããããã¯PresenterãActivity/Fragmentã®Lifecycleã«ã¤ãã¦ä½ãç¥ããªãããã§ããããã解決ããã«ã¯Presenterå´ã«ç ´æ£ããããã¨ãæãã¦ãããå¿ è¦ãããã¾ãã
ã§ã¯ããããä»ã®å®è£ ã§æ¸ãæããå ´åã§ãã
// HogeFragment.kt class HogeFragment: Fragment() { private val viewModel by lazy { ViewModelProviders.of(this).get(HogeViewModel::class.java) } private lateinit var binding: FragmentHogeBinding override fun onCreateView(/** */): View? { // ... } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) binding.viewModel = viewModel binding.lifecycleOwner = viewLifecycleOwner viewModel.fetch() } } // HogeViewModel.kt class HogeViewModel: ViewModel() { val isShow = MutableLiveData<Boolean>() fun fetch() { val data == // ... isShow.value = data != null } }
<!-- fragment_hoge.xml --> <layout> <data> <variable name="viewModel" type="...HogeViewModel" /> <import type="android.view.View" /> </data> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="..." android:visibility="@{viewModel.isShow ? View.VISIBLE : View.GONE}" /> </layout>
ããã§ã¯DataBindingã使ç¨ãã¦ãã¦ãViewModelãä¿æãã¦ããLiveDataã¨ãã¤ã³ããã¦ãã¾ãããã®LiveDataã®å¤ãå¤æ´ããã¨èªåçã«Viewå´ã«ãåæ ãããä»çµã¿ã«ãªã£ã¦ãã¾ãã ããã§éè¦ãªã®ã¯ãLiveDataã¯Lifecycleã®ãã¨ãç¥ã£ã¦ããã®ã§ãActivity/Fragmentãã¢ã¯ãã£ããªç¶æ ã®ã¨ããããã¼ã¿ãæµãã¾ããããã®ãããããã»ã©èª¬æããActivity/Fragmentãç ´æ£ãããã¨ãã®å¯¾å¿ãç¹å¥ã«ããã¨ãåé¡ãèµ·ãããã¨ãããã¾ãããã¾ãViewModelããã¦ã¯å転ã§Activityãåçæãããã¨ããViewModelã¯ç¶æ ããã£ã¦ããããããã¼ã¿åå¾ãä¸æãããã¨ãªãå¦çãç¶ç¶ãããã¨ãã§ãã¾ãã
ãã®è¨è¨ã«ãããã¨ã§Activity/Fragmentã¯ViewModelã®ç¶æ ãåæ ããã°è¯ãã ãã«ãªãã責åããã£ããåããã¦è¦éããè¯ããªãã¾ãããã¾ãåç´ã«VIPERã¯ãã¡ã¤ã«æ°ãå¤ããªããããã³ã¼ãã追ãæã«ã³ã¼ãã¸ã£ã³ãã§ãã¡ãã¡é£ã°ãªããã°ãªãããå人çã«ã¯ã³ã¼ãã追ãã«ããæãã§ããã
ä»ã«ãæ§ã ãªé¢ã§å¹çã»å質ãåä¸ãããã®ã«è²¢ç®ãã¦ããã¦ãã¾ãããã®ä»ä¾¿å©ãªJetpackã©ã¤ãã©ãªãç°¡åã«å°å ¥ã§ããããã«ãªã£ã¦ãã¾ãã
ä»ã§ã¯ãã¹ã¦ã®ç»é¢ãåããããªæãã«ãªã£ã¦ãã®ã§ãã©ãããå¦çããã¦ãã®ããç解ãããããªã£ã¦ãã¾ãã
ãã®è¨è¨å¤æ´ã§ãããæ°è¦ç»é¢ã«ã¤ãã¦ã¯æ°ããè¨è¨ã§ãããæ¢åã«ã¤ãã¦ã¯å°ããã¤é²ãã¦ãã¾ãããã¾ãUIã大ããå¤æ´ããã¿ã¤ãã³ã°ããã£ãã®ã§ããã®æã«ä¸æ°ã«ç´ããç®æãããã¾ãããåºæ¥ããã¨ããã³ãã³ããã£ã¦ãã¨ãããã大ããã³ã¹ããããããã¨ãªãå¤æ´ã§ãã¾ããã
CIç°å¢
CIã«é¢ãã¦ã¯ãJenkinsã使ã£ã¦ãã¾ãããã£ã¦ããã¨ã¯ä»¥ä¸ã«ãªãã¾ãã
- Pull Request
- ãã¹ããlintãå®è¡
- 社å ãã¹ãç°å¢ã«APKãã¢ãããã¼ã
- masterãã¼ã¸å¾
- 社å ãã¹ãç°å¢ã«APKãã¢ãããã¼ã
- Google Playå é¨ãã¹ãã¸ã¢ãããã¼ã
å¯è½ãªéãæ©ã段éã§ãªãªã¼ã¹çããã«ããã¦è§¦ããã¨ã§ãåé¡ããã£ãã¨ãã«æ©ãã«æ°ã¥ããã¨ãã§ããããã«ãã¦ãã¾ããç¹ã«Proguardã¾ããã¯è¦è½ã¨ããã¡ã«ãªããé²ãã¾ãã
ãªãªã¼ã¹ããã¨ãã¯å é¨ãã¹ãçã製åçã¸ææ ¼ããã ãã«ãªã£ã¦ãã¾ããä»ããã¯æä½æ¥ã§ãã£ã¦ãã®ã§ãããChatOpsçã§åºæ¥ãããã«ãããã¨èãã¦ãã¾ãã
ãã«ãã¢ã¸ã¥ã¼ã«
ç¾ç¶ã§ã¯ãã¹ãããAndroidTVãFireTVã§å ±æãããããªã¢ã¸ã¥ã¼ã«ã¨ãfeatureã¢ã¸ã¥ã¼ã«ãããã¤ãåå²ãã¦ãã¾ãã
å³ã«ããã¨ä»¥ä¸ã®ãããªæãã§ãã
- core
- å ¨ã¢ã¸ã¥ã¼ã«ã§å ±éå¦ç
- APIã¢ã¯ã»ã¹ãRepositoryããã¼ã¿ã¢ãã«ãªã©
- appcore
- ã¹ããã¢ããªå ±éå¦ç
- å ±éViewããã°ããªã½ã¼ã¹ãªã©
- feature
- åæ©è½ãåå²ããã¢ã¸ã¥ã¼ã«
- app
- ã¹ããã¢ããªã¡ã¤ã³
- smarttv
- TVã¢ããªå ±éå¦ç
- å ±éViewããã°ããªã½ã¼ã¹ãªã©
- androidtv
- AndroidTVã¡ã¤ã³
- firetv
- FireTVã¡ã¤ã³
ã¾ã fetureã¢ã¸ã¥ã¼ã«ã¯åå²ã§ããç®æãããã®ã§ãå°ããã¤ã§ãé²ãã¦ããããã¨æãã¾ãã
課é¡ã¨ä»å¾
ãã¹ã
æ£ç´ãã¾ã ããã¾ã§ãã¾ãæ¸ãã¦ãç¶æ³ã§ã¯ãªãã®ã§ããªãã¨ããã¦ããããã¨æã£ã¦ãã¾ãã ãã£ãããªã®ã§ãã©ã¤ãã©ãªã®é¸å®ããèãããã¨ãæã£ã¦ãã¾ããTruth è¯ãããã§ããã
Styleã¨Theme
Styleã¨Themeã«ã¤ãã¦ã¯çµæ§ã¡ããã£ã¦ãç¶æ ãªã®ã§ãæ´çãããæã£ã¦ãã¾ããç»é¢æ°ãããããããã®ã§ãã ãã¶å¤§å¤ãªä½æ¥ã«ãªãæ°é ããã¦ãã¾ããã¾ãã¯ãã©ããããµãã«æ´çããããæ¤è¨ãã¦ããå°ããã¤ãã£ã¦ããæãã«ãªãããã§ãã
Navigation
åã«æ¸ãã¾ããããã¾ã ã¾ã æ´»ç¨ã§ããç®æãããã¾ãããã¹ã¦SingleActivityã¨ã¯èãã¦ãªãã§ãããFragmentã§ã®é·ç§»ã§è¯ãç®æãããã®ã§ããããã£ãç®æã«å¯¾å¿ãã¦ããããã¨èãã¦ãã¾ãã
Coroutines
Coroutinesã«ã¤ãã¦ã¯ãã©ãããããæ¤è¨ãã¦ãã段éã§ããç¾ç¶ã§Coroutinesãããªãã¨å°ããããªå ´é¢ã¯åºã¦ãã¦ãã¾ããããJetpackãCoroutinesã®å¯¾å¿ãé²ãã§ãã¦å®è£ ããã®ã«å°ããã¨ã¯ãªãã¨èãã¦ãã¾ããã¾ããä»å¾Coroutinesã«ãã£ã¦å®è£ ã³ã¹ããä¸ãããããªæ©è½ãªãããåºã¦ããå¯è½æ§ããããã§ãã ã¡ã³ãã¼ã¨ä¼è©±ãã¦ãå°å ¥ããæ°æã¡ã¯ããã¾ãããé²ãæ¹ãã©ãããå°å ¥ããã®ããèãã¦ãã¾ãã
ã¾ã¨ã
cookpadLiveã§ã¯ç©æ¥µçã«Jetpackã使ã£ã¦ãããGoogleãæ¨å¥¨ãã¦ããããæ¹ã«ã©ãã©ãä¹ã£ãã£ã¦ãã£ã¦ãã¾ãã ä»å¾ãJetpackãæ¹åããã¦ããã¨æãã®ã§ãããã«ãã¤ã§ã追éã§ãããããªç¶æ ãä¿ã¤ããã«ãã¦ãã¾ãã
ãããããcookpadLiveã§ã¯æ°ããæè¡ãç©æ¥µçã«åãå ¥ãã¦ããã¾ããããããããã¨ãã¾ã ã¾ã ããããããã¾ãã
èå³ãããæ¹ããã£ãããã¾ããããæ°è»½ã«ã声ãããã ãããä¸ç·ã«è²ã ãã£ã¬ã³ã¸ãã¦ããã¾ãããã