本篇涉及内容:

  • ORM框架(无需再用contentprovider或者sqlitedatebasehelper之类的古董工具了)
  • 规划各种业务Bean文件(配合ORM框架)
  • 设计一个好的请求基类(BaseRequest、BaseResponse)
  • BaseActivity和BaseFragment(把公用的代码写在里面,比如检测网络、弹出alertdialog等等)
  • 定制一个Application类代替默认的(很多第三方框架需要把一些代码写到定制的Application类里面)
  1. Http请求框架(volley,推荐使用okHttp,RxJava+Rxandroid+retrofit等)
  2. JSON解析和构建框架(GsonfastJson,不要用jackson因为比较大,除非需要用嵌套的需求)
  3. JWT
  4. 消息推送(比如友盟)
  5. 用户反馈(比如友盟)
  6. 数据统计(比如友盟)
  7. 更新(比如友盟)
  8. 数据备份和恢复
  9. 点赞、评论、收藏模块
  10. About界面(版权申明+常用软件设置+版本更新+国际化等)
  11. 在线crash log上报(比如腾讯的bugly
  12. 快速开发框架(这里推荐使用butterknifeeventbus
  13. 内存泄漏检测工具(leakcanary
  14. 图片加载库(Glide
  15. 加密解密(RSAMD5,DES
  16. listview/recyclerview的基础adapter
  17. 定制搜索框
  18. 工具类(比如sharepreferenceFileScreenDesitySql,字符串处理,dpsp互转等等)
  19. 各种新式的Material design兼容控件
  20. 界面滑动冲突的问题(横竖冲突、同向冲突)
  21. 离线登录功能
  22. bitmap缓存策略
  23. 最后项目要发布了,那么久需要混淆和打包了,前者关于混淆网上有很多相关的文章,这里需要注意的是很多你所使用的第三方库都需要在混淆的时候给剔除,因为他们是基于反射机制的。这点需要你在使用每个第三方库的时候多看他们的说明书多加小心。其次,混淆后一定要打个包重新回归测试一下,以免出现因混淆而导致的问题。

剩余规划内容

如何设计开发一款Android App【各个模块需要的技术】

局部设计


  1.  

设置选项

Setting功能设置策略 

PreferenceActivity & PreferenceFragment

Ref: Android 首选项框架及PreferenceScreen, PreferenceActivity, PreferenceFragment的用法与分析

在Android1.0的时候,官方就有了PreferenceActivity,这个类继承自ListActivity,是一个抽象类,其持有PreferenceManager对象的引用。

在Android3.0时,由于Fragment的出现,官方同步支持了PreferenceFragment。

同时,PreferenceActivity中的一些API也被相应标记为过时,官方给出的解释是:

因为PreferenceActivity这个类仅仅只被允许展示一个单独的偏好设置,而这些现在都能在PreferenceFragment中找到了,

其实从中可以看出google官方的思想是用碎片化来达到解藕

【貌似没什么可写的,就是个简单的类似json的东西】

主题风格选择

ref: https://github.com/dersoncheng/MultipleTheme【有代码,有讲解】

1. attr.xml中定义:哪些属性是可以随着主题而改变的。

2. style.xml中为上述这些属性 根据主题的不同而设置不同的值。

3. layout中的控件相应属性设置为变量:android:textColor = "?attr/main_textcolor"

4. 在基类的onCreate方法里添加切换主题模式的逻辑代码(相当于默认主题设置)。

  1. public class BaseActivity extends Activity{
  2.  
  3. @Override
  4. public void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. if(SharedPreferencesMgr.getInt("theme", 0) == 1) {
  7. setTheme(R.style.theme_2);
  8. } else {
  9. setTheme(R.style.theme_1);
  10. }
  11. }
  12. }

5. 设置按键触发事件切换主题。

  1. btn.setOnClickListener(new View.OnClickListener() {
  2.   @Override
  3.   public void onClick(View v) {
  4.     if(SharedPreferencesMgr.getInt("theme", 0) == 1) {
  5.       SharedPreferencesMgr.setInt("theme", 0);
  6.       setTheme(R.style.theme_1);
  7.     } else {
  8.       SharedPreferencesMgr.setInt("theme", 1);
  9.       setTheme(R.style.theme_2);
  10.   }
    });

6. 针对切换主题模式时需要对页面ui立即更新,需要使用框架里的封装控件。

这里是动态更新主题,还需要进一步理解原理。

或者,不妨使用一些框架,例如:Android 主题动态切换框架:Prism      ----> viewpager的大翻身经验效果demo

多语言选择

传统方案:Android多语言项目的实现

代码:SwitchLanguageDemo

进入到res目录下,创建对应语言的values目录,命名规则为values-语言的缩写-语言的简称,例如“values-zh-rCN”,表达的意思为:“中文-简体中文”。

  1. import java.util.Locale;
  1. public class BaseActivity extends Activity {
  2.  
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. // 1.初始化PreferenceUtil
  7. PreferenceUtil.init(this);
  8. // 2.根据上次的语言设置,重新设置语言
  9. switchLanguage(PreferenceUtil.getString("language", "zh"));  // -->
  10. }
  11.  
  12. protected void switchLanguage(String language) {
  13. //设置应用语言类型
  14. Resources resources = getResources();
  15. Configuration config = resources.getConfiguration();
  16. DisplayMetrics dm = resources.getDisplayMetrics();
  17. if (language.equals("en")) {
  18. config.locale = Locale.ENGLISH;
  19. } else {
  20. config.locale = Locale.SIMPLIFIED_CHINESE;
  21. }
  22. resources.updateConfiguration(config, dm);
  23.  
  24. //保存设置语言的类型
  25. PreferenceUtil.commitString("language", language);
  26. }
  27. }

ORM框架

对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。

为什么要用ORM?

因为JDBC不好用!

一般情况下,当预料到数据库会复杂到某个程度,就有必要引入数据库的ORM框架,这样可以大大降低开发和维护的成本。

JDBC的API编程访问数据库,代码量较大,特别是访问字段较多的表的时候,代码显得繁琐、累赘,容易出错,例如:

  1. public void addAccount(final Account account) throws DAOException {
  2.   final Connection conn=getConnection();
  3.   PreparedStatement pstmt=con.prepareStatment("insert into account value(?,?,?,?,?,?,?,?,?)");
  4.  
  5.   pstmt.setString(1,account.getUserName());
  6.   pstmt.setInt(2,account.getPassWord());
  7.   pstmt.setString(3,account.getSex());
  8.   pstmt.setString(4,account.getQq());
  9. ......
  10.   pstmt.execute();
  11.   conn.Close();
  12. }
JDBC的繁琐步骤

ORM实现的效果如下:

  1. public Double calcAmount(String customerid, double amount)
  2. {
  3. // 根据客户ID获得客户记录
  4. Customer customer = CustomerManager.getCustomer(custmerid);
  5. // 根据客户等级获得打折规则
  6. Promotion promotion = PromotionManager.getPromotion(customer.getLevel());
  7. // 累积客户总消费额,并保存累计结果
  8. customer.setSumAmount(customer.getSumAmount().add(amount);
  9. CustomerManager.save(customer);
  10. // 返回打折后的金额
  11. return amount.multiply(protomtion.getRatio());
  12. }

选一个ORM试试!

ref: 最好的5个Android ORM框架

ref: Android ORM框架选择的一些总结

OrmLite 不是 Android 平台专用的ORM框架,它是Java ORM。支持JDBC连接,Spring以及Android平台。语法中广泛使用了注解(Annotation)。

SugarORM 是 Android 平台专用ORM。提供简单易学的APIs。可以很容易的处理1对1和1对多的关系型数据,并通过3个函数save(), delete() 和 find() (或者 findById()) 来简化CRUD基本操作。

GreenDao是一个很快的解决方案,它能够支持数千条记录的CRUD每秒,和OrmLite相比,GreenDAO要快几乎4.5倍。

Active Record(活动目录)是Yii、Rails等框架中对ORM实现的典型命名方式。Active Android 帮助你以面向对象的方式来操作SQLite。

Realm 是一个将可以使用的Android ORM,基于C++编写,直接运行在你的设备硬件上(不需要被解释),因此运行很快。它同时是开源跨平台的,iOS的代码可以在GitHub找到,你还可以找到Objective-C以及Swift编写的Realm使用实例。

ObjectBox 速度方面碾压SQLite.

BaseActivity

需求:

1, 标题栏:项目中基本每个acctivity都会有相同的标题栏,返回事件相同,标题文字都居中,标题栏侧边按钮有些界面有,有些界面没有
2, 加载界面统一
3, 联网获取失败界面显示
4, 再按一次返回键退出
5, activity管理

Goto: https://www.jianshu.com/p/b2458fc96a65【页尾有代码】

Goto: BaseActivity 里到底应该写哪些内容?【更为详细】

自定制Application类

Ref: Android中Application类用法

Application,与Activity、Service一样是Android框架的一个系统组件,当Android程序启动时系统会创建一个Application对象,用来存储系统的一些信息。

Android系统自动会为每个程序运行时创建一个Application类的对象且只创建一个,所以Application可以说是单例(singleton)模式的一个类。

启动Application时,系统会创建一个PID,即进程ID,所有的Activity都会在此进程上运行。

那么我们在Application创建的时候初始化全局变量,同一个应用的所有Activity都可以取到这些全局变量的值,换句话说,我们在某一个Activity中改变了这些全局变量的值,那么在同一个应用的其他Activity中值就会改变。

Application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。

因为它是全局的单例的,所以在不同的Activity、Service中获得的对象都是同一个对象。

1. 想要在系统启动时,首先初始化一些东西,可自定制Application类。

2. 有时候我们获取Context并不太容易,但是context又是必须的,定义自己的Application,让你任何时候都可以获取到想要的全局Context。

3. 可以通过Application来进行一些,如:数据传递、数据共享和数据缓存等操作。这种全局变量方法相对静态类更有保障,直到应用的所有Activity全部被destory掉之后才会被释放掉。

定义

  1. import android.app.Application;
  2. import android.content.Context;
  3. public class MyApplication extends Application {
  4. private static Context context;
  5. @Override
  6. public void onCreate() {
  7. super.onCreate();
  8. context = getApplicationContext();
  9. }
  10. /**
  11. * 获取全局的Context
  12. * @return
  13. */
  14. public static Context getContext(){
  15. return context;
  16. }
  17. }

配置:告知系统加载我们自定义的Application类在AndroidManifest.xml

  1. <application
  2. android:name="com.cml.example.MyApplication"
  3. ...>
  4. </application>

接下来在项目的任何地方你只需要调用MyApplication.getContext()就可以得到你想要的context了。

实战:

第一个activity设置一个值,让另外一个activity也能get到。

  1. public class FirstActivity extends Activity
  2. {
  3. private CustomApplication app;
  4.  
  5. @Override
  6. public void onCreate(Bundle savedInstanceState)
  7. {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.main);
  10.  
  11. app = (CustomApplication) getApplication(); // 获得CustomApplication对象
  12.  
  13. Log.i("FirstActivity", "初始值=====" + app.getValue()); // 获取进程中的全局变量值,看是否是初始化值

  14. app.setValue("Harvey Ren"); // 重新设置值
  15.  
  16. Log.i("FirstActivity", "修改后=====" + app.getValue()); // 再次获取进程中的全局变量值,看是否被修改
  17.  
  18. Intent intent = new Intent();
  19. intent.setClass(this, SecondActivity.class);
  20. startActivity(intent);
  21. }
  22. }

第二个activity获得该变化。

  1. public class SecondActivity extends Activity
  2. {
  3. private CustomApplication app;
  4.  
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState)
  7. {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.main);
  10.  
  11. app = (CustomApplication) getApplication();        // 获取应用程序
  12.  
  13. Log.i("SecondActivity", "当前值=====" + app.getValue()); // 获取全局值
  14. }
  15. }

AndroidManifest.xml中做一点改变。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. package="com.android.test"
  5. android:versionCode="1"
  6. android:versionName="1.0">
  7. <uses-sdk
  8. android:minSdkVersion="8" />
  9.  
  10. <application
  11. android:icon="@drawable/icon"
  12. android:label="@string/app_name"
  13. android:name="CustomApplication">
  14. <!-- 将我们以前一直用的默认Application设置成自定义的CustomApplication -->
  15. <activity
  16. android:name=".FirstActivity"
  17. android:label="@string/app_name">
  18. <intent-filter>
  19. <action
  20. android:name="android.intent.action.MAIN" />
  21. <category
  22. android:name="android.intent.category.LAUNCHER" />
  23. </intent-filter>
  24. </activity>
  25.  
  26. <activity
  27. android:name=".SecondActivity"
  28. android:label="@string/app_name">
  29. </activity>
  30. </application>
  31. </manifest>

Ref: getApplication()和getApplicationContext()区别

相同

两个方法获取的是同一个对象。

  1. public class MainActivity extends Activity {
  2.  
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. Application application = getApplication();
  8. Log.i("WY", "打印getApplication:" + application);
  9. Context pContext = getApplicationContext();
  10. Log.i("WY", "打印getApplicationContext:" + pContext);
  11. }
  12. }

区别

getApplication()是用来获取Application实例的,但是该方法只在Activity和Service中才能调用;

在一些其他的地方,比如说当我们在BroadcastReceiver中也想获取Application实例,这时就需要使用getApplicationContext()方法

整体设计


资源:Android Architecture Blueprints【Google官方推荐代码框架,适合coding from scratch】

From: Android项目开发如何设计整体架构?【思维的逐渐深入,值得进一步研究】

框架代码:https://github.com/ShonLin/QuickDevFramework【个人的一个框架总结】

FLUX是Facebook提出的一种架构,有点小复杂,此处作为开篇,停留在简单的了解层面。

本篇重点先放在传统的架构的理解和使用。

Flux

推荐用最新的Android Flux来架构你的Android程序,Facebook提出的架构,文档比较全,数据流总是单向的。用过MVC, MVP后,我还是是比较认同Flux的,而且之前公司用的架构模式跟Flux也比较像。

参考文章:

  • 数据流总是单向的

      一个单向的数据流 是 Flux 架构的核心,也是它简单易学的原因。就如下面讨论的,在进行应用测试的时候,它提供了非常大的帮助。

  • 应用被分成三个主要部分:

    • View: 应用的界面。这里创建响应用户操作的action。

    • Dispatcher: 中心枢纽,传递所有的action,负责把它们运达每个Store。

    • Store: 维护一个特定application domain的状态。它们根据当前状态响应action,执行业务逻辑,同时在完成的时候发出一个change事件。这个事件用于view更新其界面。

小对比,框架本身的优势:
  • MVP的思路是“挪”,在MVC的基础上把业务逻辑从View中挪走;
  • Flux的思路是“单向流”,用严格的单向数据流去实现比较容易跟踪检测的逻辑结构;
  • RxAndroid的思路则是“链式逻辑”,用函数反应式编程的思想把逻辑、代码和线程统统简化为一条链式调用。
 
 
 

让我们从最简单的架构开始。

了解其演化历史,才能了解其本质。

MVC

Activity的设计难免让activity变得臃肿,如下:
 
 
 

MVP

把业务逻辑从Activity中挪出来,挪到Presenter中去,让Activity回归View的角色,从此Presenter专注于业务,View专注于显示。
业务逻辑不再受Activity生命周期的影响,Activity也摆脱了业务逻辑无法复用的囧境。

注意这里的IPresenter还有IView。

Presenter层中的业务逻辑,也比较容易做单元测试,做代码复用,做进一步的抽象和分离。

抛出一个高级例子:

Ref: https://github.com/BaronZ88/MinimalistWeather

MVP+RxJava在实际项目中的应用,MVP中RxJava生命周期的管理

但我们还是从入门例子开始:

Ref: MVP模式在Android项目中的使用【阅读笔记】

View与Model并不直接交互,而是使用Presenter作为View与Model之间的桥梁。
其中Presenter中同时持有View层以及Model层的Interface的引用,而View层持有Presenter层Interface的引用。

这就是MVP模式的整个核心过程:

  1. 当View层某个界面需要展示某些数据的时候,
  2. 首先会调用Presenter层的某个接口,
  3. 然后Presenter层会调用Model层请求数据,
  4. 当Model层数据加载成功之后会调用Presenter层的回调方法通知Presenter层数据加载完毕,
  5. 最后Presenter层再调用View层的接口将加载后的数据展示给用户。

好处:

减少了Model与View层之间的耦合度。

  • 一方面可以使得View层和Model层单独开发与测试,互不依赖。
  • 另一方面Model层可以封装复用,可以极大的减少代码量。

---------------------------------------------------- view ----------------------------------------------------

需求定义

新闻列表模块主要是展示从网络获取的新闻列表信息,View层的接口大概需要如下方法:

(1)加载数据的过程中需要提示“正在加载”的反馈信息给用户。

(2)加载成功后,将加载得到的数据填充到RecyclerView展示给用户。

(3)加载成功后,需要将“正在加载”反馈信息取消掉。

(4)若加载数据失败,如无网络连接,则需要给用户提示信息。

View层的接口定义

先是定义套路中的四个接口。

  1. public interface NewsView {
  2. void showProgress();
  3. void addNews(List<NewsBean> newsList);
  4. void hideProgress();
  5. void showLoadFailMsg();
  6. }

actvity / fragment中的接口实现

  1. public class NewsListFragment extends Fragment implements NewsView, SwipeRefreshLayout.OnRefreshListener {

  2. public static NewsListFragment newInstance(int type) {...}
  3. @Override
  4. public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  1. mNewsPresenter = new NewsPresenterImpl(this);  // ---->
    mType = getArguments().getInt("type");
    }
  1. @Nullable
  2. @Override
  3. public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {...}
  4. private RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() {...};
  5. private NewsAdapter.OnItemClickListener mOnItemClickListener = new NewsAdapter.OnItemClickListener() {...}

  6. @Override
  7. public void showProgress() {
  8. mSwipeRefreshWidget.setRefreshing(true);
  9. }
  10. @Override
  11. public void addNews(List<NewsBean> newsList) {  // 加载成功后,将数据展示给用户
  12. mAdapter.isShowFooter(true);
  13. if(mData == null) {
  14. mData = new ArrayList<NewsBean>();
  15. }
  16. mData.addAll(newsList);
  17. if(pageIndex == 0) {
  18. mAdapter.setmDate(mData);  // <--------
  19. } else {
  20. //如果没有更多数据了,则隐藏footer布局
  21. if(newsList == null || newsList.size() == 0) {
  22. mAdapter.isShowFooter(false);
  23. }
  24. mAdapter.notifyDataSetChanged();
  25. }
  26. pageIndex += Urls.PAZE_SIZE;
  27. }
  28. @Override
  29. public void hideProgress() {
  30. mSwipeRefreshWidget.setRefreshing(false);
  31. }
  32. @Override
  33. public void showLoadFailMsg() {
  34. if(pageIndex == 0) {
  35. mAdapter.isShowFooter(false);
  36. mAdapter.notifyDataSetChanged();
  37. }
  38. Snackbar.make(getActivity().findViewById(R.id.drawer_layout), getString(R.string.load_fail), Snackbar.LENGTH_SHORT).show();
  39. }
  40.  
  41. ---------------------------------------------------------------------
  1. @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
  2.    super.onScrollStateChanged(recyclerView, newState);
  3.    if (newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 == mAdapter.getItemCount() && mAdapter.isShowFooter())
    {
  4.      LogUtils.d(TAG, "loading more data");
  5.      mNewsPresenter.loadNews(mType, pageIndex + Urls.PAZE_SIZE);  // 加载更多
  6.    }
  7. }
  8. @Override
  9. public void onRefresh() {
  10.    pageIndex = 0;
  11.    if(mData != null) {
  12.      mData.clear();
  13.    }
  14.    mNewsPresenter.loadNews(mType, pageIndex);
  15. }
  1. }

