Android中的MVC设计

本文探讨了在Android应用中改进MVC架构的方法,通过集中管理界面初始化数据、本地数据库缓存及数据变更通知,优化了数据处理流程。详细介绍了DaoListener、DaoObserver等组件的设计,以及如何在Activity中实现界面更新与数据处理的解耦。通过案例分析,展示了此方法的优点和潜在的性能问题,旨在提高应用的维护性和用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 前言,以前在开发j2ee的时候有那种比较完善的开发框架,例如spring mvc,所以项目架构非常清晰,基本做到了数据展示与数据获取完全分离,严格的遵守了MVC的框架模式,项目可维护性高,新进开发人员理解上手容易;后来转到android端开发,我发现android端并没有非常明显的MVC架构,因为Activity中数据展示和数据获取都可以写在一起的,也就是说Activity扮演了View和Controller的角色,因为往往是这样的:Activity中的事件响应直接去http请求或者搜索本地数据库,http请求和数据库搜索是封装起来的,但是它们返回的结果往往是通过回调得到的,而回调必须是在Activity中调用的,所以还是导致在Activity中有一大段数据处理代码,其实我认为Activity最纯粹的就应该只做数据展示和UI变化。

2. 我们团队的项目中是这么改善如上的问题的。

一切界面的初始化数据都是通过从数据库中搜索出来展示的;
所有的HTTP请求回来的数据都最先保存到本地数据库中;
数据库中的表的数据如果发生改变了之后就通知到界面,让界面及时更新数据显示;

  1. 我们做了如下实现:
    DaoListener是一个数据表监听
public class DaoListener {

    private DaoThreadMode daoThreadMode = DaoThreadMode.MainThread;

    public void onDataChanged(int daoOperationType, Object data){}

    public void onDataChanged(Object data){}

    protected DaoThreadMode getDaoThreadMode() {
        return daoThreadMode;
    }

    protected void setDaoThreadMode(DaoThreadMode daoThreadMode) {
        this.daoThreadMode = daoThreadMode;
    }
}

DaoObserver是数据表的观察者,一旦表数据发生改变就立刻发布通知

public class DaoObserver {

    private static List<DaoListener> listeners = new ArrayList<>();

    private static Handler uiHandler = new Handler(Looper.getMainLooper());

    private static Handler asyncHandler;

    private static HandlerThread handlerThread;

    static {
        handlerThread = new HandlerThread("backgroud_thread");
        handlerThread.start();
        asyncHandler = new Handler(handlerThread.getLooper());
    }

    public static void regist(DaoListener daoListener) {
        daoListener.setDaoThreadMode(DaoThreadMode.MainThread);
        regist(daoListener, DaoThreadMode.MainThread);
    }

    public static void regist(DaoListener daoListener, DaoThreadMode daoThreadMode) {
        daoListener.setDaoThreadMode(daoThreadMode);
        listeners.add(daoListener);
    }

    public static void unRegist(DaoListener daoListener) {
        listeners.remove(daoListener);
    }

    /**
     * 回调将保证在主线程中执行,禁止执行耗时任务,若执行耗时任务则要另开线程
     *
     * @param data
     */
    public static synchronized void publish(int daoOperationType, Object data) {
        notifyDataChanged(daoOperationType, data);
    }

    private static void notifyDataChanged(final int daoOperationType, final Object data) {
        for (final DaoListener daoListener : listeners) {
            DaoThreadMode daoThreadMode = daoListener.getDaoThreadMode();
            if (daoThreadMode == DaoThreadMode.MainThread) {
                uiHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        onDataChanged(daoListener, daoOperationType, data);
                    }
                });
            } else if (daoThreadMode == DaoThreadMode.BackgroundThread) {
                if (Looper.myLooper() == Looper.getMainLooper()) {
                    asyncHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            onDataChanged(daoListener, daoOperationType, data);
                        }
                    });
                } else {
                    onDataChanged(daoListener, daoOperationType, data);
                }
            } else if (daoThreadMode == DaoThreadMode.Async) {
                asyncHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        onDataChanged(daoListener, daoOperationType, data);
                    }
                });
            } else {
                // PostThread线程模式
                onDataChanged(daoListener, daoOperationType, data);
            }
        }
    }

    private static void onDataChanged(DaoListener daoListener, int daoOperationType, Object data) {
        daoListener.onDataChanged(daoOperationType, data);
        daoListener.onDataChanged(data);
    }
}

DaoOperation数据表更新的操作,增删查改

public interface DaoOperation {

    int INSERT = 1;
    int DELETE = 2;
    int UPDATE = 3;
    int SELECT = 4;
    int INSERT_BATCH = 5;
    int DELETE_BATCH = 6;
    int UPDATE_BATCH = 7;
}

DaoThreadMode数据表更新回调的线程模型

public enum DaoThreadMode {

    /**
     * 同一个线程
     */
    PostThread,

    /**
     * 主线程
     */
    MainThread,

    /**
     * 后台线程
     */
    BackgroundThread,

    /**
     * 单独开一个线程
     */
    Async
}

而在Activity中就做如下实现:

public class TestActivity extends Activity {

    DaoListener daoListener = new DaoListener() {
        @Override
        public void onDataChanged(Object data) {
            if (data instanceof Object) {
                changeUI();
            } else if (data instanceof Location) {
                showData();
            } else {
                // other type of data
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DaoObserver.regist(daoListener);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        DaoObserver.unRegist(daoListener);
    }

    private void changeUI() {
        // do something
    }

    private void showData() {
        // do something
    }
}

我们的项目的数据库操作是采用ormlite,每个数据表都对应一个实体bean和一个dao,这个dao继承一个表操作的基类dao,在基类dao里面才实现真正的增删查改并且检测表数据更新,发布更新通知到DaoListener,这样做的好处在于把所有的界面数据更新的触发点统一在一起了而不是像以前一样分散在各自的方法中;例如Activity里面有个方法去请求Http数据,那它直接在回调中把数据插入到数据库,这样在DaoListener会收到数据更新的通知,并且会把最新的数据以Object对象的形式传递过来,可根据区分Object的类型来对各个Http请求进行区别。
流程图

优点是:界面数据更新触发统一在一起,避免出现http请求数据成功并刷新界面成功,但是插入数据库失败导致界面数据和数据库中的数据不一致;把Http请求和数据刷新操作隔离开来。
缺点是:如果在DaoListener再次做数据库更新操作有可能造成递归反之程序严重卡顿,造成性能问题,之前遇到过,只要不再DaoListener做数据库操作就没有问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值