Android开发经验漫谈-XActivity

总最近加班超多,今天终于抽空有时间总结下东西。开发经验漫谈,主要记录下自己在工作中遇到的问题和解决这些问题方法,以及自己的一些设想和所见所闻。

第一行Android代码

11年大四,花了一整天时间,安装好开发环境,配置好环境变量,照着百度出来的教程,ADT自动新建了一个MainActivity,里面有个TextView,默认写的是Hello World!。然后把这几个字改成hello android,那时候还没Android手机,看着模拟器启动了两分钟,运行成功,屏幕上打印出一行字-Hello Android,幸福感爆棚。这就是我的第一行Anroid代码。从此,光荣的成为了一名Android Developer,并以此为职业至今。

糟糕的Activity

自学Android开发,没人指导,如果也没有经历过好的项目熏陶的出入门开发者,很容易把自己的项目搞得一团浆糊。完成在Activity里面打印遗憾Hello Android的壮举后,我又强行记下了Activity的生命周期,结识了Button,ListView,EditText等等控件。虽然常说Android的四大组件,真正会百分百用到的组件和最最常用的组件就只有Activity。也就是在学会了这些的时候,做了人生第一个Android项目,是一个个人事务管理软件Android端,导师的横向项目,这真是一个糟糕的体验。
让一个刚刚学会了什么是四大组件的人在没有任何指导和商业项目开发经验的情况下直接开始一个项目的开发。幸运的是,最终还是通过拖原生控件拖出来一个巨丑的App,插句题外话:原生控件的美观性这一反面而言Android确实不如iOS,此项目毫无架构可言,所有的类放在一个包下面,命名方式奇特,静态变量遍地都是,主线程放了无数个和界面绘制无关的东西,最大的Activity类高达2000行代码,天知道它是怎么运行起来的。这是我的经历,我想很多刚入行的新人也会遇到类似的问题。

XActivity

XActivity的目的就是为了来填一个Activity写了2000行的坑。这么多代码,为了找到一个东西也需要翻几页才可以,且不说结构混乱,我估计除了自己没人能够看懂它。首先明确我们的需求:

  • 代码结构清晰
  • 简洁 易懂
  • 功能全 耦合低
    分别对应解决问题
  • 把代码放到该放的地方
  • 2000行太多了
  • 其他项目做类似的东西没法复用,基类没有抽取公共功能

结构清晰

开始写项目直接继承Activity,Eclipse下面按住Ctrl+Shift+S,V弹出可以重写的方法,这里面的方法包括onCreate到onDestory这些生命周期方法,异常处理,菜单调出,事件传递一大片,功能齐全但是和我们的上层业务不相关。所以定义XAcitivty集成Activity,为了支持碎片化采用FragmentActivity,同时为了支持老的Android设备,使用V4包下面的FragmentActivity。定义功能接口:

public interface IActivityFunction {
    /** * 初始化界面 */
    void initContentView();

    /** * 初始化数据 */
    void initData();

    /** * 初始化组件 */
    void initWidget();
}

使用抽象类基类XActivity继承FragmentActivity实现IActivityFunction

public abstract class XActivity extends FragmentActivity implements IActivityFunction {

    private void initAll() {
        initData();
        initWidget();
    }

    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        initContentView();
        initAll();
    }

    @Override
    public void initContentView() {
        // setContentView();
        // 一大堆findViewById
    }

    @Override
    public void initData() {
        // 数据初始化 申明的Adapter List 数组赋初值
        // 对象 网络初始化 在这里调用他的构造器
    }

    @Override
    public void initWidget() {
        // 控件初始化
        // 比如设置DialogProgress TitleBar 要显示的内容
    }

}

通过这样来定义数据,而不是一大坨数据全部写到onCreate方法里面,一个复杂的Activity很可能有上百行这样的代码,无规则的堆叠到一起造成维护困难。

在完成这一步后,XActivity已经初见雏形,但是还有些常用功能没有加入
1.initData方法,是UI线程,不支持子线程数据加载
当然不希望项目中大量存在这样的代码

new Thread(new Runnable() {
            @Override
            public void run() {
                // DoSomeThing
            }
        }).start();

而且还要考虑一个问题是如果任务执行完回调,这时候又会多定义出一个回调接口。不如定义一个通用方法在XActivity,项目中所有Acitivty都拥有了子线程加载数据回调方法。

public abstract class XActivity extends FragmentActivity implements IActivityFunction {

    public final static int X_MSG_FINISH = 0x1000;
    private ThreadCallback callback;
    private XActivityHandler xHandler = new XActivityHandler(this);

    private static class XActivityHandler extends Handler {
        private final SoftReference<XActivity> soft;

        public XActivityHandler(XActivity aty) {
            super();
            this.soft = new SoftReference<XActivity>(aty);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 收到消息 并且软引用没有被回收
            if (msg.what == X_MSG_FINISH && soft.get() != null) {
                soft.get().callback.onFinish();
            }
        }
    }

