【移動應(yīng)用開發(fā)技術(shù)】關(guān)于重構(gòu)的一些思想_第1頁
【移動應(yīng)用開發(fā)技術(shù)】關(guān)于重構(gòu)的一些思想_第2頁
【移動應(yīng)用開發(fā)技術(shù)】關(guān)于重構(gòu)的一些思想_第3頁
【移動應(yīng)用開發(fā)技術(shù)】關(guān)于重構(gòu)的一些思想_第4頁
【移動應(yīng)用開發(fā)技術(shù)】關(guān)于重構(gòu)的一些思想_第5頁
已閱讀5頁,還剩52頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

【移動應(yīng)用開發(fā)技術(shù)】關(guān)于重構(gòu)的一些思想

DRY原則:Don'tRepeatYourself(摘自wikipedia)OOA和OOD的作用及其區(qū)別

/s/blog_72ed42d401015y5c.html站在為程序員提供第3方服務(wù)SDK的高度來寫代碼1.抽象方法和非抽象方法如果在類中定義了抽象方法,就是強(qiáng)制非抽象子類去實現(xiàn)。這樣寫的好處就是可以提醒子類需要復(fù)寫哪些方法,但是抽象方法不易過多,不需要強(qiáng)制實現(xiàn)的方法不要抽象化。如果在類中定義了非抽象方法,可以給一個默認(rèn)的實現(xiàn),子類可以選擇不復(fù)寫,采用父類默認(rèn)的實現(xiàn),也可以復(fù)寫自定義實現(xiàn)。應(yīng)用場景:

抽取了一個帶ListView的BaseListFragment,后來又有需求,需要一個帶頭的ListView的Fragment,那么,就沒有必要再寫一個類繼承BaseListFragement了,況且繼承了ListView的初始化已經(jīng)完成了,沒法再加一個頭。

所以直接在BaseListFragment里定義一個方法返回頭視圖,默認(rèn)將方法返回值至為null,在listView初始化的時候,將方法返回值添加到listview的頭部,不為空就添加到頭部。2.關(guān)于抽象的方法在哪兒被調(diào)的問題

既然抽了方法,肯定在父類里的某個地方要讓其執(zhí)行。

如果父類有自己的生命周期方法,會自動執(zhí)行一些方法,在這些方法里可以抽出方法。

如果父類沒有自己的生命周期方法,那么調(diào)用抽取的方法的父類方法要記得手動去調(diào)用。3.關(guān)于怎么抽取的問題

情景1:抽象適配器,適配器的的抽象方法調(diào)用適配器所在父類的方法,父類的這個方法再抽象讓

子類去實現(xiàn)。如我的開源中國帶indicator的框架就是這么抽取的。

這種方式就是實現(xiàn)了方法的抽象和轉(zhuǎn)移,將內(nèi)部的抽象轉(zhuǎn)移成父類的抽象。4.關(guān)于電腦模型

其實編程和現(xiàn)實是息息相關(guān)的,如電腦和各個零部件就是抽取的具體體現(xiàn),各個零部件相互配合,但

是又沒有焊死在一起,這就是降低了耦合度。

程序的框架包括界面框架、網(wǎng)絡(luò)加載框架,框架在抽取的時候應(yīng)該降低它們之間的依賴性。

如果要寫出資深的重構(gòu)代碼,必需要“精通”以下的知識:

0.繼承與多態(tài)

靜態(tài)與多態(tài)無關(guān)

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");

}

}

調(diào)用ClassB

classA

=

new

ClassB();

classA.test();

輸出:test:ClassB

調(diào)用ClassA

classA

=

new

ClassB();

classA.test();

輸出:test:ClassA

**上面ClassA與ClassB同時存在test()方法,可見靜態(tài)方法不受繼承的影響,聲明的類是什么哪個類,就調(diào)用哪個類的靜態(tài)方法。

**因此,如果想要一個類的方法可以被外部訪問,但是又不想被子類復(fù)寫,那么只有publicstatic了。

0.封裝

封裝就是一個類A調(diào)用另外的一個類C時,不會直接調(diào)用,間接調(diào)用B,再用B去調(diào)用C。

