美文网首页
15.《Android开发进阶 从小工到专家》笔记

15.《Android开发进阶 从小工到专家》笔记

作者: 为梦想战斗 | 来源:发表于2017-10-24 07:42 被阅读0次

一、Android的构成基石——四大组件
1.Activity
2.Service与AIDL
3.Broadcast
4.ContentProvider

二、创造出丰富多彩的UI——View与动画
1.重要的View控件
2.必须掌握的最重要的技能——自定义控件
最为自由的一种实现——自定义View:继承自View创建自定义控件;如有需要自定义View属性,也就是在valus/attrs.xml中定义属性集;在xml中引入命名控件,设置属性;在代码中读取xml中的属性,初始化视图;测量视图的大小;绘制视图内容
View的尺寸测量:EXACTLY,AT_MOST,UNSPECIFIED
Canvas与Paint
自定义ViewGroup
3.Scroller的使用
Scroller是一个帮助View滚动的辅助类。
4.让应用更精彩——动画
Frame动画:一系列图片构成,然后设置给某个View
补间动画:操作某个控件让其展现出旋转、渐变、移动、缩放的一种转换过程。常用属性:alpha,scale,translate,rotate
属性动画:ValueAnimator、ObjectAnimator、AnimatorSet。动画执行时间——TypeEvaluator与TimeInterpolator

三、保证App流畅的关键因素——多线程
1.Android中的消息机制
处理消息的手段——Handler、Looper、与MessageQueue
2.Android中的多线程
多线程的实现——Thread和Runnable
线程的wait、sleep、join和yield
与多线程相关的方法——Callable、Future和FutureTask:Callable与Runnable的功能大致相似,不同的是Callable是一个泛型接口;Runnable与Callable都像“脱缰的野马“,一旦解开缰绳就无法控制。Future就是这类“战马”的标准,Future为线程池制定了可管理的任务标准,它提供了cancel/isDone/get/set函数。Future只是定义了一些规范的接口,FutureTask是Future的实现类,FutureTask会像Thread包装Runnable那样对Runnable和Callable<V>进行包装,Runnable与Callable由构造函数注入。
构建服务器应用程序的有效方法——线程池:ThreadPoolExecutor、ScheduledThreadPoolExecutor
同步集合:CopyOnWriteArrayList、ConcurrentHashMap、BlockingQueue
同步锁:Synchronized、ReentrantLock与Condition、Semaphore、CyclicBarrier、CountDownLatch
AsyncTask的原理:

四、HTTP网络请求
1.HTTP网络请求原理
2.Android中执行网络请求
HttpClient、HttpURLConnection
3.网络框架的设计与实现

五、SQLite数据库
1.SQLite3的基本介绍
2.SQLite中折SQL语句
3.Android中的数据库开发
4.数据库框架ActiveAndroid的使用与基本原理

六、让程序更优秀的技术——性能优化
1.布局优化
引入include,merge的使用,ViewStub视图:viewStub.inflate();,减少布局层级:多使用RelativeLayout,少用AbsoluteLayout;2、在列表中不使用LinearLayout的weight属性;3、用ViewStub加载不常用的布局
2.内存优化
不要使用太多Service、UI不可见或内存紧张时释放内存、检查应该使用多少内存、注意bitmaps、使用优化的数据容器、注意内存开销、注意代码抽象、序列化的数据使用nano protobufs、避免使用依赖注入框架、注意外部库、优化整体性能、使用ProGuard和zipalign、使用多进程
3.内存泄漏
使用Memory Monitor监测内存使用情况
使用LeakCanary:

   创建一个application子类:
public class MyApplication extends Application {
  private static RefWatcher mRefWatcher;
  @Override
  public void onCreate() {
    super.onCreate();
    mRefWatcher = LeakCanary.install(this);
  }
  public static RefWatcher getRefWatcher() {
    return mRefWatcher;
  }
}
修改android:name=".MyApplication"

4.性能优化
过度绘制:Develpers Options --> Debug GPU Overdraw --> Show overdraw area
图形渲染:Hierarchy Viewer:tools---Android---Android Device Monitor查看视图层级;Tree View:节点性能分析,黄色、红色是需要优化的地方
TraceView分析每个函数执行时间
Debug.startMethodTracing("text_activity.trace");
Debug.stopMethodTracing();

七、装点程序“门面”——代码规范
1.排版
2.注释
3.命名
4.编码建议

八、让不断升级的系统更好管理——Git版本控制
1.Git起源
2.Git基本原理
3.Git基本配置
4.Git基本命令
5.项目协作——GitHub

九、开发人员必备的技能——单元测试
1.什么是单元测试
2.为什么要做单元测试
3.不写单元测试的借口
4.如何写单元测试
5.测试哪些内容:边界条件、覆盖执行路径
6.模拟所需的功能模块——Mock对象
手动Mock对象、使用Mockito库
7.Android中的单元测试
8.测试驱动开发(TDD)简介

十、六大原则与设计模式
1.面向对象六大原则:单一职责、里氏替换、依赖倒置、开闭、接口隔离、迪米特
2.设计模式
3.避免掉进过度设计的怪圈
4.反模式

