浅析Android代码质量管理
作者:伟峰_888
模板方法-基类封装
Activity和Fragment应该是Android最常用的组件,对他进行简单的封装对提高代码的简洁性也有很大的帮助。
BaseActivity :
public abstract class BaseActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); init(); findViews(); initData(); setListener(); setting(); } /** * 获得上下文 * @return Context */ public Context getContext(){ return this; } /** * 始化参数 */ public abstract void init(); /** * 查找所有的控件 */ public abstract void findViews(); /** * 初始化页面数据 */ public abstract void initData(); /** * 设置控件的监听事件 */ public abstract void setListener(); /** * 后续参数设置 */ public abstract void setting(); }
BaseFragment :
public abstract class BaseFragment extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public void onStart() { super.onStart(); init(); findViews(); initData(); setListener(); setting(); } public Context getContext() { return getActivity(); } public abstract void init(); public abstract void findViews(); public abstract void initData(); public abstract void setListener(); public abstract void setting(); }
代码比较简单,用到了模板设计模式,一个方法只做一样事情,初始化的就只做初始化操作,设置监听的就只设置监听。不管多少个Activity\Fragment都能很好的统一化编码风格,看起来更清晰不乱。
Fragment简单管理
下面先看看标准的创建和管理Fragment
private void showFragment(){ FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); hideFragment(fragmentTransaction); if (mFragment1== null) { mFragment1 = new MyFragment1(context); fragmentTransaction.add(R.id.content, mFragment1); fragmentTransaction.commit(); } else { fragmentTransaction.show(mFragment1); fragmentTransaction.commit(); } }
每次创建一个Fragment都要复制一边这个方法,代码冗余、不利于维护和更新。
下面封装一下
public class FragmentFactory { private FragmentActivity mContext; private static FragmentFactory factory = new FragmentFactory(); //用于存储已创建的Fragment对象 private Map<String, Fragment> mFragmentMap=new HashMap<>(); private int mLayoutId; private FragmentFactory() { } public static FragmentFactory getInstance() { return factory; } //layoutId 传入布局文件的id public FragmentFactory init(FragmentActivity context,int layoutId) { this.mContext = context; this.mLayoutId=layoutId; return factory; } public Activity getParentActivity() { return mContext; } private <T extends Fragment> Fragment createFragment(Class<T> clazz) { Fragment fragment = null; try { fragment = getFragment(clazz.getName()); FragmentTransaction fragmentTransaction = mContext.getSupportFragmentManager().beginTransaction(); hideFragment(fragmentTransaction); if (fragment == null) { fragment = (Fragment) clazz.newInstance(); setFragment(fragment); fragmentTransaction.add(mLayoutId, fragment); fragmentTransaction.commit(); } else { fragmentTransaction.show(fragment); fragmentTransaction.commit(); } } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return fragment; } private <T extends Fragment> Fragment getFragment(String className) { Fragment fragment = mFragmentMap.get(className); return fragment; } private <T extends Fragment> void setFragment(Fragment fragment) throws InstantiationException, IllegalAccessException { String className = fragment.getClass().getName(); mFragmentMap.put(className, fragment); } private void hideFragment(FragmentTransaction fragmentTransaction) { Set<String> keySet = mFragmentMap.keySet(); for (String key : keySet) { Fragment fragment = mFragmentMap.get(key); fragmentTransaction.hide(fragment); } } public <T extends Fragment> T showFragment(Class<T> clazz) { return (T) createFragment(clazz); } }
调用代码:
FragmentFactory mFragmentFactory = FragmentFactory.getInstance().init(this, R.id.fl_content); mFragmentFactory.showFragment(MyFragment1.class); mFragmentFactory.showFragment(MyFragment2.class);
上面的封装用到了泛型、工厂、单例等知识。只需要在Activity初始化一次对象就可以一行代码管理Fragment了,想显示哪个页面就传入对应的Fragment的class。
简单通用的适配器
ListView是Android最常用的一个组件,优化Litsview那就是必不可少的工作了。
用Listview最痛苦的就是写BaseAdapter的getView()方法,一遍又一遍的写,大部分代码都是重复冗余,但又不得不写。下面来抽取冗余的代码封装起来。
public abstract class CommonAdapter<T> extends BaseAdapter { //需要显示的数据,List中的类型为泛型,因为不知道用户的封装Bean private List<T> mDatas; private Context mContext; //布局文件Id private int mLayoutId; public CommonAdapter(Context context,List<T> data,int layoutId) { mDatas = data; mContext = context; mLayoutId = layoutId; } @Override public int getCount() { return mDatas.size(); } @Override public Object getItem(int position) { return mDatas.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = ViewHolder.getHolder(mContext,convertView, parent, mLayoutId); setDatas(holder,getItem(position)); return holder.getConvertView(); } /** * 为各个item中的控件设置数据 * @param holder ViewHolder * @param object 从集合中所取的一个对象 */ public abstract void setDatas(ViewHolder holder, Object object); }
public class ViewHolder { private View mConvertView; //用来存布局中的各个组件,以键值对形式 private HashMap<Integer,View> mViews = new HashMap<>(); //ViewHolder构造函数,只有当convertView为空的时候才创建 public ViewHolder(Context context,View convertView, ViewGroup parent, int layouId) { convertView = LayoutInflater.from(context).inflate(layouId,parent,false); convertView.setTag(this); //将其setTag() mConvertView = convertView; } //返回一个ViewHolder对象 public static ViewHolder getHolder(Context context, View convertView, ViewGroup parent, int layoutId) { if (convertView == null) { return new ViewHolder(context,convertView,parent,layoutId); }else { return (ViewHolder) convertView.getTag(); } } //返回一个View的子类对象,因为不确定用户布局有什么组件,相当于findViewById //这里返回一个泛型,也可以返回一个View或Object public <T extends View>T getView(int resId) { View view = mViews.get(resId); //从集合中取出这个组件 if (view == null) { //如果为空,说明为第一屏 view = mConvertView.findViewById(resId); //从convertView中找 mViews.put(resId,view); } return (T) view; } public View getConvertView() { return mConvertView; } }
调用代码:
public class MyAdapter extends CommonAdapter<Bean> { public MyAdapter(Context context, List<Bean> data, int layoutId) { super(context, data, layoutId); } @Override public void setDatas(ViewHolder holder, Object object) { Bean bean = (Bean) object; ((TextView)holder.getView(R.id.title_Tv)).setText(bean.getTitle()); ((TextView)holder.getView(R.id.desc_Tv)).setText(bean.getDesc()); ((TextView)holder.getView(R.id.time_Tv)).setText(bean.getTime()); ((TextView)holder.getView(R.id.phone_Tv)).setText(bean.getPhone()); } }
List<Bean> data=new ArrayList<>(); Bean bean=new Bean("标题1", "内容1", "时间1", "18300000000"); Bean bean2=new Bean("标题2", "内容2", "时间2", "18300000000"); data.add(bean); data.add(bean2); listView.setAdapter(new MyAdapter(context, data, R.layout.listview_item));
注释写的很清楚了,就不多说了。
自定义组合控,布局模块化
正常的项目开发中肯定有很多布局冗余例如下面图红框中的设置和导航。
很多人会把这些布局文件一遍又一遍的复制,只修改其中的ID、字符串等,其他部分几乎一模一样,造成布局文件代码特别多。
最要命的不是这个,而且把所有的逻辑写在Activity\Fragment里,造成Activity\Fragment特别的庞大,真正实现一坨X代码。
我觉得应该把公用的布局单独抽取出来到一个xml里,再用一个GroupView去处理这些逻辑和业务,减少activity\Fragment的负担。
代码就不贴了,自己去源码demo里查看ParamSwitchView,这个View是图1的一个Item,封装了布局和所需要的遥控按键左右切换数据的逻辑。
面向接口编程
面向接口编程的意思是指在面向对象的系统中所有的类或者模块之间的交互是由接口完成的。
父类的引用指向子类对象,指向不同的子类对象,产生不同的行为:
父 a =new 子A;
有很多童靴在项目开发中经常更变业务,例如:定制化系统应用,底层的接口在不同型号的TV\手机上都有可能不一样。
这时候把这些底层接口单独封装在一个类进行管理,在平台发生改变的时候只需要改变实现。
定义接口类统一化管理方法
public interface IManager { void setBackLight(int value); void setPictureMode(int mode); }
实现类 1
public class HuaWeiManager implements IManager { @Override public void setBackLight(int value) { <strong>HuaWei</strong>.savaBackLight(value); } @Override public void setPictureMode(int mode) { <strong>HuaWei</strong>.setPictureMode(mode); } }
假如现在业务需求是华为的定制系统,只需要调用华为的子类
IManager iManager=new HuaWeiManager(); iManager.setBackLight(100);
如果业务需求转变成小米,那么只需要创建一个类进行实现
实现类 2
public class XiaoMiManager implements IManager { @Override public void setBackLight(int value) { XiaoMi.savaBackLight(value); } @Override public void setPictureMode(int mode) { XiaoMi.setPictureMode(mode); } }
调用代码里只需要把HuaWeiManager改成XiaoMiManager就能适配其他机型了。
//IManager iManager=new HuaWeiManager(); IManager iManager=new XiaoMiManager(); iManager.setBackLight(100);
在这里只是灌输一个编码思维,实际开发中突发情况比较多,并不一定全部适用。
在编码之前一定要花一点点时间简单构思和组织一下代码,不要想到什么写什么。
注重工具类的封装
我们正常的开发中经常用到很多不需要在逻辑层编写的方法,我们就可以单独的把他抽取出来放在单独的类里面去单独管理。
例如:Toast 、SharePreference、获取时间、系统版本、网络、MD5等等。。。。
这些东西都可以单独的封装和管理,减少逻辑层的代码,并且也可以让其他逻辑层调用。
坏习惯
有些人喜欢把定义个Tools这样的工具类,里面存放着所有的工具方法。
1. 网络、Toast、状态、时间等等全部都用一个类去管理,这样造成的后果就是后期不方便维护和不利于更新,代码看起来杂乱无章。
2. 把一些公共的方法直接在逻辑层构建,其他地方需要就直接复制一份过去。
或者有其他相同的比较类似的方法没有进行封装,在其他地方直接复制过去只修改其他一行的代码。
好习惯
1. 把这些tools单独创建各种各样的tools去存放这些方法,Toast只存Toast相关的,网络只存网络相关的,避免交杂在一起。也符合设计原则之一的:单一原则。
2. 类似的方法独立抽取出来,用传参flag标记去区分应用场景。
源码里收藏了一些常用的工具类分享给大家。
MVP分层架构
去年写了一篇关于它的文章,大家可以看看。能够让代码变得异常的清晰。
https://www.jb51.net/article/98422.htm
改善代码的方式很多很多,一下子想不完,后面想到了什么再继续更新分享。