比如在程序里通過要開啟很多個對象,如service,廣播等。如果直接開啟會造成很大的耦合。封裝要注意的地方1)避免畫蛇添足的封裝2)只暴露上層需要的接口3)對下層的異常回調(diào)要全面,不要有任何一點(diǎn)遺漏。4)不要過早的重構(gòu)、封裝

實例:模塊加載器

1)定義加載器接口:

public

interface

IDxTestInstanceLoader

extends

IAreaModuleLifeMethods{

void

load();

void

unload();

void

attach(Context

context);

}

2)實現(xiàn)加載器接口,創(chuàng)建實例。

比如實現(xiàn)load接口,可以開啟服務(wù),也可以開啟廣播,也可以開啟線程。

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

有時候執(zhí)行某些方法,必須有一些前提條件,沒有必要再每個要執(zhí)行的方法里單獨(dú)寫執(zhí)行條件的判斷??梢苑庋b一個方法:

1.接口

情景1:對幾個列表按時間字段進(jìn)行排序,但是幾個列表的實體類之間沒有任何的繼承關(guān)系,得到

時間的方法也不一樣,所以對于每一個列表都要定義一個比較器。

我在想,同樣比較的是時間,為什么要定義3個比較器呢?

于是,我定義了一個接口:

public

interface

DateInterface

{

String

getDate();

}

讓每個列表的實體類都去實現(xiàn)這個接口:

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進(jìn)行排序:

使用接口,本質(zhì)的作用是讓原本看似不相干的實體類之間產(chǎn)生了關(guān)系。也就是定義相同的行為,getDate,然后比較器針對接口編程,而不是某個具體的類。

情景2:ViewPager裝了多個Fragment,只想在viewpager滑動到哪一頁,就更新哪一頁的數(shù)據(jù)。

一般人的做法就是在每個Fragment定義一個加載數(shù)據(jù)的方法,然后在onPageChange方法里根據(jù)

position得到對應(yīng)的Fragment,然后調(diào)用fragment的對應(yīng)方法。

如果讓每一個Fragment實現(xiàn)一個懶加載數(shù)據(jù)的接口,那么在onPageChange就不需要根據(jù)posit

ion去if-else了,直接將fragment強(qiáng)轉(zhuǎn)成接口,調(diào)用接口的方法即可。

定義接口,有哪些好處:

1)方便明確業(yè)務(wù)

2)如果更換方法名,所有實現(xiàn)了接口的類都會自動更換,避免了手動更換的麻煩。

3)方便其他模塊調(diào)用,其他模塊只關(guān)心接口的方法就行,不需要關(guān)注接口實現(xiàn)類的其他方法。

2.反射

反射需要注意的地方:反射的類在某個版本可能更新了,之前的版本可能沒有某個方法,反射就會報NoSuchMethod異常。給Java類動態(tài)的增加屬性最近在做一個項目的時候,用到JSON解析,會創(chuàng)建很多的實體,但這些實體一般只有一個屬性,我在想,有沒有一種技術(shù)只創(chuàng)建一個空的類,然后動態(tài)的改變它的屬性呢?

后來百度了一下,確實存在這樣的技術(shù),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

但是無賴的是這個技術(shù)在純Java代碼里可以正常執(zhí)行(隨便定義一個類,一個main方法,類無任何的繼承。),但是在Activity里使用的話,就會報一個錯誤。要搞懂這個錯誤就得真正理解ClassLoader等類加載機(jī)制的原理。

情景1:字段過濾器

public

interface

IPropertyFilter

{

boolean

apply(Object

object,

String

name,

Object

value);

}public

interface

IGetFieldMap