十一、重构
1.为什么要重构
2.什么时候重构
3.常用的重构手法
提取子函数、上移函数到父类、下移函数到子类、封装固定的调用逻辑、使用泛型去除重复逻辑、使用对象避免过多的函数、转移函数、将类型码转为状态模式、NullObject、分解“胖”类型

十二、从码农历练成工程师——综合实战
1.项目需求
2.第一版实现
3.第一版存在的问题与重构
类型重命名、去除重复代码、简化复杂函数、明确职责与降低耦合
4.降低复杂性——MVP架构
Presenter交互中间人、View用户界面、Model数据的存取
首先要定义View角色,定义一个MvpView接口代表View角色的抽象定义,它有两个接口分别为加载数据时显示loading和加载完数据之后隐藏loading:

public interface MvpView{
    public void onShowLoding();
    public void onHideLoding();
}

然后是具体的View角色,对Activity、Fragment定义一个功能接口。例如,对于文章列表Fragment,定义一个ArticleListView,它的功能将从网络或数据库中获取到的文章列表显示到RecyclerView上,然后还有一个功能是第一次从网络上加载到数据 后会将从数据库加载 的缓存文章清除,因此,还需要一个清除功能:

public interface ArticleListView extends MvpView{
  public void onFetchedArticles(List<Article> result);
  public void clearCacheArticles();
}

然后ArticleListFragment要实现这个接口,在这些接口中实现相应的功能,即此时ArticleListFragment扮演的是这个MvpView角色:

public class ArticleListFragment extends Fragment implements onRefreshListener,onLoadListener,ArticleListView {
  protected SwipeRefreshLayout mSwipeRefreshLayout;
  protected AutoLoadRecyclerView mRecyclerView;
  protected ArticleAdapter mAdapter;
  private ArticleListPresenter mPresenter = new ArticleListPresenter();

  public void onResume(){
    super.onResume();
    mPresenter.attach(this);
    mPresenter.fetchLastestArticles();
  }

  public void onRefresh() {
    mPresenter.fetchLastestArticles();
  }

  public void onLoad() {
    mPresenter.loadNextPageArticles();
  }

  public void onFetchedArticles(List<Article> result) {
    mAdapter.addItems(result);
  }

  public void clearCacheArticles() {
    mAdapter.clear();
  }

  public void onShowLoding() {
    mSwipeRefreshLayout.setRefreshing(true);
  }

  public void onHideLoding() {
    mSwipeRefreshLayout.setRefreshing(false);
  }

  public void onDestroy() {
    super.onDestroy();
    mPresenter.detach();
  }
}

从上述程序中可以看到,此时的Fragment很简单,只有一些初始化视图和对视图进行一些简单的代码,相关的业务逻辑都通过ArticleListPresenter来实现。这里又引入了MVP中的另一个核心元素Presenter。Presenter是View与Model交互的中间人,业务逻辑也包含在该角色中。这样就相当于Presenter要持有View对象,而我们的View对象往往是Activity、Fragment,当Activity退出时Presenter如果正在执行一个耗时的网络请求,那么将导致Activity的内存无法被释放而千万内存泄漏。因此,需要定义一个含有关联、取消关联View角色的Presenter基类:

public abstract class BasePresenter<T extends MvpView> {
  T mView;

  public void attach(T view) {
    mView = view;
  }

  public void detach() {
    mView = null;
  }
}

其中T就是一个具体的MVP的View。
然后我们定义继承自BasePresenter的ArticleListPresenter类,将ArticleListFragment的业务逻辑转移到该类中:

public class ArticleListPresenter extends BasePresenter<ArticleListView> {
  public static final int FIRST_PAGE = 1;
  PRIVATE INT MPAGEINDEX = first_page;
  ArticleParser mArticleParser = new ArticleParser();
  private boolean isCacheLoaded = false;

  public void fetchLastestArticles() {
    if (!isCacheLoaded) {
      mView.fetchedArticles(DatabaseHelper.getInstance().loadArticles());
    }
    fetchArticlesAsync(FIRST_PAGE);
  }
 
  private void fetchArticleAsync(final int page) {
    mView.onShowLoding();
    HttpFlinger.get(prepareRequestUrl(page),mArticleParse,new DataListener<List<Article>>() {
        @Override
        public void onComplete(List<Article> result) {
          mView.onHideLoding();
          if (!isCacheLoaded && result != null) { mView.clearCacheArticles(); isCacheLoaded = true;
          }
          if (result == null) { return; }
          mView.onFetchedArticles(result);
          DatabaseHelper.getInstance().saveArticles(result);
          updatePageIndex(page,result);
        }
      });
  }
//代码省略
}

