版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025-2030年中國川菜餐飲行業(yè)資本規(guī)劃與股權(quán)融資戰(zhàn)略制定與實施研究報告
- 2025-2030年中國新型煙草行業(yè)商業(yè)模式創(chuàng)新戰(zhàn)略制定與實施研究報告
- 建設(shè)工程資料歸檔規(guī)范
- 2024年月亮灣教案
- 石門縣黨建知識培訓(xùn)課件
- 吉林省扶余市(一實驗、二實驗)2023-2024學(xué)年九年級上學(xué)期期末化學(xué)測試卷
- 現(xiàn)代企業(yè)制度的局限性與大型企業(yè)經(jīng)營模式
- 二零二五年度廢棄塑料清運(yùn)及資源化利用合同3篇
- 醫(yī)院醫(yī)患溝通技巧培訓(xùn)
- 2025版二零二五年度智能家居研發(fā)工程師勞動合同書3篇
- 2023年非標(biāo)自動化工程師年度總結(jié)及來年計劃
- 2023-2024學(xué)年甘肅省嘉峪關(guān)市酒鋼三中高三上數(shù)學(xué)期末學(xué)業(yè)質(zhì)量監(jiān)測試題含解析
- 水利機(jī)械施工方案
- 懸挑式腳手架驗收記錄表
- 主變壓器試驗報告模板
- 電動叉車安全操作規(guī)程
- 靜鉆根植樁施工組織設(shè)計
- 工程精細(xì)化管理
- 柴油供貨運(yùn)輸服務(wù)方案
- 2022年長春市中小學(xué)教師筆試試題
- 肉牛肉羊屠宰加工項目選址方案
評論
0/150
提交評論