{

Map

getFieldMap();

//所有字段名,拼出map。

Map

getFieldMap(String

...fieldNames);

//根據(jù)字段名,拼出map。

Map

getFieldMapExcept(String

...exceptFieldNames);

//除了指定的幾個字段名,拼出map。

Map

getFieldMap(List<String>

fieldNames);

//根據(jù)字段名,拼出map。

Map

getFieldMap(IPropertyFilter

propertyFilter);

//根據(jù)過濾器,拼出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類并調(diào)用其方法

/pshiping2014/article/details/79549680

linux工程師說將一個key存儲到build里了,但是查看Build類發(fā)現(xiàn)可以通過SystemProperties這個類來獲取,但是這個類是hide類,所以只能用反射去拿。

3.注解

4.泛型

Java泛型詳解

/jinuxwu/article/details/6771121

獲取泛型的Class

/onlysun/p/4539472.html

4-5.枚舉(優(yōu)化代碼可讀性)

/lmj623565791/article/details/79278864

枚舉如何定義構(gòu)造參數(shù)?

注意最后一個枚舉要加;然后按照正常的類寫語法就行了。public

enum

BleAreaType

{

HENAN("河南"),

//河南地區(qū)

GUIZHOU("貴州"),

//貴州地區(qū)

GUANGXI("廣西");

//廣西地區(qū)

private

String

areaChineseName;

BleAreaType(String

areaChineseName){

this.areaChineseName

=

areaChineseName;

}

public

String

getAreaChineseName()

{

return

areaChineseName;

}

}

5.自定義

自定義View或者類有多種形式:

1)完全自定義(復(fù)寫onDraw、onLayout)

2)組合自定義

3)包裹自定義(在自定義屬性綁定需要操作的子View的id,在onFinishInflate方法里find處理相關(guān)的邏輯,Google的很多框架級的原生組件用的就是這個)

如DrawerLayout,抽屜控件等。

4)工具類中封裝view,利用已有的view實現(xiàn)統(tǒng)一的業(yè)務(wù)接口。

5)復(fù)寫Android自定義的類(要求研究源碼,然后才能隨心所欲的復(fù)寫哈。)

實際場景1:使用ArrayAdapter這個類填充Spinner,如果ArrayAdapter的泛型是一個對象的話,最終Spinner顯示的是對象的哈希值。而我真正想展示在

Spinner上的只是ArrayAdapter泛型的某個字段而已。

最笨的解決方法就是遍歷List<T>,得到想要展示的字符串集合List<String>,再將List<String>設(shè)置給適配器。但是這樣一來的話,在spinner里點(diǎn)擊事件

里往往又會用到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;

}

通過源碼發(fā)現(xiàn),如果ArrayAdapter的泛型是字符串,那么spinner展示的是字符串;如果ArrayAdapter的泛型是一個對象的話,返回的是這個對象的toString方法的返回值。

解決方案1:復(fù)寫ArrayAdapter的getView的相關(guān)方法。

此解決方案的核心是將ArrayAdapter展示Spinner內(nèi)容部分的具體代碼抽象化成方法,從而使ArrayAdapter亦抽象化。

但是此方式有一個弊端:每有一個泛型類,就得新建一個對應(yīng)的Adapter類,太浪費(fèi)資源。