从上述程序中可以看到网络请求、数据库操作等相关的业务逻辑都被隔离在Presenter中。这样一来,ArticleListFragment等View角色就只负责处理视图的显示、页面跳转等简单功能,避免了与业务逻辑耦合在Activity、Fragment等View角色中,导致难以维护和修改。而Presenter则负责具体的业务逻辑处理,从数据库、网络获取资源,并且对数据、操作进行一些逻辑处理。这个MVP实际上并没有真正意义上的Model,这是因为目前项目规模可以暂时忽略掉这部分的结构。当业务比较复杂时,可以将获取数据的操作移到一个独立的Model层,Model就是我们的数据源,而不是在Presenter中来控制这个逻辑,此时Model相当于你的数据中心。
5.开启单元测试之路——添加单元测试

public interface MvpView {
    public void onShowLoding();
    public void onHideLoding();
}

public interface ArticleListView extends MvpView {
    public void onFetchedArticles(List<Article> result);

    public void clearCacheArticles();
}

public interface ArticleDetailView extends MvpView {
    public void onFetchedArticleContent(String html);
}


public abstract class BasePresenter<T extends MvpView> {
    protected T mView;

    public void attach(T view) {
        mView = view;
    }

    public void detach() {
        mView = null;
    }
}

public class ArticleListPresenter extends BasePresenter<ArticleListView> {

    public static final int FIRST_PAGE = 1;
    private int mPageIndex = FIRST_PAGE;
    ArticleParser mArticleParser = new ArticleParser();
    private boolean isCacheLoaded = false;

    /**
     * 第一次先从数据库中加载缓存,然后再从网络上获取数据
     */
    public void fetchLastestArticles() {
        if (!isCacheLoaded) {
            mView.onFetchedArticles(DatabaseHelper.getInstance().loadArticles());
        }
        // 从网络上获取最新的数据
        fetchArticlesAsync(FIRST_PAGE);
    }

    private void fetchArticlesAsync(final int page) {
        mView.onShowLoding();
        HttpFlinger.get(prepareRequestUrl(page), mArticleParser, new DataListener<List<Article>>() {
            @Override
            public void onComplete(List<Article> result) {
                mView.onHideLoding();
                if (!isCacheLoaded && result != null) {
                    mView.clearCacheArticles();
                    isCacheLoaded = true;
                }

                if (result == null) {
                    return;
                }
                mView.onFetchedArticles(result);
                // 存储文章列表
                DatabaseHelper.getInstance().saveArticles(result);
                updatePageIndex(page, result);
            }
        });
    }

    /**
     * 更新下一页的索引,当请求成功且不是第一次请求最新数据时更新索引值。
     * 
     * @param loadPage
     * @param result
     */
    public void updatePageIndex(int curPage, List<Article> result) {
        if (result.size() > 0
                && shouldUpdatePageIndex(curPage)) {
            mPageIndex++;
        }
    }

    /**
     * 是否应该更新Page索引。更新索引值的时机有两个,一个是首次成功加载最新数据时mPageIndex需要更新;另一个是每次加载更多数据时需要更新.
     * 
     * @param curPage
     * @return
     */
    private boolean shouldUpdatePageIndex(int curPage) {
        return (mPageIndex > 1 && curPage > 1)
                || (curPage == 1 && mPageIndex == 1);
    }

    public int getPageIndex() {
        return mPageIndex;
    }

    public void loadNextPageArticles() {
        fetchArticlesAsync(mPageIndex);
    }

    private String prepareRequestUrl(int page) {
        return "http://www.devtf.cn/api/v1/?type=articles&page=" + page
                + "&count=20&category=1";
    }
}

public class ArticleDetailPresenter extends BasePresenter<ArticleDetailView> {

    /**
     * 加载文章的具体内容,先从数据库中加载,如果数据库中有,那么则不会从网络上获取
     * 
     * @param postId
     */
    public void fetchArticleContent(final String postId,String title) {
        // 从数据库上获取文章内容缓存
        // ArticleDetail cacheDetail =
        // DatabaseHelper.getInstance().loadArticleDetail(postId);
        // String articleContent = cacheDetail.content;

        String articleContent = loadArticleContentFromDB(postId);
        if (!TextUtils.isEmpty(articleContent)) {
            String htmlContent = HtmlUtls.wrapArticleContent(title, articleContent);
            mView.onFetchedArticleContent(htmlContent);
        } else {
            fetchContentFromServer(postId, title);
        }
    }

    public String loadArticleContentFromDB(String postId) {
        return DatabaseHelper.getInstance().loadArticleDetail(postId).content;
    }

    protected void fetchContentFromServer(final String postId,final String title) {
        mView.onShowLoding();
        String reqURL = "http://www.devtf.cn/api/v1/?type=article&post_id=" + postId;
        HttpFlinger.get(reqURL,
                new DataListener<String>() {
                    @Override
                    public void onComplete(String result) {
                        mView.onHideLoding();
                        if (TextUtils.isEmpty(result)) {
                            result = "未获取到文章内容~";
                        }
                        mView.onFetchedArticleContent(HtmlUtls.wrapArticleContent(title, result));
                        DatabaseHelper.getInstance().saveArticleDetail(
                                new ArticleDetail(postId, result));
                    }
                });
    }
}

相关文章

网友评论

      本文标题:15.《Android开发进阶 从小工到专家》笔记

      本文链接:https://www.haomeiwen.com/subject/ihsguxtx.html