Android MVC Part6 Persistence

最难的 MVC 已经讲解过了。如果你可以在 android 上实现 MVC, 这系列剩下的部分将会很容易。奖赏一下自己吧,接下来我们要讨论一下数据持久化。 我会默认你已经知道 sqlite 和其他 android 中持久化的方法。如果不知道,现在是时候去补补了。我会建议你了解 Data Access Objects 或者叫 DAOs 的概念。这个概念非常简单:我们传递一个模型的实例给 DAOs ,由它来保存,更新或者删除数据。再也不用编写一大堆 sql 语句了。妈妈再也不用担心我调用数据库了。废话不说,看看代码: :::java public class CounterDao { protected static final String TABLE = "Counter"; protected static final String _ID = "_id"; protected static final String LABEL = "label"; protected static final String COUNT = "count"; protected static final String LOCKED = "locked"; public CounterDao() { // } public ArrayList<CounterVo> getAll() { // ....

November 15, 2013

Android MVC Part4 The Controller

在 MVC 的三个部分中,控制器是最繁忙的。回顾一下,模型只是一个比较优雅的键值对象。一个好的视图是一个呆子,它只是当模型改变的时候更新自己和控制器告诉他什么就做什么。而控制器主要做三件事: 更新模型 处理视图传过来的信息 发信息给试图 这里我们主要关注后面两件事情。 ##处理消息 在所有部分当中,控制器获取消息的实现是最随意的,主要决定权在你手中,它不需要多态。你不需要跟着教程来做。而下面只是我的实现方式。但要记住 MVC 只是一个框架,不需要太注重细节,你应该关注你想要实现什么。下面是 TapListController 的示例代码。 为什么选取 TapListController 而不是 TapController ?坑爹吗? 其实主要是因为 TapController 实现了 状态模式来代理消息。所以选择 TapListController 这个更简单的类来说,以避免越说越乱。 :::java public class TapListController extends Controller{ public static final int MESSAGE_GET_COUNTERS = 1; public static final int MESSAGE_MODEL_UPDATED = 2; public static final int MESSAGE_DELETE_COUNTER = 3; public static final int MESSAGE_INCREMENT_COUNTER = 4; public static final int MESSAGE_DECREMENT_COUNTER = 5; private ArrayList<CounterVo> model; public ArrayList<CounterVo> getModel(){ return model; } public TapListController(ArrayList<CounterVo> model){ this....

November 14, 2013

Android MVC Part5 Putting It Together

之前我们已经讨论了模型(Models),视图(Views)和控制器(Controllers)。现在我们需要把他们组合在一次成为真正符合 MVC 规范。 Activity 是我们程序的每一个新视图的入口, 我们需要在 Activity 的 onCreate 方法实例化模型和控制器。其实一个典型的 MVC 模型视图是不会去实例化模型和控制器的,但是..android 有点特殊。模型一旦创建了,我们需要把 activity 注册到模型中。再者,如果你想从控制器获取消息,你也需要向控制器注册。让我们看看他们长什么模样的。 ##Activity: :::java @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); counter = new CounterVo(); counter.addListener(this); controller = new TapController(counter); controller.addOutboxHandler(new Handler(this)); // ... other set up code like referencing widgets/views ... } 这里视图在模型和控制器上都注册了。让我们看看模型对于视图的回调函数。 :::java @Override public void onChange(CounterVo counter) { mHandler.sendEmptyMessage(UPDATE_VIEW); } 一旦模型通知视图有改变,我们就在 UI 线程修改 UI控件。这就是你数据的绑定。现在让我们来看看处理从控制器发来的信息。 :::java @Override public boolean handleMessage(Message msg) { switch(msg.what) { case TapListController....

November 14, 2013

Android MVC Part2 The Model

##MVC 基本概念 MVC 是一些设计模式组合在一起的框架模式。其中的模型(The Model)代表着程序的状态。要知道,对象(Object)由状态(属性)和行为(方法)组成。所以程序也要状态(The Model)。本质上, models 是一个键值对象,当里面的状态改变的时候发送事件。视图(The View)是用户看到并与之交互的对象。视图作为观察者绑定到模型中,当模型的状态改变,视图会得到通知并作出相应的更新。当用户与视图交互,视图会发送事件给控制器(The Controller)。控制器的作用就是处理输入逻辑,它解释用户的手势,更新模型,而且向视图反馈信息。下面是它们的关系图。 ##本项目的Model 在这个项目中,最主要的模型是 CounterVo 代码如下 :::java package com.lingavin.tapcounter.vos; public class CounterVo extends SimpleObservable<CounterVo>{ private int id =-1; public int getId(){ return id; } public void setId(int id){ this.id = id; notifyObservers(this); } private int count = 0; public int getCount(){ return count; } public void setCount(int count){ this.count = count; notifyObservers(this); } private String label=""; public String getLabel(){ return label; } public void setLabel(String label){ this....

November 13, 2013

Android MVC Part3 The View

##什么是视图? 在 Android 中很多同学说 View 就是视图。但是 View 只有显示的能力,它不能处理响应和发送消息。先来看看视图的特点: 绑定模型 发送信息给控制器 处理控制器返回的信息 看来 activity 符合我们的要求,它可以绑定,可以处理信息,而且控制着 android 中所谓的 view 。但是 activity 又有一些其他的特性使它不像是 MVC 中的 V ,例如它管理着生命周期,而且它有一些像控制器的方法 boolean dispatchKeyEvent(KeyEvent event); boolean onOptionsItemSelected(MenuItem item); void onCreateContextMenu(….); so on…. 要知道 dispatchEvent 如果返回 true 表示这个 activity 会处理该事件。但是实际上,处理事件的逻辑应该交给控制器做的。 我们通过一些方法来避免上述的问题,使 activities 更像是一个视图。其灵感来源于这篇文章 为了把 activity 变成 view ,我们做了下面的操作。 ##1. 数据绑定 来看看 TapActivity 是如何绑定数据的 :::java package com.lingavin.tapcounter.activities; import com.lingavin.tapcounter.R; import com.lingavin.tapcounter.R.layout; import com.lingavin.tapcounter.vos.CounterVo; import com.lingavin.tapcounter.vos.OnChangeListener; import android.app.Activity; import android.os.Bundle; import android....

November 13, 2013

Android MVC Part1 Intro

##about 一直想寻找 android 关于基于 mvc 开发的教程, 但是国内的网站都讲得很空泛,最后在一个外国的 blog 里看到一系列的文章,介绍得很详细和具体,而且是基于项目的,所以转过来了。 这系列的文章不仅简述 MVC,还涉及到了状态模式的运用,如何用 Data Access Objects 来持久化数据,使用 web service 的命令来传递数据。这系列文章假设你已经了解用这些技术的好处,所以不会详细讲解为什么要这么用。 你可以通过点击这里查看原blog。 ##包管理 第一章先来建立工程并取好包名。好的包管理可以使明确你的类的大致作用,对编程有很大的帮助,试想下如果把所有 class 放到一个包中,当项目变大后,你想修改其中一个 class 将会是个噩梦。这里提供一个比较好的包的分类。 activities controllers daos lists models utils vos widgets 注意到所有的包名都是复数,因为里面不止一个 class 。上面的包名如果项目用不到可以删除。由包的名字我们就可以知道里面的类大致的功能,这就是好处。 activities – 很明显,这里会放入所有的 activity controllers – View 的大脑就放在这里面了 daos – 这里存放持久化逻辑 lists – 这里存放所有 list adapter models – 这里存放 View 绑定的 Model utils – 存放静态类和工具类 vos – 存放 Value Objects 也就是 POJOs 又或者 Data Transfer Objects widgets – 借鉴于android 存放定制化 widget part1 就到这里,源代码可以在这里下载 https://github....

November 12, 2013

android中的强指针和弱指针

##Demo 从一个 demo 来感性认识一下 android 里面到处使用的 RefBase ,sp 和 wp 。 首先在 android 源码目录 external 中建立 weightpointer 目录。 在 weightpointer 目录建立 weightpointer.cpp :::cpp #include <stdio.h> #include <utils/RefBase.h> class WeightClass : public RefBase{ public: void printRefCount(){ int32_t strong = getStrongCount(); weakref_type *ref = getWeakRefs(); printf("*********************\n"); printf("Strong Ref Count: %d\n", (strong == INITAL_STRONG_VALUE? 0:strong)); printf("Weak Ref Count: %d\n", ref->getWeakCount()); printf("*********************\n"); } }; 编写 WeightClass 其继承了 RefBase 。实现了一个叫做printRefCount方法,作用就是通过调用 RefBase 相关接口,获得并打印生成对象的强引用和弱引用的信息。 ###StrongClass 接着实现一个叫做 StrongClass 的类。...

November 11, 2013

compile android4.4 on mavericks

最新的 android4.4 终于出来了。曾着新安装的 mavericks ,在苹果的机器上体验了一下编译 android 系统,说实话,很愉快。 官方没有提到 mavericks 中如何配置环境来编译 android 源代码,幸好还有强大的 xda 。 现在假设现在有一台刚开封的 osx mavericks ,我们将会一步一步配置成可以编译 android 源码。 #1.从苹果商店起步 我们需要从苹果商店下载 xcode5 ,因为我们需要 xcode 给我们带来需要的包和 command line tools 。这个 xcode 很大,小水管拖了几小时才拖了回来。 或者可以点击以下链接下载安装。 Apple Xcode 5 for OS X 10.9 Mavericks #2.安装 Java JDK 6 没错,是安装 JDK 6, 别贪新鲜下个 7 回来,会哭死你的。 下载和安装都很简单,apple 已经为我们准备好 dmg 了,点击以下链接。 Java for OS X 2013-005 #3.安装 Brew 和相关的包 brew 就相当于 ubuntu 的 apt-get ,就算不是为了编译 android 也是程序员必装的软件。...

November 9, 2013

pkg-config

在编译例子的时候用到了 pkg-config 。例如 :::c $ gcc -o example example.c `pkg-config alsaplayer --cflags --libs` 这个 example 使用了 libalsaplayer.so ,只要运行上述命令,编译就可以通过。gcc 的使用已经熟悉了,但是 pkg-config 却不知道是什么东西。 最后通过 wiki 明白了这个家伙究竟是什么东西了。 pkg-config 和 ls 一样,是 可执行程序。作用是查询已安装库的各种信息。例如如果我们在终端输入 :::c pkg-config alsaplayer --cflags --libs 就会输出下面的字符串 :::c -I/usr/local/include/alsaplayer -L/usr/local/lib -lalsaplayer -ldl 打印出了头文件的位置和连接库的位置和需要的链接库,再拼接之前的 gcc -o example example.c ,难怪能够编译了。 但是究竟 pkg-config 是如何得到这些信息的? wiki 上面说了,在安装 alsaplayer 的时候,有一个 叫做 alsaplayer.pc 的文件被放到了 /usr/local/lib/pkgconfig 这个目录里面,打开这个 pc 后缀的文件,内容如下 :::c prefix=/usr/local exec_prefix=${prefix} libdir=${exec_prefix}/lib includedir=${prefix}/include plugindir=${exec_prefix}/lib/alsaplayer alsaplayer_includedir=${prefix}/include/alsaplayer inputplugindir=${plugindir}/input outputplugindir=${plugindir}/output scopeplugindir=${plugindir}/scopes interfaceplugindir=${plugindir}/interface Name: AlsaPlayer Description: AlsaPlayer audio player with plugin support Version: 0....

October 31, 2013

内存侧漏的那些事儿

在编写 c/c++ 程序的时候需要随时提防内存泄漏的问题。而且有时候内存泄漏了却不知道是哪里的问题。这些多是对内存管理不熟悉导致的。 直接开炮,从四道面试题开始 在解答之前先补了一下内存分配的知识。 一个程序通常分为三个区域存储数据。 静态存储区,存储全局变量和static变量,程序退出自动释放内存。 栈存储区,存储临时变量,函数结束自动释放内存。 堆存储区,向系统调用 malloc 和 new 将在这里划分存储空间,需要手动释放。 第一题 :::c void GetMemory(char *p) { p = (char *)malloc(100); } void Test(void) { char *str = NULL; GetMemory(str); strcpy(str, "hello world"); printf(str); } 这题的意图是想在子函数里面分配堆空间。但是程序运行却奔溃了。根据预期效果,即使没有写 free 程序也不会奔溃的吧。除非 strcpy 的时候 str 还是NULL。 调试验证了 str 还是NULL 的问题。问题出在调用函数的值传递问题。 在 c/c++ 中 值传递的意思是分配一个栈空间来存储传入子函数的值,函数结束的时候释放其空间。 知道了值传递后我们分析一下程序 首先 *str 指向 NULL 然后执行GetMemory(str),系统就开辟了一个栈 *p 来存储这个NULL(值传递) 然后 p 分配到了一段堆内存 退出子函数, 释放存储 p 地址的栈(内存泄漏,因为指向p的堆内存不可能释放了)。这个时候 str 还是 NULL ,因为 p 和 str 是两个地址完全不同的指针,p 分配的内存跟 str 无关。 运行strcpy 直接奔溃了。 :::c char *GetMemory(void) { char p[] = "hello world"; return p; } void Test(void) { char *str = NULL; str = GetMemory(); printf(str); } p 可视为指向栈内存 hello world 的指针。 返回 p 但是其栈内存已被回收,会输出不确定的内容。 :::c Void GetMemory2(char **p, int num) { *p = (char *)malloc(num); } void Test(void) { char *str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); } 比起第一题好像是改善了,能打印 hello 出来,但是最后忘记 free 了。...

October 29, 2013