DRY原则:Don't Repeat Yourself (摘自wikipedia)
OOA和OOD的作用及其区别 http://blog.sina.com.cn/s/blog_72ed42d401015y5c.html
站在为程序员提供第3方服务SDK的高度来写代码
1.抽象方法和非抽象方法
如果在类中定义了抽象方法,就是强制非抽象子类去实现。这样写的好处就是可以提醒子类需要复
写哪些方法,但是抽象方法不易过多,不需要强制实现的方法不要抽象化。
如果在类中定义了非抽象方法,可以给一个默认的实现,子类可以选择不复写,采用父类默认的实
现,也可以复写自定义实现。
应用场景:
抽取了一个带ListView的BaseListFragment,后来又有需求,需要一个带头的ListView的
Fragment,那么,就没有必要再写一个类继承BaseListFragement了,况且继承了ListView的初始化已经
完成了,没法再加一个头。
所以直接在BaseListFragment里定义一个方法返回头视图,默认将方法返回值至为null,在listView
初始化的时候,将方法返回值添加到listview的头部,不为空就添加到头部。
2.关于抽象的方法在哪儿被调的问题
既然抽了方法,肯定在父类里的某个地方要让其执行。
如果父类有自己的生命周期方法,会自动执行一些方法,在这些方法里可以抽出方法。
如果父类没有自己的生命周期方法,那么调用抽取的方法的父类方法要记得手动去调用。
3.关于怎么抽取的问题
情景1:抽象适配器,适配器的的抽象方法调用适配器所在父类的方法,父类的这个方法再抽象让
子类去实现。如我的开源中国带indicator的框架就是这么抽取的。
这种方式就是实现了方法的抽象和转移,将内部的抽象转移成父类的抽象。
4.关于电脑模型
其实编程和现实是息息相关的,如电脑和各个零部件就是抽取的具体体现,各个零部件相互配合,但
是又没有焊死在一起,这就是降低了耦合度。
程序的框架包括界面框架、网络加载框架,框架在抽取的时候应该降低它们之间的依赖性。
如果要写出资深的重构代码,必需要“精通”以下的知识:
0.封装
封装就是一个类A调用另外的一个类C时,不会直接调用,间接调用B,再用B去调用C。
比如在程序里通过要开启很多个对象,如service,广播等。如果直接开启会造成很大的耦合。
实例:模块加载器
1)定义加载器接口:
public interface IDxTestInstanceLoader extends IAreaModuleLifeMethods{ void load(); void unload(); void attach(Context context); }
2)实现加载器接口,创建实例。
比如实现load接口,可以开启服务,也可以开启广播,也可以开启线程。
3)定义加载器管理者
DxTestInstanceManager IAreaModuleLifeMethods{ Context List= ArrayList<>(Arrays.(IDxTestInstanceLoader[]{ TrafficStatTestInstanceLoader()ShareScreenLoader() }))DxTestInstanceManager = DxTestInstanceManager()(){} DxTestInstanceManager (){ } (IDxTestInstanceLoader dxTestInstanceLoader){ .add(dxTestInstanceLoader)} (){ (IDxTestInstanceLoader item : ){ item.attach()item.load()} } (){ (IDxTestInstanceLoader item : ){ item.unload()} } TrafficStatTestInstanceLoader (){ (.size() > ){ (TrafficStatTestInstanceLoader) .get()} } (Context context) { .= context} () { IDxTestInstanceLoader iDxTestInstanceLoader = .get()(iDxTestInstanceLoader != ){ iDxTestInstanceLoader.onResetSettingFiles()} } () { IDxTestInstanceLoader iDxTestInstanceLoader = .get()(iDxTestInstanceLoader != ){ iDxTestInstanceLoader.onDeviceServiceDestroyed()} } () { IDxTestInstanceLoader iDxTestInstanceLoader = .get()(iDxTestInstanceLoader != ){ iDxTestInstanceLoader.onDeviceUpdateStepPassed()} } }
1.接口
情景1:对几个列表按时间字段进行排序,但是几个列表的实体类之间没有任何的继承关系,得到
时间的方法也不一样,所以对于每一个列表都要定义一个比较器。
我在想,同样比较的是时间,为什么要定义3个比较器呢?
于是,我定义了一个接口:
public interface DateInterface {
String getDate();
}
让每个列表的实体类都去实现这个接口:
public class BidRecordSQL extends Entity implements DateInterface{
//...
@Override
public String getDate() {
return investTime;
}
}
public class BaseDepositsHistoryDomain extends Entity implements DateInterface{
//...
@Override
public String getDate() {
return investTime;
}
}
然后定义一个时间比较器:
/**
* 时间比较器-降序排序
* Created by Zhang on 2016/2/15.
*/
public class DescendingDateComparator implements Comparator {
@Override
public int compare(DateInterface lhs, DateInterface rhs) {
return -1 * lhs.getDate().compareTo(rhs.getDate());
}
}
然后,使用Collections工具类对List进行排序:
使用接口,本质的作用是让原本看似不相干的实体类之间产生了关系。也就是定义相同的行为,getDate, 然后比较器针对接口编程,而不是某个具体的类。
情景2:ViewPager装了多个Fragment,只想在viewpager滑动到哪一页,就更新哪一页的数据。
一般人的做法就是在每个Fragment定义一个加载数据的方法,然后在onPageChange方法里根据
position得到对应的Fragment,然后调用fragment的对应方法。
如果让每一个Fragment实现一个懒加载数据的接口,那么在onPageChange就不需要根据posit ion去if-else了,直接将fragment强转成接口,调用接口的方法即可。
定义接口,方便明确业务,而且如果更换方法名,所有实现了接口的类都会自动更换,避免了手动更换的麻烦。
2.反射
情景1:字段过滤器
public interface IPropertyFilter {
boolean apply(Object object, String name, Object value);
}
public interface IGetFieldMap {
Map getFieldMap(); //所有字段名,拼出map。
Map getFieldMap(String ...fieldNames); //根据字段名,拼出map。
Map getFieldMapExcept(String ...exceptFieldNames); //除了指定的几个字段名,拼出map。
Map getFieldMap(List fieldNames); //根据字段名,拼出map。
Map getFieldMap(IPropertyFilter propertyFilter); //根据过滤器,拼出map。
}
public class BaseRequestBean implements IGetFieldMap {
@Override
public Map getFieldMap() {
return getFieldMap(new IPropertyFilter() {
@Override
public boolean apply(Object object, String name, Object value) {
return true;
}
});
}
@Override
public Map getFieldMap(String... fieldNames) {
return getFieldMap(Arrays.asList(fieldNames));
}
@Override
public Map getFieldMapExcept(final String... exceptFieldNames) {
return getFieldMap(new IPropertyFilter() {
@Override
public boolean apply(Object object, String name, Object value) {
for (String item : exceptFieldNames){
if(name.equals(item)){
return false;
}
}
return true;
}
});
}
@Override
public Map getFieldMap(List fieldNames) {
Map result = new HashMap();
Class mClass = getClass();
Field[] declaredFields = mClass.getDeclaredFields();
for (Field field : declaredFields) {
String fieldName = field.getName();
if (!field.isAccessible()) {
field.setAccessible(true);
}
try {
Object fieldValue = field.get(this);
if (fieldValue == null) continue;
if (!fieldNames.conta×××(fieldName)) continue;
result.put(fieldName, fieldValue);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return result;
}
@Override
public Map getFieldMap(IPropertyFilter propertyFilter) {
Map result = new HashMap();
Class mClass = getClass();
Field[] declaredFields = mClass.getDeclaredFields();
for (Field field : declaredFields) {
String fieldName = field.getName();
if (!field.isAccessible()) {
field.setAccessible(true);
}
try {
Object fieldValue = field.get(this);
if (!propertyFilter.apply(this, fieldName, fieldValue)) continue;
result.put(fieldName, fieldValue);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return result;
}
}
情景2:打印Build类里所有的字段
https://blog.csdn.net/xiaoxian8023/article/details/24109185
情景3:引用hide类并调用其方法
https://blog.csdn.net/pshiping2014/article/details/79549680
linux工程师说将一个key存储到build里了,但是查看Build类发现可以通过SystemProperties这个类来获取,但是这个类是hide类,所以只能用反射去拿。
3.注解
4.泛型
Java泛型详解
http://blog.csdn.net/jinuxwu/article/details/6771121
获取泛型的Class
http://www.cnblogs.com/onlysun/p/4539472.html
4-5.枚举(优化代码可读性)
http://blog.csdn.net/lmj623565791/article/details/79278864
5.自定义
自定义View或者类有多种形式:
1)完全自定义(复写onDraw 、onLayout)
2)组合自定义
3)包裹自定义(在自定义属性绑定需要操作的子View的id,在onFinishInflate方法里find处理相关的逻辑,Google的很多框架级的原生组件用的就是这个)
如DrawerLayout,抽屉控件等。
4)工具类中封装view,利用已有的view实现统一的业务接口。
5)复写Android自定义的类(要求研究源码,然后才能随心所欲的复写哈。)
实际场景1:使用ArrayAdapter这个类填充Spinner,如果ArrayAdapter的泛型是一个对象的话,最终Spinner显示的是对象的哈希值。而我真正想展示在
Spinner上的只是ArrayAdapter泛型的某个字段而已。
最笨的解决方法就是遍历List
里往往又会用到List
于是研究了一下ArrayAdapter这个类的源码,看看它的view是如何生成的。
public View getView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(mInflater, position, convertView, parent, mResource);
}
private View createViewFromResource(LayoutInflater inflater, int position, View convertView,
ViewGroup parent, int resource) {
View view;
TextView text;
if (convertView == null) {
view = inflater.inflate(resource, parent, false);
} else {
view = convertView;
}
try {
if (mFieldId == 0) {
// If no custom field is assigned, assume the whole resource is a TextView
text = (TextView) view;
} else {
// Otherwise, find the TextView field within the layout
text = (TextView) view.findViewById(mFieldId);
}
} catch (ClassCastException e) {
Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
throw new IllegalStateException(
"ArrayAdapter requires the resource ID to be a TextView", e);
}
T item = getItem(position);
if (item ×××tanceof CharSequence) {
text.setText((CharSequence)item);
} else {
text.setText(item.toString());
}
return view;
}
通过源码发现,如果ArrayAdapter的泛型是字符串,那么spinner展示的是字符串;如果ArrayAdapter的泛型是一个对象的话,返回的是这个对象的toString方法的返回值。
解决方案1:复写ArrayAdapter的getView的相关方法。
此解决方案的核心是将ArrayAdapter展示Spinner内容部分的具体代码抽象化成方法,从而使ArrayAdapter亦抽象化。
但是此方式有一个弊端:每有一个泛型类,就得新建一个对应的Adapter类,太浪费资源。
/**
* Created by 陈章 on 2017/12/19.
* 适配器
*/
public abstract class CZArrayAdapter extends ArrayAdapter{
public CZArrayAdapter(Context context, List objects) {
super(context, android.R.layout.simple_spinner_item, objects);
}
@NonNull
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(LayoutInflater.from(getContext()), position, convertView, parent);
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(LayoutInflater.from(getContext()), position, convertView, parent);
}
protected abstract String getText(T t);
private View createViewFromResource(LayoutInflater inflater, int position, View convertView,
ViewGroup parent) {
T item = (T) getItem(position);
View view;
TextView text;
if (convertView == null) {
view = inflater.inflate(android.R.layout.simple_spinner_item, parent, false);
} else {
view = convertView;
}
text = (TextView) view;
text.setText(getText(item));
return view;
}
}
解决方案2:复写ArrayAdapter的getView的泛型类的toString方法
复写泛型类的toString方法,返回想在spinner上展示的字段。如果泛型类的toString方法没有在其它地方有特殊的引用,这种解决方法是最快最简单的。
6.其它,肯定有,尚未总结。
怎么解耦的问题:
不解耦的弊端,比如我之前将有关android源码的探索全部全部放到一篇文章里,后来博客系统出现了点问题。差点导致那篇博客损毁。所以不解耦很明显有2个缺点:
1)一损俱损:一个地方出现问题,会导致另外一个地方也出现问题。
2)查阅不方便:将所有的内容写到一篇博客,就像将所有的代码写在一个类中。这样这个类看起来就比较麻烦。
1.解耦
1)View的解耦:一般如详情页面、带有Banner图的页面,里面的View可能会有很多。可以将
大的View细分为小的View。
public abstract class BasePart {
/**
* 获取当前模块的View对象
* @return
*/
public abstract View getView();
/**
* 处理逻辑和数据
* @param t
*/
public abstract void setData(T t);
/**
* startActivity with bundle
*
* @param clazz
* @param bundle
*/
protected void readyGo(Class> clazz, Bundle bundle) {
Intent intent = new Intent(CommonHelper.context(), clazz);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag.
if (null != bundle) {
intent.putExtras(bundle);
}
CommonHelper.context().startActivity(intent);
}
}
只要在activity里initview()的时候,new PartView(),将子View添加到Activity的ContainerView中
,请求到数据之后再partView.setData();
但是PartView是一个很简单的类,如果需要Activity的参数(如调用Activity的方法等),可以在构造函数里传入Activity.
对于别人重构的框架,应该如何去用的问题:
1.抽象类,肯定是要实现它示实现的方法。写逻辑就是对抽象方法的具体实现。
将一些公共的libs和基类放在依赖库中
1)出现的问题:将工程的layout下的布局文件写到依赖库里,那么相关的资源color、style看着是
明明定义了,但就是can't resolve.
AndroidStudio里所有项目的公共Library库,不要单独复制,要共有,防止以后要修改。
https://blog.csdn.net/codezjx/article/details/49531887
注意:如果ModuleA依赖了ModuleB,ModuleB有依赖了ModuleC,那么ModuleA的gradle里也要依赖moduleC,否则会报的ModuleB里找不到ModuleC的错误。
上层的Module如果依赖了下层的Module,上层的module要依赖下层所依赖的所有module。!!!
如果工程有多个module,一定要从子module找起,单独打开某个module集合目录 ,排除依赖没有写的情况 。!!!!!!!!!!!
module里不能有application结点,所以module里的service、receiver这些结点也只能挪到app里定义。
aar的引用
https://blog.csdn.net/u013440413/article/details/78685192 (app里使用aar)
https://blog.csdn.net/lin_dianwei/article/details/79532078 (module里使用aar)
注意build.gradle里使用implecation compile api依赖的区别
AndroidStudio 多层级 Module 对 aar 引用问题
https://www.cnblogs.com/bellkosmos/p/6146349.html
关于多重依赖清单文件重复的问题:
https://www.cnblogs.com/bluestorm/p/6692789.html
关于依赖多个module,可能有一个致命的问题。各个module及app的compileSdk的版本不一致,可能会导致以下问题:
Error:Execution failed for task ':app:transformDexArchiveWithExternalLibsDexMergerForDebug'.
> java.lang.RuntimeException: java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Unable to merge dex
上面这个错误并非是jar包重复,可能是各个module及app的compileSdk的版本不一致导致的。
多层module依赖,打jar包的问题。
1)moduleA依赖moduleB,moduleC依赖moduleA,如果将moduleA,moduleB打成jar包,给moduleC引用,回报重复类的错误:multidex class。。。
api (project(':moduleA')) { //解决重复依赖问题 exclude module: 'moduleB' }
这样即可解决问题。
接着,如果将moduleC再生成一个jar包,moduleC.jar。引用moduleC.jar,类调用正常,但是一运行就会报错:
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.cz.basetool.ui_work.thread.ThreadManager" on path: DexPathList[[zip file "/data/app/com.example.jl.jiangxihfscj-2/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at com.juli.basescjmodule.libs.hardware_libary.a.initSystemConfig(SystemUtil.java:45)?
at com.juli.basescjmodule.libs.hardware_libary.a.a(SystemUtil.java:39)?
at com.juli.basescjmodule.libs.base_tool.hardware_service.obu.action.system.SystemActionController.init(SystemActionController.java:38)?
at com.juli.basescjmodule.libs.base_tool.hardware_service.obu.action.system.SystemActionController.init(SystemActionController.java:32)?
at com.juli.basescjmodule.libs.basesdk.JLBaseActionContoller.initSDK(JLBaseActionContoller.java:83)?
at com.example.jiangxisdklibrary.JLScjContoller.initSDK(JLScjContoller.java:30)?
at com.example.jl.jiangxihfscj.ProApplication.onCreate(ProApplication.java:24)?
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1012)?
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4553)?
at android.app.ActivityThread.access$1500(ActivityThread.java:151)?
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1364)?
at android.os.Handler.dispatchMessage(Handler.java:102)?
at android.os.Looper.loop(Looper.java:135)?
at android.app.ActivityThread.main(ActivityThread.java:5254)?
at java.lang.reflect.Method.invoke(Native Method)?
at java.lang.reflect.Method.invoke(Method.java:372)?
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:902)?
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:697)?
Suppressed: java.lang.ClassNotFoundException: com.cz.basetool.ui_work.thread.ThreadManager
at java.lang.Class.classForName(Native Method)
at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
... 19 more
Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available
而且在moduleC.jar也找不到这个类,准确的说是找不到moduleA和moduleB的类。
》》如果moduleC中有UI,将moduleC打成jar包,还会报UI相关找不到的错误。
解决办法:https://www.jianshu.com/p/8f9cf6271c20
https://www.cnblogs.com/Jason-Jan/p/9192273.html
1.fat-aar.gradle:要打哪个module的jar包,就放到哪个module下面。
2.github上的这个库,有个前提就是gradle插件必须是2.3.3,所以我把
gradle要加上buildToolsVersion
gradle要将implecation换成compile
工程的gradle插件变为2.3.3
classpath 'com.android.tools.build:gradle:2.3.3'
properties的版本可以不用改,如果改成2.3.3还会报错:
https://www.jianshu.com/p/6a0bc5792860
默认aar的生成位置:
默认也会生成jar文件,但是引用后发现里面只有R文件:
那么,既然aar都能打出来,是不是改改fat-aar就能搞个fat-jar打个jar呢???????????????????????
还有一种方法:https://blog.csdn.net/anymyna/article/details/82054020 (将aar转成jar)
3.怎么混淆:https://www.jianshu.com/p/aa3e78a7a095 (关于混淆的配置,只需要在最上层的merge模块中配置即可)
4.release的aar不出来咋办:
执行cleanBuildCache然后执行assembleRelease
将module与主程序解耦
在module写了一些功能,但是功能需要主程序去辅助完成。这样就矛盾了,module又不能去依赖主程序,怎么调用主程序的东西呢?
解决办法 1:
在module里定义一个抽象类,module能实现的功能全部实现,实现不了定义成抽象方法,将主程序完成某功能需要的参数回调出去。再在主程序里注入实现:
主程序要干的事情:
IDeviceAction { (openOutputDataSender outputDataSender)(openOutputDataSender outputDataSender)(openOutputDataSender outputDataSender)(highVolumeOutputDataSender outputDataSender)(volumeValueOutputDataSender outputDataSender)(OutputDataSender outputDataSender)}
moudle里半实现的抽象类:
BlueToothActionReceiver BroadcastReceiver IDeviceAction{ String = String = String = (Context contextIntent intent) { (intent != && intent.getAction().equals()){ option = intent.getIntExtra(-)String state = intent.getStringExtra()(option){ DeviceActionOption.: setGPS(state.equals() ? : OutputDataSender.())DeviceActionOption.: setRecord(state.equals() ? : OutputDataSender.())DeviceActionOption.: setSta(state.equals() ? : OutputDataSender.())DeviceActionOption.: (state.substring()){ : setVolume(OutputDataSender.()): setVolume(OutputDataSender.()): String valume = state.substring()value = CodeTool.(valume)setVolume(valueOutputDataSender.())} DeviceActionOption.: getDeviceStatus(OutputDataSender.())} } } }
在module里发广播,这样主程序就能完成未完成的功能。
重构的实例场景
1)一个网络请求接口(传一个paperid得到试卷的题目)要在程序的多处调用,之前离职的程序员写
的代码也没有封装:
/**
* 从网络获取ListView的数据
*/
private void getListData(String p_id) {
if (!NetWorkStatusUtil.isNetworkAvailable(context)) {
ToastUtil.showShort(context, "请先链接网络");
return;
}
LoadingDialog.showProgress(this, "题目正在加载中,请稍等...", true);
String url = HttpConfig.QUESTION;
RequestParams params = new RequestParams();
// params.addBodyParameter("token", MD5Encoder.getMD5());
KLog.e("试卷页面p_id:" + p_id);
params.addQueryStringParameter("token", MD5Encoder.getMD5());
params.addQueryStringParameter("p_id", p_id);
//p_id=2是单选;
//http://app.haopeixun.org/index.php?g=apps&m=paper&a=getExams&token=75c824f486b5fa5b60330697bdb03842&p_id=8
//
HttpUtils httpUtils = new HttpUtils();
httpUtils.send(HttpMethod.GET, url, params, new RequestCallBack() {
@Override
public void onSuccess(ResponseInfo responseInfo) {
jsonString = responseInfo.result;
Log.i(TAG, "考题答案: ");
KLog.json(jsonString);
if (!TextUtils.isEmpty(jsonString)) {
JSONObject jsonObject;
try {
jsonObject = new JSONObject(jsonString);
int resultCode = Integer.parseInt(jsonObject.getString("result"));
KLog.e("结果码:" + resultCode);
switch (resultCode) {
case 0:
if (jsonString.isEmpty()) {
showEmptyView();
return;
}
parserData(jsonString);
break;
default:
showEmptyView();
}
} catch (JSONException e) {
showEmptyView();
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
LoadingDialog.dismissProgressDialog();
}
}
}
@Override
public void onFailure(HttpException arg0, String arg1) {
KLog.e(arg1);
LoadingDialog.dismissProgressDialog();
ToastUtil.showLong(context, "请求考题失败");
}
});
}
private void parserData(String jsonString) {
// ***********给user赋值******************
qustionItems.clear();
try {
JSONObject jsonObject = new JSONObject(jsonString);
JSONArray dataArray = jsonObject.getJSONArray("data");
totalScore = dataArray.length();
Log.i(TAG, "总共题目数量:" + totalScore);
for (int i = 0; i < dataArray.length(); i++) {
QuestionBean questionBean = new QuestionBean();
JSONObject jsonObject2 = dataArray.getJSONObject(i);
questionBean.addtime = jsonObject2.getString("addtime");
questionBean.e_analyze = jsonObject2.getString("e_analyze");
questionBean.e_answer_count = jsonObject2.getString("e_answer_count");
// *********
JSONObject jsonObject3 = jsonObject2.getJSONObject("e_answer");// 题选项对象
questionBean.eanswer = questionBean.new Eanswer();
questionBean.eanswer.setA(jsonObject3.getString("A"));
questionBean.eanswer.setB(jsonObject3.getString("B"));
String answerC = jsonObject3.optString("C");
if (answerC != null && !TextUtils.isEmpty(answerC)) {
questionBean.eanswer.setC(answerC);
}
String answerD = jsonObject3.optString("D");
if (answerD != null && !TextUtils.isEmpty(answerD)) {
questionBean.eanswer.setD(answerD);
}
switch (Integer.parseInt(questionBean.e_answer_count)) {
case 5:
questionBean.eanswer.setE(jsonObject3.getString("E"));
break;
case 6:
questionBean.eanswer.setE(jsonObject3.getString("E"));
questionBean.eanswer.setF(jsonObject3.getString("F"));
break;
case 7:
questionBean.eanswer.setE(jsonObject3.getString("E"));
questionBean.eanswer.setF(jsonObject3.getString("F"));
questionBean.eanswer.setG(jsonObject3.getString("G"));
break;
}
// **********
questionBean.e_classid = jsonObject2.getString("e_classid");
questionBean.e_exam_type = jsonObject2.getString("e_exam_type");
questionBean.e_fenzhi = jsonObject2.getString("e_fenzhi");
questionBean.e_good = jsonObject2.getString("e_good");
questionBean.e_id = jsonObject2.getString("e_id");
questionBean.e_probability = jsonObject2.getString("e_probability");
questionBean.e_result = jsonObject2.getString("e_result");
questionBean.e_status = jsonObject2.getString("e_status");
questionBean.e_title = jsonObject2.getString("e_title");
questionBean.e_typeid = jsonObject2.getString("e_typeid");
questionBean.examid = jsonObject2.getString("examid");
questionBean.id = jsonObject2.getString("id");
questionBean.paperid = jsonObject2.getString("paperid");
qustionItems.add(questionBean);
StringBuilder sb = new StringBuilder();
//sb.append("");
uAnswerList.add(sb);
}
} catch (JSONException e) {
e.printStackTrace();
} finally {
LoadingDialog.dismissProgressDialog();
}
showDataView();
KLog.e("集合大小" + qustionItems.size());
if (qustionItems.size() > 0) {
tv_total_questions.setText("1 / 共" + qustionItems.size() + "道题");
}
questionPagerAdapter.set(qustionItems, uAnswerList);
uAnswerStateAdapter.set(getAnswerCard());
}
同样的接口,我觉得没有必要再往新的activity复制一遍,于是我将这个接口单独封装成一个类
PaperListRequester中
在构造PaperListRequester这个类的时候,我将Context和接口需要的参数pid传递进去。定义了
一个监听器ParseListener,监听内部数据解析的结果。通过一个方法getData,并传递一个监听器返回结果。
这是调用PaperListRequester这个类请求接口的代码:
将请求、解析接口的逻辑封装在PaperListRequester这个类中,一下代码就看得顺眼了。这也是Google及一些开源库常用的工具类封装手法。
2)工程中有个ActivityA,作用是取一个数据,但是这个取数据的过程,与activity里的handler相关联。代码量也比较多。这时候有个ActivityB,需要和
ActivityA重复一样的工作,但是ActivityB本身类里逻辑比较复杂,如果直接把ActivityA里的代码粘贴过来,会严重影响这个类的结构。所以这里
就需要新建一个类C.java,用来封装ActivityA的逻辑,通过接口返回数据。在ActivityB里调用C,只管请求和取数据,中间的过程交由C.java去实现。
实现解耦。
6.避免过多使用if-else
如果一个项目中,一个变量因素会影响多个地方的行为或者动作,可以将这此动作定义成接口。然后根据变量定义不同的实例实现接口。
实战1:比如我的项目里,不同的设备串口号、其它的一些信息不一样
1-1:抽取接口
**
* Created by XinYi on 2018/8/7.
* 获取设备的一些信息,因设备硬件版本不同而不同,需要兼容。
* 1.移远(28)
* 2.旗文(06B)
*/
public interface IDeviceContent {
/**
* 升麦串口号
* @return
*/
String getSmSerialFileName();
/**
* OBD串口号
* @return
*/
String getObdSerialFileName();
/**
* SD卡挂载,扫描的根目录是否正确。
* @see SMCardManager.CardChecker#initFilter()
* @param fileName
* @return
*/
boolean isSmScardPath(String fileName);
}
1-2:根据不同的平台,实现接口,创建不同的实例:
平台1:
/**
* Created by XinYi on 2018/8/7.
* 旗文(06B) 平台
*/
public class QWDeviceContent implements IDeviceContent {
@Override
public String getSmSerialFileName() {
return "/dev/ttyMT2";
}
@Override
public String getObdSerialFileName() {
return "/dev/ttyM×××";
}
@Override
public boolean isSmScardPath(String fileName) {
return fileName.conta×××("usbotg");
}
}
平台2:
/**
* Created by XinYi on 2018/8/7.
* 移远(28)平台
*/
public class YYDeviceContent implements IDeviceContent {
@Override
public String getSmSerialFileName() {
return "/dev/ttyHSL0";
}
@Override
public String getObdSerialFileName() {
return "/dev/ttyHSL1";
}
@Override
public boolean isSmScardPath(String fileName) {
return (fileName.conta×××(Constants.SM_SDCARD_QW_PATH) && fileName.length() == 9);
}
}
1-3:指定一个实际使用的实例
private void fillDataFromBuilder(Builder builder) {
this.deviceType = builder.deviceType;
this.areaId = builder.areaId;
if(deviceType == DeviceType.TYPE_28){
deviceContent = new YYDeviceContent();
}else if(deviceType == DeviceType.TYPE_06B){
deviceContent = new QWDeviceContent();
}
}
这样就避免了每一个方法返回的值都需要根据平台用if-else判断一下,一旦增加一个平台,只需要创建一个平台的实例即可。
避免多个异步接口多层嵌套回调
实际场景:下面这此设置项,每一项都有一个单独的接口,都是异步回调。这样如果有10多层异步嵌套,将会造成代码阅读性非常差。
于是我想到了“封装”
#1.定义单项设置器的基类
/**
* Created by XinYi on 2018/12/20.
* 单个参数设置基类
*/
public abstract class BaseSingleRecordParamHandler {
protected String TAG = this.getClass().getSimpleName();
protected RecordParamsHandleController recordParamsSetController;
protected String param;
/**
* 设置参数
*/
abstract void handleSetParam();
/**
* 获取参数
*/
abstract void handleGetParam();
protected YiYunController.YiYunActionCallBack getYiYunActionCallBack(){
return new YiYunController.YiYunActionCallBack() {
@Override
public void onSuccess(Object obj) {
BaseSingleRecordParamHandler.this.onSetParamSuccess();
}
@Override
public void onFailure(Exception e) {
BaseSingleRecordParamHandler.this.onSetParamFailure(e);
}
};
}
protected BaseSingleRecordParamHandler getNextSingleRecordParamHandler(){
return recordParamsSetController.getNextSingleRecordParamHandler();
}
public void attachRecordParamsSetController(RecordParamsHandleController recordParamsSetController) {
this.recordParamsSetController = recordParamsSetController;
}
public BaseSingleRecordParamHandler setParam(String param) {
this.param = param;
return this;
}
protected void onSetParamSuccess(){
BaseSingleRecordParamHandler nextSingleRecordParamSetter = recordParamsSetController.getNextSingleRecordParamHandler();
if(nextSingleRecordParamSetter != null){
nextSingleRecordParamSetter.handleSetParam();
}else{
recordParamsSetController.getParamsSetResultCallBack().onSuccess();
}
}
protected void onSetParamFailure(Exception e){
recordParamsSetController.getParamsSetResultCallBack().onFailure(e);
}
protected void onGetParamSuccess(){
BaseSingleRecordParamHandler nextSingleRecordParamSetter = recordParamsSetController.getNextSingleRecordParamHandler();
if(nextSingleRecordParamSetter != null){
nextSingleRecordParamSetter.handleSetParam();
}else{
recordParamsSetController.getParamsGetResultCallBack().onSuccess(recordParamsSetController.getCollectedParams());
}
}
protected void onGetParamFailure(Exception e){
recordParamsSetController.getParamsGetResultCallBack().onFailure(e);
}
}
#2.定义设置控制器
控制器的好处:
1)通过调整设置器的add顺序,可以轻松调整单项设置的先后顺序。(想想如果用的是多层嵌套,那将是一场灾难。)
2)避免了多层嵌套回调
3)隔离了用户与设置器
/**
* Created by XinYi on 2018/12/20.
* 记录仪参数设置控制器
*/
public class RecordParamsHandleController {
private static final String TAG = "RecordParamsHandleContr";
private int nextSetterIndex = 0; //标记当前处理者的索引
private List singleRecordParamSetterList = new ArrayList<>();
private static final RecordParamsHandleController ourInstance = new RecordParamsHandleController();
private ParamsSetResultCallBack paramsSetResultCallBack;
private ParamsGetResultCallBack paramsGetResultCallBack;
private String collectedParams = "";
public static RecordParamsHandleController getInstance() {
return ourInstance;
}
private RecordParamsHandleController() {
}
/**
*
* @param param 0:参数获取或者设置标志 1:正式参数
*/
public void init(String param[]){
nextSetterIndex = 0;
collectedParams = "";
//注意下面的add是有顺序的
singleRecordParamSetterList.clear();
singleRecordParamSetterList.add(RecordResolutionParamHandler.getInstance(this));
singleRecordParamSetterList.add(RecordMicParamHandler.getInstance(this));
singleRecordParamSetterList.add(RecordLoopParamHandler.getInstance(this));
singleRecordParamSetterList.add(RecordGsensorParamHandler.getInstance(this));
singleRecordParamSetterList.add(AMapParamHandler.getInstance(this));
singleRecordParamSetterList.add(ScreenIntensityParamHandler.getInstance(this));
singleRecordParamSetterList.add(RecordDurationParamHandler.getInstance(this));
singleRecordParamSetterList.add(ScreenSleepParamHandler.getInstance(this));
singleRecordParamSetterList.add(ResetTFCardParamHandler.getInstance(this));
singleRecordParamSetterList.add(ResetSystemParamHandler.getInstance(this));
singleRecordParamSetterList.add(RecordOsdParamHandler.getInstance(this));
LogController.d(TAG, "init: len = " + param.length);
//设置参数
if(param.length > 1){
for (int i = 2; i < param.length; i++) {
LogController.d(TAG, "init: i = " + i);
singleRecordParamSetterList.get(i-2).setParam(param[i]);
}
}
}
/**
* 开始设置参数
* @param paramsSetResultCallBack
*/
public void startSetParams(ParamsSetResultCallBack paramsSetResultCallBack){
this.paramsSetResultCallBack = paramsSetResultCallBack;
singleRecordParamSetterList.get(0).handleSetParam();
}
/**
* 开始获取参数
* @param paramsGetResultCallBack
*/
public void startGetParams(ParamsGetResultCallBack paramsGetResultCallBack){
this.paramsGetResultCallBack = paramsGetResultCallBack;
singleRecordParamSetterList.get(0).handleGetParam();
}
protected BaseSingleRecordParamHandler getNextSingleRecordParamHandler(){
nextSetterIndex++;
if(nextSetterIndex + 1 >singleRecordParamSetterList.size()){
return null;
}
return singleRecordParamSetterList.get(nextSetterIndex);
}
public interface ParamsSetResultCallBack{
void onSuccess();
void onFailure(Exception e);
}
public interface ParamsGetResultCallBack{
void onSuccess(String param);
void onFailure(Exception e);
}
public ParamsSetResultCallBack getParamsSetResultCallBack() {
return paramsSetResultCallBack;
}
public ParamsGetResultCallBack getParamsGetResultCallBack() {
return paramsGetResultCallBack;
}
public void collectParams(String param){
collectedParams += param;
}
public String getCollectedParams() {
return collectedParams;
}
}