/**

*

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:復(fù)寫ArrayAdapter的getView的泛型類的toString方法

復(fù)寫泛型類的toString方法,返回想在spinner上展示的字段。如果泛型類的toString方法沒有在其它地方有特殊的引用,這種解決方法是最快最簡單的。

6.配置

對于一些比較固定的配置,不要使用修改代碼的方式,而是通過編輯配置文件、讀配置文件的方式。這樣更加的安全。6.攔截思想

我們可以把一個正常的流程想象成一根線,有時想改變這根線的某一個點(diǎn)的執(zhí)行流程,程序可能向后或者不向后執(zhí)行。我們需要做的就是定義攔截器。

示例1:設(shè)備按下一個物理按鍵,通用程序會執(zhí)行一個功能1,但是某一個地區(qū)可能需要執(zhí)行功能2.

一般人的想法就是,直接在通用程序里通過if-else來判斷地區(qū)。將地區(qū)的邏輯,寫在通用的代碼里,這就是一種耦合。假如后續(xù)要增加地區(qū),又要實現(xiàn)不同的功能,那這塊的代碼就會呈現(xiàn)爆炸式的增長。

正確的做法就是:定義攔截器,地區(qū)設(shè)置了攔截器,就將數(shù)據(jù)丟給攔截器處理,各地區(qū)的功能代碼寫在各地區(qū)的模塊里。這樣就優(yōu)美的將地區(qū)和通用程序的代碼解耦了

...

case

1://

升邁消息處理

MessageBean

messageBean

=

(MessageBean)

msg.obj;

QualityChecker.getInstance(SocketService.this,virtualSMSerialPort).onSMCallBack(messageBean);

AndroidConsoleLogPrinter.e("升邁消息處理

"

,"cmd

=

"

+

Integer.parseInt(messageBean.getCMD(),

16));

//回調(diào)升邁消息給地區(qū)子模塊

boolean

intercept

=

false;

if(smMessageReceiver

!=

null){

intercept

=

smMessageReceiver.dispatchSmIntent(messageBean);

}

AndroidConsoleLogPrinter.e("cmd

=

"

+

messageBean.getCMD()

,

"intercept:

"

+

intercept);

if(intercept)

return;

//子地區(qū)攔截復(fù)寫了對應(yīng)的指令,基本模塊不再執(zhí)行。

switch

(Integer.parseInt(messageBean.getCMD(),

16))

{

case

0x1A://

OBU通道命令

...

示例2:通用程序需要給單片機(jī)定時發(fā)送一個心跳數(shù)據(jù),單片機(jī)指示燈好顯示各個狀態(tài)的情況。但是某一個地區(qū)沒有電子狗,心跳的部分?jǐn)?shù)據(jù)還不一樣,指示電子狗狀態(tài)的netDogState字段,需要替換成另外一個應(yīng)用的狀態(tài)。

正確做法:定義攔截器,攔截心跳數(shù)據(jù),替換部分心跳數(shù)據(jù),返回新的心跳數(shù)據(jù)。

...

//由于不同的地區(qū),回復(fù)的心跳可能不太一樣。需要讓子區(qū)模塊進(jìn)行攔截

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:客戶端,接收到服務(wù)端的一個命令字,就會創(chuàng)建一個對象解析對應(yīng)此命令字。

并且命令字解析完了,直接就做UI顯示了。

但是現(xiàn)在客戶端有一個測試需要,需要執(zhí)行多個命令字,都執(zhí)行成功才算成功。

問題就來了,現(xiàn)在每個命令字都是單獨(dú)解析處理的。需要集中處理,于是定義一個攔截器:

/**

*

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.

*

設(shè)備握手響應(yīng)

*/

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:

收到設(shè)備握手響應(yīng)<<--

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("設(shè)備握手響應(yīng),回復(fù)失敗.");

}

}

}

測試代碼里,加上攔截器的控制代碼,這樣各個指令的回調(diào)就集中了,便于判斷處理:

千萬注意:攔截器一定要準(zhǔn)確的取消攔截,不能影響攔截器切點(diǎn)處的代碼執(zhí)行。

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復(fù)位失敗");

}

}

@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復(fù)位失敗");

}

}

@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復(fù)位

JuLiBleServerSDK.getInstance().getShangHaiPure33ProtocolSender().send(ClientCmds.REQUEST_CMD_A2,

RequestDataGenerateUtil.createA2Data(false,

true));

}

8.注入

1)方法注入(適用于靜態(tài)方法無法擴(kuò)展)

最近在寫音頻播放,采用了MediaPlayer,發(fā)現(xiàn)這個類特別容易出現(xiàn)狀態(tài)異常。就寫了個類繼承MediaPlayer,對各個方法稍微重寫了下。

MediaPlayer有很多靜態(tài)的create方法,由于是靜態(tài)的,子類無法復(fù)寫,也沒法擴(kuò)展。create方法的內(nèi)部會newMediaPlayer然后返回,這樣create出來的是父類的對象,根本沒有子類的屬性。

我嘗試著再寫個create方法,將newMediaPlayer弄一個抽象方法返回,但是抽象和靜態(tài)是死敵,根本無法做到。我靈機(jī)一動,在自定義的create方法加個MediaPlayer的入?yún)?,這樣子類在調(diào)用的時候就可以傳

遞子類的對象了。

