MVP项目Philm代码分析

来源:互联网 发布:恒腾网络股票行情 编辑:程序博客网 时间:2024/06/14 05:49

1、关于总体上的分析,可以看看这个链接

http://www.lightskystreet.com/2015/02/10/philm_mvp/

2、这里对philm进行代码层面上的分析

在androidmanifest.xml中找到主activity为MainActivity,我们就从这里开始分析一些主要的逻辑部分

MainActivity继承于BasePhilmActivity,

先看BasePhilmActivity:

onCreate中有初始化MainController和Display

  1. /** 获取总的controller */ 
  2. mMainController = PhilmApplication.from(this).getMainController(); 
  3. /** 创建Display 对象    */ 
  4. mDisplay = new AndroidDisplay(this, mDrawerLayout);

然后在onResume中为MainController设置了Display(mMainController.attachDisplay(mDisplay);)

以及对MainController进行初始化的操作(mMainController.init();)

当然在onPause中肯定会有相关的解除资源的操作了

总的来看,BasePhilmActivity完成的工作主要是初始化Controller和Display

然后看MainActivity:

根据MVP的思想,activity/fragment是一个view(UI), UI的逻辑由Controller通过持有UI的UC来处理(UC用来响应UI的动作), 这里MainActivity对应的UI为MainController.MainUi

  1. public interface MainUi extends MainControllerUi { 
  2.     void showLoginPrompt(); 
  3.  
  4. public interface MainControllerUi extends BaseUiController.Ui<MainControllerUiCallbacks> { 
  5.  
  6. public interface Ui<UC> { 
  7.         void setCallbacks(UC callbacks); 
  8.         boolean isModal(); 
  9. }

所以这里的MainUi总共有三个方法:

void showLoginPrompt();void setCallbacks(UC callbacks); boolean isModal();

MainUi对应的UC为MainControllerUiCallbacks

在onResume中把当前的UIattach到Controller中

  1. protected void onResume() { 
  2.     super.onResume(); 
  3.     getMainController().attachUi(this); 
  4. }

attach的实现在BaseUiController

  1. public synchronized final void attachUi(U ui) { 
  2.     /** 
  3.      * 如果ui==null,那么就报错:ui cannot be null 
  4.      */ 
  5.     Preconditions.checkArgument(ui != null"ui cannot be null"); 
  6.     /** 
  7.      * 如果mUis.contains(ui),那么就报错:UI is already attached 
  8.      */ 
  9.     Preconditions.checkState(!mUis.contains(ui), "UI is already attached"); 
  10.  
  11.     /** 
  12.      * 新加UI 
  13.      */ 
  14.     mUis.add(ui); 
  15.  
  16.     /** 
  17.      * 给UI添加UC 
  18.      */ 
  19.     ui.setCallbacks(createUiCallbacks(ui)); 
  20.  
  21.     if (isInited()) {/** Controller初始化成功了 */ 
  22.         if (!ui.isModal() && !(ui instanceof SubUi)) { 
  23.             /** 给actionbar设置标题  */ 
  24.             updateDisplayTitle(getUiTitle(ui)); 
  25.         } 
  26.  
  27.         onUiAttached(ui); 
  28.         /** UI展示、刷新 */ 
  29.         populateUi(ui); 
  30.     } 
  31. }

这里UI通过ui.setCallbacks(createUiCallbacks(ui));绑定了UC

对于MainActivity来说,它对应的Controller为MainController,所以我们可以在MainController 中找到createUiCallbacks(ui)的实现:

  1. @Override 
  2. protected MainControllerUiCallbacks createUiCallbacks(final MainControllerUi ui) { 
  3.     return new MainControllerUiCallbacks() { 
  4.         @Override 
  5.         public void onSideMenuItemSelected(SideMenuItem item) { 
  6.             Display display = getDisplay(); 
  7.             if (display != null) { 
  8.                 showUiItem(display, item); 
  9.                 display.closeDrawerLayout(); 
  10.             } 
  11.         } 
  12.  
  13.         @Override 
  14.         public void addAccountRequested() { 
  15.             Display display = getDisplay(); 
  16.             if (display != null) { 
  17.                 display.startAddAccountActivity(); 
  18.                 display.closeDrawerLayout(); 
  19.             } 
  20.         } 
  21.  
  22.         @Override 
  23.         public void showMovieCheckin() { 
  24.             Display display = getDisplay(); 
  25.             WatchingMovie checkin = mState.getWatchingMovie(); 
  26.  
  27.             if (display != null && checkin != null) { 
  28.                 display.closeDrawerLayout(); 
  29.                 display.startMovieDetailActivity(checkin.movie.getImdbId(), null); 
  30.             } 
  31.         } 
  32.  
  33.         @Override 
  34.         public void setShownLoginPrompt() { 
  35.             mPreferences.setShownTraktLoginPrompt(); 
  36.         } 
  37.     }; 
  38. }

可以看到,UC正是MainControllerUiCallbacks

MainActivity的布局是一个DrawerLayout,有MenuFragment(SideMenuFragment) 和显示内容Fragment,首次展示的Fragment是通过BasePhilmActivity 中handleIntent(Intent intent, Display display)来确定的

  1. @Override 
  2. protected void handleIntent(Intent intent, Display display) { 
  3.     if (Intent.ACTION_MAIN.equals(intent.getAction())) { 
  4.         if (!display.hasMainFragment()) { 
  5.             getMainController().setSelectedSideMenuItem(MainController.SideMenuItem.DISCOVER); 
  6.             display.showDiscover(); 
  7.         } 
  8.     } 
  9. }

在Philm项目中,通过查看AndroidDisplay可以知道,整个项目的activity跳转、fragment切换、DrawerLayout的 闭和开、actionbar的控制都是通过这个Display来实现的。

通过查看display.showDiscover()可以发现,Mainactivity中的那个fragment对应的类 为DiscoverTabFragment,这个fragment中也没有什么东西,也就是一个viewpager管理着几个fragment, 这里不再做具体的分析了(主要是上次分析的笔记丢失了......)

PopularMoviesFragment

通过分析,DiscoverTabFragment中的对于流行的电影对应的fragment为PopularMoviesFragment. 我们就分析一下数据的交互问题。 PopularMoviesFragment->MovieGridFragment->BasePhilmMovieListFragment->BaseMovieControllerListFragment, 这是这个fragment对应的继承关系,在BaseMovieControllerListFragment 的onResume中进行了UI的attach, 这个UI 对应的Controller为MovieController,在MovieController中先是执行BaseUiController中的 attachUi,然后紧接着执行onUiAttached,这个方法的实现在MovieController

  1. @Override 
  2. protected void onUiAttached(final MovieUi ui) { 
  3.     final MovieQueryType queryType = ui.getMovieQueryType(); 
  4.  
  5.     if (queryType.requireLogin() && !isLoggedIn()) { 
  6.         return
  7.     } 
  8.  
  9.     String title = null
  10.     String subtitle = null
  11.  
  12.     final int callingId = getId(ui); 
  13.  
  14.     switch (queryType) { 
  15.         case TRENDING: 
  16.             fetchTrendingIfNeeded(callingId); 
  17.             break
  18.         case POPULAR: 
  19.             fetchPopularIfNeeded(callingId); 
  20.             break
  21.  
  22.             ..... 
  23.     } 
  24.  
  25.     final Display display = getDisplay(); 
  26.     if (display != null) { 
  27.         if (!ui.isModal()) { 
  28.             display.showUpNavigation(queryType != null && queryType.showUpNavigation()); 
  29.             display.setColorScheme(getColorSchemeForUi(ui)); 
  30.         } 
  31.         display.setActionBarSubtitle(subtitle); 
  32.     } 
  33. }

可以看到,获取数据的方法为fetchPopularIfNeeded(callingId);

  1. private void fetchPopularIfNeeded(final int callingId) { 
  2.     MoviesState.MoviePaginatedResult popular = mMoviesState.getPopular(); 
  3.     if (popular == null || PhilmCollections.isEmpty(popular.items)) { 
  4.         fetchPopular(callingId, TMDB_FIRST_PAGE); 
  5.     } 
  6. }
  1. private void fetchPopular(final int callingId, final int page) { 
  2.     executeTask(new FetchTmdbPopularRunnable(callingId, page)); 
  3. }

通过FetchTmdbPopularRunnable来获取数据了,FetchTmdbPopularRunnable是类似于asynctask的东东, 有一些基本的生命周期函数了。

  1. public abstract class NetworkCallRunnable<R> { 
  2.  
  3.     public void onPreTraktCall() {} 
  4.  
  5.     public abstract R doBackgroundCall() throws RetrofitError; 
  6.  
  7.     public abstract void onSuccess(R result); 
  8.  
  9.     public abstract void onError(RetrofitError re); 
  10.  
  11.     public void onFinished() {} 
  12.  
  13.  }

这里主要分析doBackgroundCallonSuccess.

  1. @Override 
  2. public MovieResultsPage doBackgroundCall() throws RetrofitError { 
  3.     return getTmdbClient().moviesService().popular( 
  4.             getPage(), 
  5.             getCountryProvider().getTwoLetterLanguageCode()); 
  6.  
  7.  
  8. @Override 
  9. public final void onSuccess(TR result) { 
  10.     if (result != null) { 
  11.         R paginatedResult = getResultFromState(); 
  12.  
  13.         if (paginatedResult == null) { 
  14.             paginatedResult = createPaginatedResult(); 
  15.             paginatedResult.items = new ArrayList<>(); 
  16.         } 
  17.  
  18.         updatePaginatedResult(paginatedResult, result); 
  19.         updateState(paginatedResult); 
  20.     } 
  21.  
  22.  
  23.  @Override 
  24. protected void updatePaginatedResult( 
  25.         MoviesState.MoviePaginatedResult result, 
  26.         MovieResultsPage tmdbResult) { 
  27.     result.items.addAll(getTmdbMovieEntityMapper().mapAll(tmdbResult.results)); 
  28.  
  29.     result.page = tmdbResult.page; 
  30.     if (tmdbResult.total_pages != null) { 
  31.         result.totalPages = tmdbResult.total_pages; 
  32.     } 
  33.  
  34. @Override 
  35. protected MoviesState.MoviePaginatedResult createPaginatedResult() { 
  36.     return new MoviesState.MoviePaginatedResult(); 
  37.  
  38. @Override 
  39. protected MoviesState.MoviePaginatedResult getResultFromState() { 
  40.     return mMoviesState.getPopular(); 
  41.  
  42. @Override 
  43. protected void updateState(MoviesState.MoviePaginatedResult result) { 
  44.     mMoviesState.setPopular(result); 
  45. }

主要的逻辑都在这里了。在doBackgroundCall中通过一个接口获取数据,然后在onSuccess中 对获取到的数据进行处理:通过updateState()保存数据到MoviesState中。

这里牵扯到该项目中的又一个角色,Status.在Philm项目中,Status负责保存HTTP中获取的数据, 然后通过eventbus把获取的数据post给UI,注意,status仅限于保存数据和转发数据,没有获取数据等复杂操作。 在ApplicationState中找到setPopular

  1. @Override 
  2. public void setPopular(MoviePaginatedResult items) { 
  3.     mPopular = items; 
  4.     mEventBus.post(new PopularChangedEvent()); 
  5. }

通过eventbus来告知Controller,在MovieController中找到了接收这个事件的方法:

  1. @Subscribe 
  2. public void onPopularChanged(MoviesState.PopularChangedEvent event) { 
  3.     populateUiFromQueryType(MovieQueryType.POPULAR); 
  4.  
  5. private final void populateUiFromQueryType(MovieQueryType queryType) { 
  6.     MovieUi ui = findUiFromQueryType(queryType); 
  7.     if (ui != null) { 
  8.         populateUi(ui); 
  9.     } 
  10.  
  11. @Override 
  12. protected void populateUi(final MovieUi ui) { 
  13.     if (!isLoggedIn() && ui.getMovieQueryType().requireLogin()) { 
  14.         ui.showError(NetworkError.UNAUTHORIZED_TRAKT); 
  15.         return
  16.     } 
  17.  
  18.     if (mMoviesState.getTmdbConfiguration() == null) { 
  19.         mLogger.i(LOG_TAG, "TMDB Configuration not downloaded yet."); 
  20.         return
  21.     } 
  22.  
  23.     if (Constants.DEBUG) { 
  24.         mLogger.d(LOG_TAG, "populateUi: " + ui.getClass().getSimpleName()); 
  25.     } 
  26.  
  27.     // Set the color scheme 
  28.     ui.setColorScheme(getColorSchemeForUi(ui)); 
  29.  
  30.     if (ui instanceof SearchMovieUi) { 
  31.         populateSearchMovieUi((SearchMovieUi) ui); 
  32.     } else if (ui instanceof MovieListUi) { 
  33.          //PopularMoviesFragment是一个MovieListUi 
  34.         populateMovieListUi((MovieListUi) ui); 
  35.     }  
  36.     ...... 
  37.  
  38. private void populateMovieListUi(MovieListUi ui) { 
  39.     final MovieQueryType queryType = ui.getMovieQueryType(); 
  40.  
  41.     Set<MovieFilter> filters = null
  42.  
  43.     if (isLoggedIn()) { 
  44.         if (queryType.supportFiltering()) { 
  45.             ui.setFiltersVisibility(true); 
  46.             filters = mMoviesState.getFilters(); 
  47.             ui.showActiveFilters(filters); 
  48.         } 
  49.     } else { 
  50.         ui.setFiltersVisibility(false); 
  51.     } 
  52.  
  53.     List<PhilmMovie> items = null
  54.  
  55.     List<MovieFilter> sections = queryType.getSections(); 
  56.     List<MovieFilter> sectionProcessingOrder = queryType.getSectionsProcessingOrder(); 
  57.  
  58.     switch (queryType) { 
  59.         case TRENDING: 
  60.             items = mMoviesState.getTrending(); 
  61.             break
  62.         case POPULAR: 
  63.             MoviesState.MoviePaginatedResult popular = mMoviesState.getPopular(); 
  64.             if (popular != null) { 
  65.                 items = popular.items; 
  66.             } 
  67.             break
  68.         // ...... 
  69.     } 
  70.  
  71.     if (!PhilmCollections.isEmpty(items)) { 
  72.         // Always filter movies (for adult) 
  73.         items = filterMovies(items, filters); 
  74.     } 
  75.  
  76.     if (items == null) { 
  77.         ui.setItems(null); 
  78.     } else if (PhilmCollections.isEmpty(sections)) { 
  79.         ui.setItems(createListItemList(items)); 
  80.  
  81.         if (isLoggedIn()) { 
  82.             ui.allowedBatchOperations(MovieOperation.MARK_SEEN, 
  83.                     MovieOperation.ADD_TO_COLLECTION, MovieOperation.ADD_TO_WATCHLIST); 
  84.         } else { 
  85.             ui.disableBatchOperations(); 
  86.         } 
  87.     } else { 
  88.         ui.setItems(createSectionedListItemList(items, sections, sectionProcessingOrder)); 
  89.     } 
  90. }

最后,在MovieGridFragment中对数据进行了显示

  1. @Override 
  2. public void setItems(List<ListItem<PhilmMovie>> items) { 
  3.     mMovieGridAdapter.setItems(items); 
  4.     moveListViewToSavedPositions(); 
  5. }


0 0
原创粉丝点击