版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】关于重构的一些思想
DRY原则:Don'tRepeatYourself(摘自wikipedia)OOA和OOD的作用及其区别
/s/blog_72ed42d401015y5c.html站在为程序员提供第3方服务SDK的高度来写代码1.抽象方法和非抽象方法如果在类中定义了抽象方法,就是强制非抽象子类去实现。这样写的好处就是可以提醒子类需要复写哪些方法,但是抽象方法不易过多,不需要强制实现的方法不要抽象化。如果在类中定义了非抽象方法,可以给一个默认的实现,子类可以选择不复写,采用父类默认的实现,也可以复写自定义实现。应用场景:
抽取了一个带ListView的BaseListFragment,后来又有需求,需要一个带头的ListView的Fragment,那么,就没有必要再写一个类继承BaseListFragement了,况且继承了ListView的初始化已经完成了,没法再加一个头。
所以直接在BaseListFragment里定义一个方法返回头视图,默认将方法返回值至为null,在listView初始化的时候,将方法返回值添加到listview的头部,不为空就添加到头部。2.关于抽象的方法在哪儿被调的问题
既然抽了方法,肯定在父类里的某个地方要让其执行。
如果父类有自己的生命周期方法,会自动执行一些方法,在这些方法里可以抽出方法。
如果父类没有自己的生命周期方法,那么调用抽取的方法的父类方法要记得手动去调用。3.关于怎么抽取的问题
情景1:抽象适配器,适配器的的抽象方法调用适配器所在父类的方法,父类的这个方法再抽象让
子类去实现。如我的开源中国带indicator的框架就是这么抽取的。
这种方式就是实现了方法的抽象和转移,将内部的抽象转移成父类的抽象。4.关于电脑模型
其实编程和现实是息息相关的,如电脑和各个零部件就是抽取的具体体现,各个零部件相互配合,但
是又没有焊死在一起,这就是降低了耦合度。
程序的框架包括界面框架、网络加载框架,框架在抽取的时候应该降低它们之间的依赖性。
如果要写出资深的重构代码,必需要“精通”以下的知识:
0.继承与多态
静态与多态无关
public
class
ClassA
{
private
static
final
String
TAG
=
"ClassA";
public
static
void
test(){
Log.e(TAG,
"test:
ClassA");
}
}public
class
ClassB
extends
ClassA
{
private
static
final
String
TAG
=
"ClassB";
public
static
void
test(){
Log.e(TAG,
"test:
ClassB");
}
}
调用ClassB
classA
=
new
ClassB();
classA.test();
输出:test:ClassB
调用ClassA
classA
=
new
ClassB();
classA.test();
输出:test:ClassA
**上面ClassA与ClassB同时存在test()方法,可见静态方法不受继承的影响,声明的类是什么哪个类,就调用哪个类的静态方法。
**因此,如果想要一个类的方法可以被外部访问,但是又不想被子类复写,那么只有publicstatic了。
0.封装
封装就是一个类A调用另外的一个类C时,不会直接调用,间接调用B,再用B去调用C。
比如在程序里通过要开启很多个对象,如service,广播等。如果直接开启会造成很大的耦合。封装要注意的地方1)避免画蛇添足的封装2)只暴露上层需要的接口3)对下层的异常回调要全面,不要有任何一点遗漏。4)不要过早的重构、封装
实例:模块加载器
1)定义加载器接口:
public
interface
IDxTestInstanceLoader
extends
IAreaModuleLifeMethods{
void
load();
void
unload();
void
attach(Context
context);
}
2)实现加载器接口,创建实例。
比如实现load接口,可以开启服务,也可以开启广播,也可以开启线程。
3)定义加载器管理者
DxTestInstanceManager
IAreaModuleLifeMethods{
Context
List<IDxTestInstanceLoader>
=
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()}
}
}
巧用runnable
有时候执行某些方法,必须有一些前提条件,没有必要再每个要执行的方法里单独写执行条件的判断。可以封装一个方法:
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<DateInterface>
{
@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强转成接口,调用接口的方法即可。
定义接口,有哪些好处:
1)方便明确业务
2)如果更换方法名,所有实现了接口的类都会自动更换,避免了手动更换的麻烦。
3)方便其他模块调用,其他模块只关心接口的方法就行,不需要关注接口实现类的其他方法。
2.反射
反射需要注意的地方:反射的类在某个版本可能更新了,之前的版本可能没有某个方法,反射就会报NoSuchMethod异常。给Java类动态的增加属性最近在做一个项目的时候,用到JSON解析,会创建很多的实体,但这些实体一般只有一个属性,我在想,有没有一种技术只创建一个空的类,然后动态的改变它的属性呢?
后来百度了一下,确实存在这样的技术,Cglib。
/WUWENJINWUWENJIN/article/details/83276553
/didi7696/article/details/82351167
/zghwaicsdn/article/details/50957474/
/li951418089/article/details/50392727
/1772.html
/zxf330301/p/5798241.html
/article/182201.htm
但是无赖的是这个技术在纯Java代码里可以正常执行(随便定义一个类,一个main方法,类无任何的继承。),但是在Activity里使用的话,就会报一个错误。要搞懂这个错误就得真正理解ClassLoader等类加载机制的原理。
情景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<String>
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<String>
fieldNames)
{
Map<String,
Object>
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<String,
Object>
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类里所有的字段
/xiaoxian8023/article/details/24109185
情景3:引用hide类并调用其方法
/pshiping2014/article/details/79549680
linux工程师说将一个key存储到build里了,但是查看Build类发现可以通过SystemProperties这个类来获取,但是这个类是hide类,所以只能用反射去拿。
3.注解
4.泛型
Java泛型详解
/jinuxwu/article/details/6771121
获取泛型的Class
/onlysun/p/4539472.html
4-5.枚举(优化代码可读性)
/lmj623565791/article/details/79278864
枚举如何定义构造参数?
注意最后一个枚举要加;然后按照正常的类写语法就行了。public
enum
BleAreaType
{
HENAN("河南"),
//河南地区
GUIZHOU("贵州"),
//贵州地区
GUANGXI("广西");
//广西地区
private
String
areaChineseName;
BleAreaType(String
areaChineseName){
this.areaChineseName
=
areaChineseName;
}
public
String
getAreaChineseName()
{
return
areaChineseName;
}
}
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<T>,得到想要展示的字符串集合List<String>,再将List<String>设置给适配器。但是这样一来的话,在spinner里点击事件
里往往又会用到List<T>,这样就容易造成混乱。
于是研究了一下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<T>
extends
ArrayAdapter{
public
CZArrayAdapter(Context
context,
List<T>
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.配置
对于一些比较固定的配置,不要使用修改代码的方式,而是通过编辑配置文件、读配置文件的方式。这样更加的安全。6.拦截思想
我们可以把一个正常的流程想象成一根线,有时想改变这根线的某一个点的执行流程,程序可能向后或者不向后执行。我们需要做的就是定义拦截器。
示例1:设备按下一个物理按键,通用程序会执行一个功能1,但是某一个地区可能需要执行功能2.
一般人的想法就是,直接在通用程序里通过if-else来判断地区。将地区的逻辑,写在通用的代码里,这就是一种耦合。假如后续要增加地区,又要实现不同的功能,那这块的代码就会呈现爆炸式的增长。
正确的做法就是:定义拦截器,地区设置了拦截器,就将数据丢给拦截器处理,各地区的功能代码写在各地区的模块里。这样就优美的将地区和通用程序的代码解耦了
...
case
1://
升迈消息处理
MessageBean
messageBean
=
(MessageBean)
msg.obj;
QualityChecker.getInstance(SocketService.this,virtualSMSerialPort).onSMCallBack(messageBean);
AndroidConsoleLogPrinter.e("升迈消息处理
"
,"cmd
=
"
+
Integer.parseInt(messageBean.getCMD(),
16));
//回调升迈消息给地区子模块
boolean
intercept
=
false;
if(smMessageReceiver
!=
null){
intercept
=
smMessageReceiver.dispatchSmIntent(messageBean);
}
AndroidConsoleLogPrinter.e("cmd
=
"
+
messageBean.getCMD()
,
"intercept:
"
+
intercept);
if(intercept)
return;
//子地区拦截复写了对应的指令,基本模块不再执行。
switch
(Integer.parseInt(messageBean.getCMD(),
16))
{
case
0x1A://
OBU通道命令
...
示例2:通用程序需要给单片机定时发送一个心跳数据,单片机指示灯好显示各个状态的情况。但是某一个地区没有电子狗,心跳的部分数据还不一样,指示电子狗状态的netDogState字段,需要替换成另外一个应用的状态。
正确做法:定义拦截器,拦截心跳数据,替换部分心跳数据,返回新的心跳数据。
...
//由于不同的地区,回复的心跳可能不太一样。需要让子区模块进行拦截
String
heartData
=
gps+","
+
Latitude+
","
+
Longitude+","
+
sim+","
+
wifiState+","
+
netDogState+","
+
isNetwork+","
+
statusInfo.gpsSpeed;
if(smMessageReceiver
!=
null){
String
heartDataNew
=
smMessageReceiver.dispatchSmHeartIntent(heartData);
cmd
=
SerialInterface.consistStatusPush(CodeTool.splitStringArray(heartDataNew,
","));
}else{
cmd
=
SerialInterface.consistStatusPush(CodeTool.splitStringArray(heartData,
","));
}
virtualSMSerialPort.input(cmd);
...
示例2:客户端,接收到服务端的一个命令字,就会创建一个对象解析对应此命令字。
并且命令字解析完了,直接就做UI显示了。
但是现在客户端有一个测试需要,需要执行多个命令字,都执行成功才算成功。
问题就来了,现在每个命令字都是单独解析处理的。需要集中处理,于是定义一个拦截器:
/**
*
Created
by
XinYi
on
2019/7/25.
*
由于集成测试,需要拦截。
*/
public
class
CommandIntecepter
{
private
boolean
intercepted
=
false;
//是否拦截指令,只让自己处理。
private
InterceptCallBack
interceptCallBack;
private
static
final
CommandIntecepter
ourInstance
=
new
CommandIntecepter();
public
static
CommandIntecepter
getInstance()
{
return
ourInstance;
}
private
CommandIntecepter()
{
}
public
void
intercept(InterceptCallBack
interceptCallBack){
intercepted
=
true;
erceptCallBack
=
interceptCallBack;
}
public
void
cancelIntercept(){
intercepted
=
false;
erceptCallBack
=
null;
}
public
boolean
isIntercepted()
{
return
intercepted;
}
public
InterceptCallBack
getInterceptCallBack()
{
return
interceptCallBack;
}
public
interface
InterceptCallBack{
void
onA1(boolean
success);
void
onA2(boolean
success);
void
onA3(boolean
success);
void
onA4(boolean
success);
void
onA5(boolean
success);
void
onA6(boolean
success);
void
onA7(boolean
success);
void
onFailure();
}
}
每一个命令字,都加上拦截处理:/**
*
Created
by
XinYi
on
2018/9/29.
*
设备握手响应
*/
public
class
B1ResponseActioner
extends
BaseActioner
{
private
static
final
String
TAG
=
"B1ResponseActioner";
private
static
B1ResponseActioner
instance
=
new
B1ResponseActioner();
public
static
B1ResponseActioner
getInstance(){
return
instance;
}
@Override
public
void
action(String
realData)
{
AndroidConsoleLogPrinter.d(TAG,
"action:
收到设备握手响应<<--
A1
");
CommonResponseBean
commonResponseBean
=
new
CommonResponseBean(realData);
if(commonResponseBean.isOK()){
if(CommandIntecepter.getInstance().isIntercepted()){
CommandIntecepter.getInstance().getInterceptCallBack().onA1(true);
return;
}
BleResultUIDisplayer.getInstance().onA1(commonResponseBean.getData());
}else{
if(CommandIntecepter.getInstance().isIntercepted()){
CommandIntecepter.getInstance().getInterceptCallBack().onA1(false);
return;
}
BleResultUIDisplayer.getInstance().onFailure("设备握手响应,回复失败.");
}
}
}
测试代码里,加上拦截器的控制代码,这样各个指令的回调就集中了,便于判断处理:
千万注意:拦截器一定要准确的取消拦截,不能影响拦截器切点处的代码执行。
private
void
onceTest(final
OnceTestCallBack
onceTestCallBack){
CommandIntecepter.getInstance().intercept(new
CommandIntecepter.InterceptCallBack()
{
@Override
public
void
onA1(boolean
success)
{
}
@Override
public
void
onA2(boolean
success)
{
if(success){
JuLiBleServerSDK.getInstance().getShangHaiPure33ProtocolSender().send(ClientCmds.REQUEST_CMD_A3,
RequestDataGenerateUtil.createA3Data(iccCos));
}else{
onceTestCallBackFailure(onceTestCallBack,"ICC复位失败");
}
}
@Override
public
void
onA3(boolean
success)
{
if(success){
JuLiBleServerSDK.getInstance().getShangHaiPure33ProtocolSender().send(ClientCmds.REQUEST_CMD_A4,
RequestDataGenerateUtil.createA4Data(true));
}else{
onceTestCallBackFailure(onceTestCallBack,"ICC通道失败");
}
}
@Override
public
void
onA4(boolean
success)
{
if(success){
JuLiBleServerSDK.getInstance().getShangHaiPure33ProtocolSender().send(ClientCmds.REQUEST_CMD_A5,
RequestDataGenerateUtil.createA5Data(esamCos));
}else{
onceTestCallBackFailure(onceTestCallBack,"ESAM复位失败");
}
}
@Override
public
void
onA5(boolean
success)
{
if(success){
onceTestCallBack.onSuccess();
}else{
onceTestCallBackFailure(onceTestCallBack,"ESAM通道失败");
}
}
@Override
public
void
onA6(boolean
success)
{
}
@Override
public
void
onA7(boolean
success)
{
}
@Override
public
void
onFailure()
{
onceTestCallBackFailure(onceTestCallBack,"未知异常");
}
});
//Picc复位
JuLiBleServerSDK.getInstance().getShangHaiPure33ProtocolSender().send(ClientCmds.REQUEST_CMD_A2,
RequestDataGenerateUtil.createA2Data(false,
true));
}
8.注入
1)方法注入(适用于静态方法无法扩展)
最近在写音频播放,采用了MediaPlayer,发现这个类特别容易出现状态异常。就写了个类继承MediaPlayer,对各个方法稍微重写了下。
MediaPlayer有很多静态的create方法,由于是静态的,子类无法复写,也没法扩展。create方法的内部会newMediaPlayer然后返回,这样create出来的是父类的对象,根本没有子类的属性。
我尝试着再写个create方法,将newMediaPlayer弄一个抽象方法返回,但是抽象和静态是死敌,根本无法做到。我灵机一动,在自定义的create方法加个MediaPlayer的入参,这样子类在调用的时候就可以传
递子类的对象了。
/**
*
对父类的{@link
MediaPlayer#create(Context,
int)}方法作了拓展修改,增加了MediaPlayer对象参数,对象由外部创建,避免静态方法内部创建无法扩展。
*
*
@param
context
*
@param
resid
*
@return
*/
public
static
MediaPlayer
create(Context
context,
MediaPlayer
mp,
int
resid)
{
//注释的这段代码不加也不会有问题,加了会抛异常。
int
s
=
0;
/*try
{
s
=
(int)
ReflectManger.invokeMethod(Class.forName("android.media.AudioSystem"),
null,
"newAudioSessionId");
//AudioSystem为hide,无法直接使用。
}
catch
(NoSuchMethodException
e)
{
//TODO
会抛异常
NoSuchMethodException:
newAudioSessionId
[]
e.printStackTrace();
}
catch
(InvocationTargetException
e)
{
e.printStackTrace();
}
catch
(IllegalAccessException
e)
{
e.printStackTrace();
}
catch
(ClassNotFoundException
e)
{
e.printStackTrace();
}*/
return
create(context,
mp,
resid,
null,
s
>
0
?
s
:
0);
}
/**
*
对父类的{@link
MediaPlayer#create(Context,
int,
AudioAttributes,
int)}方法作了拓展修改,增加了MediaPlayer对象参数,对象由外部创建,避免静态方法内部创建无法扩展。
*
*
@param
context
*
@param
mp
*
@param
resid
*
@param
audioAttributes
*
@param
audioSessionId
*
@return
*/
public
static
MediaPlayer
create(Context
context,
MediaPlayer
mp,
int
resid,
AudioAttributes
audioAttributes,
int
audioSessionId)
{
try
{
AssetFileDescriptor
afd
=
context.getResources().openRawResourceFd(resid);
if
(afd
==
null)
return
null;
final
AudioAttributes
aa
=
audioAttributes
!=
null
?
audioAttributes
:
new
AudioAttributes.Builder().build();
mp.setAudioAttributes(aa);
mp.setAudioSessionId(audioSessionId);
mp.setDataSource(afd.getFileDescriptor(),
afd.getStartOffset(),
afd.getLength());
afd.close();
mp.prepare();
return
mp;
}
catch
(IOException
ex)
{
Log.d(TAG,
"create
failed:",
ex);
//
fall
through
}
catch
(IllegalArgumentException
ex)
{
Log.d(TAG,
"create
failed:",
ex);
//
fall
through
}
catch
(SecurityException
ex)
{
Log.d(TAG,
"create
failed:",
ex);
//
fall
through
}
return
null;
}
这样一来,我对抽象又有了新的认识:抽象方法是对整个方法体的抽象,一般的带参方法是对方法体的部分抽象。
9.其它,肯定有,尚未总结。
怎么解耦的问题:
不解耦的弊端,比如我之前将有关android源码的探索全部全部放到一篇文章里,后来博客系统出现了点问题。差点导致那篇博客损毁。所以不解耦很明显有2个缺点:
1)一损俱损:一个地方出现问题,会导致另外一个地方也出现问题。
2)查阅不方便:将所有的内容写到一篇博客,就像将所有的代码写在一个类中。这样这个类看起来就比较麻烦。
1.解耦
1)View的解耦:一般如详情页面、带有Banner图的页面,里面的View可能会有很多。可以将
大的View细分为小的View。
public
abstract
class
BasePart<T>
{
/**
*
获取当前模块的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()的时候,newPartView(),将子View添加到Activity的ContainerView中
,请求到数据之后再partView.setData();
但是PartView是一个很简单的类,如果需要Activity的参数(如调用Activity的方法等),可以在构造函数里传入Activity.对于别人重构的框架,应该如何去用的问题:
1.抽象类,肯定是要实现它示实现的方法。写逻辑就是对抽象方法的具体实现。将一些公共的libs和基类放在依赖库中
1)出现的问题:将工程的layout下的布局文件写到依赖库里,那么相关的资源color、style看着是
明明定义了,但就是can'tresolve.
AndroidStudio里所有项目的公共Library库,不要单独复制,要共有,防止以后要修改。/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的引用
/u013440413/article/details/78685192
(app里使用aar)
/lin_dianwei/article/details/79532078
(module里使用aar)
注意build.gradle里使用implecationcompileapi依赖的区别
/bellkosmos/p/6146349.html关于多重依赖清单文件重复的问题:
/bluestorm/p/6692789.html关于依赖多个module,可能有一个致命的问题。各个module及app的compileSdk的版本不一致,可能会导致以下问题:
Error:Executionfailedfortask':app:transformDexArchiveWithExternalLibsDexMergerForDebug'.
>java.lang.RuntimeException:java.lang.RuntimeException:com.android.builder.dexing.DexArchiveMergerException:Unabletomergedex
上面这个错误并非是jar包重复,可能是各个module及app的compileSdk的版本不一致导致的。单个module打jar包1)在android结点增加
2)在module的gradle里书写打包脚本
2)在module的gradle里书写打包脚本,增加具体的时间//修改jar名字+将指定jar生成的地方
task
makeJar(type:
Copy)
{
//删除存在的
delete
'build/libs'
//设置拷贝的文件
from('build/intermediates/intermediate-jars/release/')
//打进jar包后的文件目录
into('libs/')
//将classes.jar放入build/libs/目录下
//include
,exclude参数来设置过滤
//(我们只关心classes.jar这个文件)
include('classes.jar')
//重命名
rename
('classes.jar',
"CZBaseToolLibSdk_${releaseTime()}.jar")
//注意Jar包名字是双引号
}
makeJar.dependsOn(build)
def
releaseTime()
{
return
new
Date().format("yyyyMMddHHmm",
TimeZone.getTimeZone("GMT+08:00"))
}多层module依赖,打jar包的问题。
/mq0036/p/8566427.html#a22
1)moduleA依赖moduleB,moduleC依赖moduleA,如果将moduleA,moduleB打成jar包,给moduleC引用,回报重复类的错误:multidexclass。。。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(ProApplicat
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 丧葬服务合同范本
- 演艺服务合同实战技巧解析
- 临时保安聘用合同
- 正规个人借款合同模板
- 终止合同协议书全文版
- 借款合同模板关联企业间的借贷协议
- 物业服务合同范本标准
- 家庭服务员雇佣协议
- 录播教室设备采购合同
- 农家有机肥销售合同
- 2024年车辆工程大一大学生职业生涯规划书
- 2024年决战行测5000题言语理解与表达(培优b卷)
- 【培训课件】建设工程施工工地消防安全管理
- 广东省深圳市2023-2024学年高一上学期期末考试物理试题(含答案)
- 2023-2024学年江苏省苏州市昆山市八年级(上)期末英语试卷
- 2024届高考语文专题复习:文言文阅读专项练习题汇编(含答案)
- 2025年慢性阻塞性肺疾病全球创议GOLD指南修订解读课件
- 绿色建筑概论学习通超星期末考试答案章节答案2024年
- 露天矿山开采施工组织方案
- GB/T 44481-2024建筑消防设施检测技术规范
- 海滩冲浪课件教学课件
评论
0/150
提交评论