加入一级大目录:

  1. public class NewsFragment extends Fragment {
  2.  
  3. @Nullable
  4. @Override
  5. public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {...}
  6.  
  7. private void setupViewPager(ViewPager mViewPager) {
  8. //Fragment中嵌套使用Fragment一定要使用getChildFragmentManager(),否则会有问题
  9. MyPagerAdapter adapter = new MyPagerAdapter(getChildFragmentManager());
  10. adapter.addFragment(NewsListFragment.newInstance(NEWS_TYPE_TOP), getString(R.string.top));
  11. adapter.addFragment(NewsListFragment.newInstance(NEWS_TYPE_NBA), getString(R.string.nba));
  12. adapter.addFragment(NewsListFragment.newInstance(NEWS_TYPE_CARS), getString(R.string.cars));
  13. adapter.addFragment(NewsListFragment.newInstance(NEWS_TYPE_JOKES), getString(R.string.jokes));
  14. mViewPager.setAdapter(adapter);
  15. }
  16.  
  17. public static class MyPagerAdapter extends FragmentPagerAdapter {...}
  18. }

---------------------------------------------------- model ----------------------------------------------------

 
主要负责从服务器获取新闻列表信息。
 
接口定义
  1. public interface NewsModel {
  2. void loadNews(String url, int type, OnLoadNewsListListener listener);  // 内部使用的是回调的方法
  3. voidloadNewsDetail(String docid, OnLoadNewsDetailListener listener);
  4. }