    private void initAll() {
        initData();
        new Thread(new Runnable() {
            @Override
            public void run() {
                // DoSomeThing
                initDataByThread();
                xHandler.sendEmptyMessage(X_MSG_FINISH);
            }
        }).start();
        initWidget();
    }

    protected void initDataByThreadFinish() {
    }

    @Override
    public void initDataByThread() {
        callback = new ThreadCallback() {
            @Override
            public void onFinish() {
                initDataByThreadFinish();
            }
        };
    }

    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        initContentView();
        initAll();
    }

    @Override
    public void initContentView() {
        // setContentView();
        // 一大堆findViewById
    }

    @Override
    public void initData() {
        // 数据初始化 申明的Adapter List 数组赋初值
        // 对象 网络初始化 在这里调用他的构造器
    }

    @Override
    public void initWidget() {
        // 控件初始化
        // 比如设置DialogProgress TitleBar 要显示的内容
    }

    private interface ThreadCallback {
        void onFinish();
    }

}

通过ThreadCallback接口定义onFinish方法回调线程执行完成的操作,然后通过onDataByThreadFinish()转发,直接重写onDataByThreadFinish方法就可以实现,回调后的操作了。
通过一个Handler持有一个XActivity的软引用,这样如果在引用被后台回收了,就不执行线程加载完成的回调方法。
在业务Activity中加载子线程数据只需要重写最多两个方法,

  1. initDataByThread() 加载数据
  2. initDataByThreadFinish() 加载数据完成后的回调函数
    这是设计模式里的一个典型的模板方法模式

简洁

让一个Acitivty变得简洁:

  • 共有代码抽取,把公共代码提取到基类或者工具类
  • 业务逻辑明确,如使用MVC模式开发,Activity只负责界面有关View层的工作
  • 模块化开发,抽取公共组件+善用Fragment,模块化开发

一个方法,如果在Activity A中会用,B中也会用到,那么就要考虑是否把它放在工具类中,或者放在一个公共方法中。如果这个方法可能和界面存在通信,可以使用Handler转发消息的方式通知界面更新。
按照大多数主流开发规范的推荐,Activity只负责处理界面逻辑,业务代码放到业务层,存储逻辑反倒存储层,不要把所有工作都放到界面层,同时还要注意Activity是存在主线程上的,不要再上面写耗时操作,导致ANR异常。
最后说说模块化开发,我认为模块化开发是软件开发里面最核心,最有用和最有价值的东西了。现在生活中很多事情都会不由自主的往模块化的思想靠拢。所谓模块化,就是把一个大的任务分成一个一个小的模块,最后所有的这些模块拼接起来形成任务本身。这样做有几个好处:

  • 任务细分后,便于执行和制定节点
  • 方便团队合作
  • 某一个环节(模块)出现问题可以快速定位处理
    最近做的一个项目为例,因为客户需求设计图变过几次,所以最开始的时候没有分模块,大部分信息写到Activity里面的,于是功能还没写完Activity已经800行了,赶紧把业务分拆到几个Fragment里买去,搞清楚每个Fragment的入口和出口就行了,统一通过Handler进行和Activity的通信。可能使用事务总线解耦会更好,但是这会消耗更多的系统资源和影响响应效率。


    Android开发经验漫谈-XActivity_第1张图片

    统计了下总共进行了5次网络请求,1次蓝牙连接和通信,1次定位,拍照和图片选取以及预览。提交数据还有2次请求预设,以及成功后的回调方法实现。如果这些代码都存在一个Activity里面简直崩溃,按照业务划分为

  • 一个公共组件顶部导航-在BaseActivity里卖弄

  • 三个Fragemnt CarInfoFragment负责连接和读取蓝牙信息; TakePhotoFragment负责拍照和图片选取,图片预览和上传; LocationHelpFragment负责获取地理位置,获取服务器地理信息,和判定是否是相近位置点
  • Activity自带控件;1个EditText 和2个Button 以及确认和删除的网络请求方法
    只要维护好组件之间,和组件和Activity的通信就行了,大大的减少了代码量和维护难度
    形成的示意图如下:

    Android开发经验漫谈-XActivity_第2张图片
    这里有些歪题了,回归正题

