【移动应用开发技术】关于重构的一些思想_第1页
【移动应用开发技术】关于重构的一些思想_第2页
【移动应用开发技术】关于重构的一些思想_第3页
【移动应用开发技术】关于重构的一些思想_第4页
【移动应用开发技术】关于重构的一些思想_第5页
已阅读5页,还剩52页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】关于重构的一些思想

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. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论