利用OkHttp实现了网络通信。

将网络请求进行封装class OkHttpUtils,可以减少很多的代码量,并且后期如果我不想用okhttp了,想换成其它的库,修改起来也方便。

【注意,这里使用了回调的方法】

  1. public class NewsModelImpl implements NewsModel {
  2.  
  3. @Override
  4. public void loadNews(String url, final int type, final OnLoadNewsListListener listener) {
  5. OkHttpUtils.ResultCallback<String> loadNewsCallback = new OkHttpUtils.ResultCallback<String>() {
  6. @Override
  7. public void onSuccess(String response) {  <---- 回调
  8. List<NewsBean> newsBeanList = NewsJsonUtils.readJsonNewsBeans(response, getID(type));
  9. listener.onSuccess(newsBeanList);
  10. }
  11.  
  12. @Override
  13. public void onFailure(Exception e) {  <---- 回调
  14. listener.onFailure("load news list failure.", e);
  15. }
  16. };
  17. OkHttpUtils.get(url, loadNewsCallback);
  18. }
  19.  
  20. @Override
  21. public void loadNewsDetail(final String docid, final OnLoadNewsDetailListener listener) {
  22. String url = getDetailUrl(docid);
  23. OkHttpUtils.ResultCallback<String> loadNewsCallback = new OkHttpUtils.ResultCallback<String>() {
  24. @Override
  25. public void onSuccess(String response) {  <---- 回调
  26. NewsDetailBean newsDetailBean = NewsJsonUtils.readJsonNewsDetailBeans(response, docid);
  27. listener.onSuccess(newsDetailBean);
  28. }
  29.  
  30. @Override
  31. public void onFailure(Exception e) {  <---- 回调
  32. listener.onFailure("load news detail info failure.", e);
  33. }
  34. };
  35. OkHttpUtils.get(url, loadNewsCallback);
  36. }
  37.  
  38. private String getID(int type) {...}
  39. private String getDetailUrl(String docId) {...}
  40. }