/**

*

對父類的{@link

MediaPlayer#create(Context,

int)}方法作了拓展修改,增加了MediaPlayer對象參數(shù),對象由外部創(chuàng)建,避免靜態(tài)方法內(nèi)部創(chuàng)建無法擴(kuò)展。

*

*

@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對象參數(shù),對象由外部創(chuàng)建,避免靜態(tài)方法內(nèi)部創(chuàng)建無法擴(kuò)展。

*

*

@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;

}

這樣一來,我對抽象又有了新的認(rèn)識:抽象方法是對整個方法體的抽象,一般的帶參方法是對方法體的部分抽象。

9.其它,肯定有,尚未總結(jié)。

怎么解耦的問題:

不解耦的弊端,比如我之前將有關(guān)android源碼的探索全部全部放到一篇文章里,后來博客系統(tǒng)出現(xiàn)了點(diǎn)問題。差點(diǎn)導(dǎo)致那篇博客損毀。所以不解耦很明顯有2個缺點(diǎn):

1)一損俱損:一個地方出現(xiàn)問題,會導(dǎo)致另外一個地方也出現(xiàn)問題。

2)查閱不方便:將所有的內(nèi)容寫到一篇博客,就像將所有的代碼寫在一個類中。這樣這個類看起來就比較麻煩。

1.解耦

1)View的解耦:一般如詳情頁面、帶有Banner圖的頁面,里面的View可能會有很多??梢詫?/p>

大的View細(xì)分為小的View。

public

abstract

class

BasePart<T>

{

/**

*

獲取當(dāng)前模塊的View對象

*

@return

*/

public

abstract

View

getView();

/**

*

處理邏輯和數(shù)據(jù)

*

@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中

,請求到數(shù)據(jù)之后再partView.setData();

但是PartView是一個很簡單的類,如果需要Activity的參數(shù)(如調(diào)用Activity的方法等),可以在構(gòu)造函數(shù)里傳入Activity.對于別人重構(gòu)的框架,應(yīng)該如何去用的問題:

1.抽象類,肯定是要實現(xiàn)它示實現(xiàn)的方法。寫邏輯就是對抽象方法的具體實現(xiàn)。將一些公共的libs和基類放在依賴庫中

1)出現(xiàn)的問題:將工程的layout下的布局文件寫到依賴庫里,那么相關(guān)的資源color、style看著是

明明定義了,但就是can'tresolve.

AndroidStudio里所有項目的公共Library庫,不要單獨(dú)復(fù)制,要共有,防止以后要修改。/codezjx/article/details/49531887注意:如果ModuleA依賴了ModuleB,ModuleB有依賴了ModuleC,那么ModuleA的gradle里也要依賴moduleC,否則會報的ModuleB里找不到ModuleC的錯誤。

上層的Module如果依賴了下層的Module,上層的module要依賴下層所依賴的所有module。?。?!

如果工程有多個module,一定要從子module找起,單獨(dú)打開某個module集合目錄,排除依賴沒有寫的情況。!?。。。。。。。。。odule里不能有application結(jié)點(diǎn),所以module里的service、receiver這些結(jié)點(diǎn)也只能挪到app里定義。aar的引用

/u013440413/article/details/78685192

(app里使用aar)

/lin_dianwei/article/details/79532078

(module里使用aar)

注意build.gradle里使用implecationcompileapi依賴的區(qū)別

/bellkosmos/p/6146349.html關(guān)于多重依賴清單文件重復(fù)的問題:

/bluestorm/p/6692789.html關(guān)于依賴多個module,可能有一個致命的問題。各個module及app的compileSdk的版本不一致,可能會導(dǎo)致以下問題:

Error:Executionfailedfortask':app:transformDexArchiveWithExternalLibsDexMergerForDebug'.

>java.lang.RuntimeException:java.lang.RuntimeException:com.android.builder.dexing.DexArchiveMergerException:Unabletomergedex

上面這個錯誤并非是jar包重復(fù),可能是各個module及app的compileSdk的版本不一致導(dǎo)致的。單個module打jar包1)在android結(jié)點(diǎn)增加

2)在module的gradle里書寫打包腳本

2)在module的gradle里書寫打包腳本,增加具體的時間//修改jar名字+將指定jar生成的地方

task

makeJar(type:

Copy)

{

//刪除存在的

delete

'build/libs'

//設(shè)置拷貝的文件

from('build/intermediates/intermediate-jars/release/')

//打進(jìn)jar包后的文件目錄

into('libs/')

//將classes.jar放入build/libs/目錄下

//include

,exclude參數(shù)來設(shè)置過濾

//(我們只關(guān)心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引用,回報重復(fù)類的錯誤:multidexclass。。。api

(project(':moduleA'))

{

//解決重復(fù)依賴問題

exclude

module:

'moduleB'

}

這樣即可解決問題。

接著,如果將moduleC再生成一個jar包,moduleC.jar。引用moduleC.jar,類調(diào)用正常,但是一運(yùn)行就會報錯: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. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論