功能抽取

  1. 简洁的方式对Fragment加载和替换操作
  2. Activity跳转优雅替换
  3. 广播注册和反注册优雅实现
  4. Activity堆栈管理
  5. findViewById函数优雅替换
  6. ProgressDialog抽取到基类
    // 初始化View
    @Override
    public void initContentView() {
        setContentView(R.layout.activity_main);
    }

    // 子线程加载完成后,切换到主线程做响应操作
    @Override
    protected void initDataByThreadFinish() {
        super.initDataByThreadFinish();
        tvHello.setText("wait 3s");
    }

    // 休眠3秒模拟子线程加载数据
    @Override
    public void initDataByThread() {
        super.initDataByThread();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 初始化数据
    @Override
    public void initData() {
        leftFragment = new FragmentLeft();
        rightFragment = new FragmentRight();
        super.initData();
    }

    // 初始化组件
    @Override
    public void initWidget() {
        super.initWidget();
        tvHello = $(R.id.tvHello);
        btnShowDlg = $(R.id.btnShowDlg, true);
        btnChangeFrag = $(R.id.btnChangeFrag, true);
    }

    // 点击事件处理
    @Override
    public void widgetClick(View v) {
        super.widgetClick(v);
        switch (v.getId()) {
        case R.id.btnShowDlg:
            showProgress("Waiting...");
            break;
        case R.id.btnChangeFrag:
            break;
        default:
            break;
        }
    }

参考提取完成后的代码示例,比较如果不使用抽取功能的方式实现的代码可读性和可维护性
通过一个简单的泛型处理,省略掉冗余和强转难看的findViewByID

    @Override
    public <T extends View> T $(int id) {
        return (T) findViewById(id);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends View> T $(int id, boolean clickFlag) {
        T view = (T) findViewById(id);
        if (clickFlag) {
            view.setOnClickListener(this);
        }
        return view;
    }

通过一个Stack记录下存在的Activity,通过这个静态类可以在任何地方引用当前处于栈顶的Activity,方便实现解耦。用一个状态位记录Activity的状态,这样可以很清楚而且简单的知道当前Activity处于生命周期中的那个阶段

public int activityState = DESTORY;

    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        XActivityManager.getManager().addActivity(this);
        initContentView();
        initAll();
        registerBroadcast();
        activityState = CREATE;

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        XActivityManager.getManager().finishActivity(this);
        unRegisterBroadcast();
        activityState = DESTORY;
    }

    @Override
    protected void onPause() {
        super.onPause();
        activityState = PAUSE;
    }

    @Override
    protected void onResume() {
        super.onResume();
        activityState = RESUME;
    }

    @Override
    protected void onStart() {
        super.onStart();
        activityState = START;
    }

    @Override
    protected void onStop() {
        super.onStop();
        activityState = STOP;
    }

通过静态内部类,获取Activity 栈管理器的单例

public class XActivityManager {
    /** 记录管理器内所有Activity */
    private Stack<IActivityFunction> aties;

    /** * Single模式 私有构造方法和初始化 */
    private XActivityManager() {
        if (null == aties) {
            aties = new Stack<IActivityFunction>();
        }
    }

    /** * 建造管理器,Lazy Loading * * @return */
    public static XActivityManager getManager() {

        return XActivityManagerHolder.manager;
    }

    // 单例使用静态内部类实现 懒加载。
    public static class XActivityManagerHolder {
        private static final XActivityManager manager = new XActivityManager();
    }

    /** * 获取栈内Activity个数 * * @return */
    public int getCount() {
        return aties.size();
    }

    /** * Activity入栈 * * @param aty */
    public void addActivity(IActivityFunction aty) {
        aties.add(aty);
    }

    /** * 获取栈顶Acitivty * * @return */
    public Activity topActivity() {
        if (getCount() == 0) {
            return null;
        }
        return (Activity) aties.firstElement();
    }

    /** * 结束指定Activity(重载) * * @param clazz * @return */
    public Activity findActivity(Class<?> clazz) {
        IActivityFunction aty = null;
        for (IActivityFunction activity : aties) {
            if (activity.getClass().equals(clazz)) {
                aty = activity;
                break;
            }
        }
        return (Activity) aty;
    }

    /** * 结束栈顶Activity */
    public void finishActivity() {
        Activity aty = topActivity();
        if (aty != null) {
            finishActivity(aty);
        }
    }

    /** * 结束指定Acitivty * * @param clazz */
    public void finishActivity(Class<?> clazz) {
        Activity aty = findActivity(clazz);
        if (aty != null) {
            aties.remove(aty);
        }
    }

    /** * 结束除了参数以为的所有Activity * * @param clazz */
    public void finishOtherActivity(Class<?> clazz) {
        for (IActivityFunction aty : aties) {
            if (aty.getClass().equals(clazz)) {
                finishActivity((Activity) aty);
            }
        }
    }

    /** * 结束所有activity */
    public void finishAllActivity() {
        for (IActivityFunction aty : aties) {
            finishActivity((Activity) aty);
        }
    }

    /** * App退出 */
    public void appExit() {
        try {
            finishActivity();
            Runtime.getRuntime().exit(0);
        } catch (Exception e) {
            e.printStackTrace();
            // -1->异常退出
            Runtime.getRuntime().exit(-1);
        }
    }

    /** * 结束指定Activity (重载) * * @param activity */
    public void finishActivity(Activity activity) {
        if (activity != null) {
            aties.remove(activity);
            activity = null;
        }
    }

}

代码下载地址:我的GitHub

你可能感兴趣的:(android,Android开发,开发经验,经验)