值得注意的是,这里的model是需要被presenter分离的,所以,view不会直接引用model的东西。

 ---------------------------------------------------- presenter ---------------------------------------------------- 
 
View层需要调用Presenter层加载新闻信息。
加载这件事,Presenter与model有联系。
 
Presenter的接口定义:
  1. public interface NewsPresenter {
  2. void loadNews(int type, int page);
  3. }

NewsPresenterImpl的构造函数中需要传入View层的接口对象NewView,并且需要创建一个NewsModel对象。Presenter的具体实现:

  1. public class NewsPresenterImpl implements NewsPresenter, OnLoadNewsListListener {
  2.  
  3. private static final String TAG = "NewsPresenterImpl";
  4.  
  5. private NewsView mNewsView;
  6. private NewsModel mNewsModel;
  7.  
  8. public NewsPresenterImpl(NewsView newsView) {
  9. this.mNewsView = newsView;
  10. this.mNewsModel = new NewsModelImpl();  // 相当于造了一个工具,提供网络通信服务
  11. }
  12.  
  13. @Override
  14. public void loadNews(final int type, final int pageIndex) {
  15. String url = getUrl(type, pageIndex);
  16. LogUtils.d(TAG, url);
  17. //只有第一页的或者刷新的时候才显示刷新进度条
  18. if(pageIndex == 0) {
  19. mNewsView.showProgress();
  20. }
  21. mNewsModel.loadNews(url, type, this);  // Jeff: 可见,只有presenter会使用这个包裹回调方法的loadNews,view是不知道的,但view会调用presenter的loadNews
  22. }
  23.  
  24. /**
  25. * 根据类别和页面索引创建url
  26. * @param type
  27. * @param pageIndex
  28. * @return
  29. */
  30. private String getUrl(int type, int pageIndex) {
  31. StringBuffer sb = new StringBuffer();
  32. switch (type) {
  33. case NewsFragment.NEWS_TYPE_TOP:
  34. sb.append(Urls.TOP_URL).append(Urls.TOP_ID);
  35. break;
  36. case NewsFragment.NEWS_TYPE_NBA:
  37. sb.append(Urls.COMMON_URL).append(Urls.NBA_ID);
  38. break;
  39. case NewsFragment.NEWS_TYPE_CARS:
  40. sb.append(Urls.COMMON_URL).append(Urls.CAR_ID);
  41. break;
  42. case NewsFragment.NEWS_TYPE_JOKES:
  43. sb.append(Urls.COMMON_URL).append(Urls.JOKE_ID);
  44. break;
  45. default:
  46. sb.append(Urls.TOP_URL).append(Urls.TOP_ID);
  47. break;
  48. }
  49. sb.append("/").append(pageIndex).append(Urls.END_URL);
  50. return sb.toString();
  51. }
  52.  
  53. @Override
  54. public void onSuccess(List<NewsBean> list) {
  55. mNewsView.hideProgress();
  56. mNewsView.addNews(list);
  57. }
  58.  
  59. @Override
  60. public void onFailure(String msg, Exception e) {
  61. mNewsView.hideProgress();
  62. mNewsView.showLoadFailMsg();
  63. }
  64. }

再回首view中的使用体验:

  1. public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
  2.   super.onScrollStateChanged(recyclerView, newState);
  3.   if (newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 == mAdapter.getItemCount() && mAdapter.isShowFooter())
    {
  4.     LogUtils.d(TAG, "loading more data");
  5.     mNewsPresenter.loadNews(mType, pageIndex + Urls.PAZE_SIZE);  // 加载更多
  6.   }
  7. }
  8.  
  9. public void onRefresh() {
  10.   pageIndex = 0;
  11.   if(mData != null) {
  12.     mData.clear();
  13.   }
  14.   mNewsPresenter.loadNews(mType, pageIndex);
  15. }

以上就是MVP的整个过程。

 
  
 
 

REST API

Ref: 理解RESTful架构

   Fielding将他对互联网软件的架构原则,定名为REST,即Representational State Transfer的缩写。我对这个词组的翻译是"表现层状态转化"

每种资源对应一个特定的URI。所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。

状态转化(State Transfer):GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

Ref: RESTful API 设计指南

RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。

Ref: 怎样用通俗的语言解释REST,以及RESTful?

就是用URL定位资源,用HTTP描述操作。

看Url就知道要什么
看http method就知道干什么
看http status code就知道结果如何

样例示范:Github developer REST API v3

[Module] 03 - Software Design and Architecture的更多相关文章

  1. 《Software Design中文版01》

    <Software Design中文版01> 基本信息 作者: (日)技术评论社 译者: 苏祎 出版社:人民邮电出版社 ISBN:9787115347053 上架时间:2014-3-18 ...

  2. [Architecture Design] CLK Architecture

    CLK.Prototype.Architecture 最近找数据,看到了博客园在不久之前,办了一个架构分享的活动:.Net项目分层与文件夹结构大全.看完之后觉得获益良多,接着也忍不住手痒,开始整理属于 ...

  3. Cloud Design Patterns & Architecture Styles

    Cloud Design Patterns Categories Data Management Design and Implementation Messaging Patterns Ambass ...

  4. A Philosophy of Software Design

    关于复杂性,尚无统一的定义,从不同的角度可以给出不同的答案.可以用数量来度量,比如芯片集成的电子器件越多越复杂(不一定对):按层次性[2]度量,复杂度在于层次的递归性和不可分解性.在信息论中,使用熵来 ...

  5. [Design Patterns] 4. Creation Pattern

    设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结,使用设计模式的目的是提高代码的可重用性,让代码更容易被他人理解,并保证代码可靠性.它是代码编制真正实现工程化. 四个关键元素 ...

  6. [React] 02 - Intro: why react and its design pattern

    为啥使用React,给我个理由 过去 需要手动更新DOM.费力地记录每一个状态:既不具备扩展性,又很难加入新的功能,就算可以,也是有着冒着很大的风险. 不过,使用这种开发方式很难打造出极佳的用户体验. ...

  7. [Arch] 04. Software Architectural Patterns

    让我们一起 回忆: 原则 基本认识 S 应该仅有一个引起它变化的原因 O 在不被修改的前提下被扩展 L 尽量从抽象类继承 I 应该依赖于抽象 D 倾向瘦接口 让我们开始 新课: [Design Pat ...

  8. [React] 07 - Flux: uni-flow for react

    相关资源 Ref: [Android Module] 03 - Software Design and Architecture Ref: Flux 架构入门教程 Ref: 详解React Flux架 ...

  9. Agile software architecture design document style..( sketches and no UMLs)

    http://www.infoq.com/articles/agile-software-architecture-sketches-NoUML If you're working in an agi ...

随机推荐

  1. 给Libgdx的ShapeRenderer开启抗锯齿

    http://blog.rpsg-team.com/?p=134 ——————————————————————————————————————————————————————————————————— ...

  2. 分治算法--寻找第k大数

    问题描述:给定线性序集中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k大的元素,(这里给定的线性集是无序的). 其实这个问题很简单,直接对线性序列集qsort,再找出第k个即可.但是这样的 ...

  3. loadrunner 性能测试报error-27796的解决

    网上观点: 在注册表HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters里,有如下两个键值:TcpTimedWai ...

  4. JSON的简单介绍以及C语言的JSON库使用

    JSON概述 JSON: JavaScript 对象表示法( JavaScript Object Notation) .是一种轻量级的数据交换格式. 它基于ECMAScript的一个子集. JSON采 ...

  5. HBase的compact分析

    HBase是基于LSM树存储模型的分布式NoSQL数据库.LSM树对比普遍的B+树来说,能够获得较高随机写性能的同时,也能保持可靠的随机读性能(可参考这里).在进行读请求的时候,LSM树要把多个子树( ...

  6. nextcloud私有云盘的部署

    nextcloud在centos系统下搭建自己的私有云盘 搭建一套自己的私有云盘,让数据存储更加方便.可靠.自己搭建的云存储,首先没有什么容量.下载速度的限制,而且本地访问速度很快.一开始以为Next ...

  7. 初试PyOpenGL一 (Python+OpenGL)

    很早就一直想学Python,看到一些书都有介绍,不管是做为游戏的脚本语言,还是做为开发项目的主要语言都有提及(最主要的CUDA都开始支持Python,CUDA后面一定要学),做为先熟悉一下Python ...

  8. 【转载】Linux命令行常用光标移动快捷键

    声明:下面内容来自:http://www.linuxidc.com/Linux/2016-10/136027.htm, 来源:linux社区  作者:aslongas 我转载于此处,为了作个笔记,方便 ...

  9. 用OpenGL进行曲线、曲面的绘制

    实验目的 理解Bezier曲线.曲面绘制的基本原理:理解OpenGL中一维.二维插值求值器的用法. 掌握OpenGL中曲线.曲面绘图的方法,对比不同参数下的绘图效果差异: 代码1:用四个控制点绘制一条 ...

  10. (转)live555学习笔记-UsageEnvironment和TaskScheduler

    2011-12-6阅读1264 评论1 一直想学习流媒体服务器的设计,这几天有点时间,看了一下live555的源代码.live555是一个开源的跨平台流媒体服务器,使用编程语言是C++.将现阶段学习笔 ...