溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

關(guān)于重構(gòu)的一些思想

發(fā)布時間:2020-06-20 03:01:28 來源:網(wǎng)絡(luò) 閱讀:870 作者:屠夫章哥 欄目:移動開發(fā)

DRY原則:Don't Repeat Yourself (摘自wikipedia


OOA和OOD的作用及其區(qū)別?http://blog.sina.com.cn/s/blog_72ed42d401015y5c.html


站在為程序員提供第3方服務(wù)SDK的高度來寫代碼


1.抽象方法和非抽象方法

如果在類中定義了抽象方法,就是強制非抽象子類去實現(xiàn)。這樣寫的好處就是可以提醒子類需要復(fù)

寫哪些方法,但是抽象方法不易過多,不需要強制實現(xiàn)的方法不要抽象化。


如果在類中定義了非抽象方法,可以給一個默認的實現(xiàn),子類可以選擇不復(fù)寫,采用父類默認的實

現(xiàn),也可以復(fù)寫自定義實現(xiàn)。

應(yīng)用場景:

? 抽取了一個帶ListView的BaseListFragment,后來又有需求,需要一個帶頭的ListView的

Fragment,那么,就沒有必要再寫一個類繼承BaseListFragement了,況且繼承了ListView的初始化已經(jīng)

完成了,沒法再加一個頭。

? 所以直接在BaseListFragment里定義一個方法返回頭視圖,默認將方法返回值至為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ù)寫,那么只有public static了。


?

?0.封裝

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

? ? ? 比如在程序里通過要開啟很多個對象,如service,廣播等。如果直接開啟會造成很大的耦合。


  • 封裝要注意的地方

    1)避免畫蛇添足的封裝

    2)只暴露上層需要的接口

    3)對下層的異?;卣{(diào)要全面,不要有任何一點遺漏。

    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í)行的方法里單獨寫執(zhí)行條件的判斷??梢苑庋b一個方法:

    ??

  • private?void?runTask(Runnable?runnable)?{
    ????if?(isBleConnected)?{
    ????????runnable.run();
    ????}?else?{
    ????????shortToastOnUiThread("請先連接藍牙再操作!");
    ????}
    }

???????

??????


? 1.接口

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

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

???? ??關(guān)于重構(gòu)的一些思想

????? ?我在想,同樣比較的是時間,為什么要定義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進行排序:

????關(guān)于重構(gòu)的一些思想???

? 使用接口,本質(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強轉(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

??????

????????https://blog.csdn.net/WUWENJINWUWENJIN/article/details/83276553

????????https://blog.csdn.net/didi7696/article/details/82351167

????????https://blog.csdn.net/zghwaicsdn/article/details/50957474/

????????https://blog.csdn.net/li951418089/article/details/50392727

????????http://www.gaohaiyan.com/1772.html

????????https://www.cnblogs.com/zxf330301/p/5798241.html

????????https://www.it610.com/article/182201.htm

??? ? 但是無賴的是這個技術(shù)在純Java代碼里可以正常執(zhí)行(隨便定義一個類,一個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);??????????????//根據(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類里所有的字段

??https://blog.csdn.net/xiaoxian8023/article/details/24109185

??

??情景3:引用hide類并調(diào)用其方法

??https://blog.csdn.net/pshiping2014/article/details/79549680

??

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

?


? 3.注解

?4.泛型

? ?Java泛型詳解

? ?http://blog.csdn.net/jinuxwu/article/details/6771121

? ?

? ?獲取泛型的Class?

? ?http://www.cnblogs.com/onlysun/p/4539472.html

?

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

???http://blog.csdn.net/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里點擊事件

????里往往又會用到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類,太浪費資源。?

/**
?*?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.攔截思想??

? ?我們可以把一個正常的流程想象成一根線,有時想改變這根線的某一個點的執(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通用程序需要給單片機定時發(fā)送一個心跳數(shù)據(jù),單片機指示燈好顯示各個狀態(tài)的情況。但是某一個地區(qū)沒有電子狗,心跳的部分數(shù)據(jù)還不一樣,指示電子狗狀態(tài)的netDogState字段,需要替換成另外一個應(yīng)用的狀態(tài)。

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

...
????//由于不同的地區(qū),回復(fù)的心跳可能不太一樣。需要讓子區(qū)模塊進行攔截
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í)行成功才算成功。

? ??????

????????關(guān)于重構(gòu)的一些思想

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

????/**
?*?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;
????????this.interceptCallBack?=?interceptCallBack;
????}

????public?void?cancelIntercept(){
????????intercepted?=?false;
????????this.interceptCallBack?=?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í)行。

????????

????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)方法無法擴展)

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

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

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

????????? 遞子類的對象了。


??????????

/**
?????*?對父類的{@link?MediaPlayer#create(Context,?int)}方法作了拓展修改,增加了MediaPlayer對象參數(shù),對象由外部創(chuàng)建,避免靜態(tài)方法內(nèi)部創(chuàng)建無法擴展。
?????*
?????*?@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)建無法擴展。
?????*
?????*?@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.其它,肯定有,尚未總結(jié)。??

怎么解耦的問題:

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

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

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



? 1.解耦

?? ?1)View的解耦:一般如詳情頁面、帶有Banner圖的頁面,里面的View可能會有很多。可以將

?????大的View細分為小的View。

?????

public?abstract?class?BasePart<T>?{
???/**
????*?獲取當前模塊的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()的時候,new PartView(),將子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't resolve.

??

AndroidStudio里所有項目的公共Library庫,不要單獨復(fù)制,要共有,防止以后要修改。

https://blog.csdn.net/codezjx/article/details/49531887


注意:如果ModuleA依賴了ModuleB,ModuleB有依賴了ModuleC,那么ModuleA的gradle里也要依賴moduleC,否則會報的ModuleB里找不到ModuleC的錯誤。

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

????????

????????如果工程有多個module,一定要從子module找起,單獨打開某個module集合目錄 ,排除依賴沒有寫的情況 。?。。。。。。。。。?!


module里不能有application結(jié)點,所以module里的service、receiver這些結(jié)點也只能挪到app里定義。


aar的引用

????? https://blog.csdn.net/u013440413/article/details/78685192? ?(app里使用aar)

?

??????https://blog.csdn.net/lin_dianwei/article/details/79532078? ? ? (module里使用aar)


????? 注意build.gradle里使用implecation compile api依賴的區(qū)別

AndroidStudio 多層級 Module 對 aar 引用問題

????????https://www.cnblogs.com/bellkosmos/p/6146349.html



關(guān)于多重依賴清單文件重復(fù)的問題:

????????https://www.cnblogs.com/bluestorm/p/6692789.html


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

????????Error:Execution failed for task ':app:transformDexArchiveWithExternalLibsDexMergerForDebug'.

????????> java.lang.RuntimeException: java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Unable to merge dex

????? ? 上面這個錯誤并非是jar包重復(fù),可能是各個module及app的compileSdk的版本不一致導(dǎo)致的。



  • 單個module打jar包

    1)在android結(jié)點增加?

  • ??lintOptions?{
    ????????abortOnError?false
    ????}


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

  • //修改jar名字+將指定jar生成的地方
    task?makeJar(type:?Copy)?{
    ????//刪除存在的
    ????delete?'build/libs/cz_zbar_sdk.jar'
    ????//設(shè)置拷貝的文件
    ????from('build/intermediates/intermediate-jars/release/')
    ????//打進jar包后的文件目錄
    ????into('libs/')
    ????//將classes.jar放入build/libs/目錄下
    ????//include?,exclude參數(shù)來設(shè)置過濾
    ????//(我們只關(guān)心classes.jar這個文件)
    ????include('classes.jar')
    ????//重命名
    ????rename?('classes.jar',?'cz_zbar_sdk.jar')
    }
    
    makeJar.dependsOn(build)

????? ?2)在module的gradle里書寫打包腳本,增加具體的時間

//修改jar名字+將指定jar生成的地方????????
????????task?makeJar(type:?Copy)?{
????????????//刪除存在的
????????????delete?'build/libs'
????????????//設(shè)置拷貝的文件
????????????from('build/intermediates/intermediate-jars/release/')
????????????//打進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包的問題。

?????????https://www.cnblogs.com/mq0036/p/8566427.html#a22


????? ?1)moduleA依賴moduleB,moduleC依賴moduleA,如果將moduleA,moduleB打成jar包,給moduleC引用,回報重復(fù)類的錯誤:multidex class。。。

api?(project(':moduleA'))?{
????//解決重復(fù)依賴問題
????exclude?module:?'moduleB'
}

??????????? 這樣即可解決問題。

????????接著,如果將moduleC再生成一個jar包,moduleC.jar。引用moduleC.jar,類調(diào)用正常,但是一運行就會報錯:

Caused?by:?java.lang.ClassNotFoundException:?Didn't?find?class?"com.cz.basetool.ui_work.thread.ThreadManager"?on?path:?DexPathList[[zip?file?"/data/app/com.example.jl.jiangxihfscj-2/base.apk"],nativeLibraryDirectories=[/vendor/lib,?/system/lib]]
?????????????????????????????????????????????????????????????????????????????????at?dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
?????????????????????????????????????????????????????????????????????????????????at?java.lang.ClassLoader.loadClass(ClassLoader.java:511)
?????????????????????????????????????????????????????????????????????????????????at?java.lang.ClassLoader.loadClass(ClassLoader.java:469)
?????????????????????????????????????????????????????????????????????????????????at?com.juli.basescjmodule.libs.hardware_libary.a.initSystemConfig(SystemUtil.java:45)?
?????????????????????????????????????????????????????????????????????????????????at?com.juli.basescjmodule.libs.hardware_libary.a.a(SystemUtil.java:39)?
?????????????????????????????????????????????????????????????????????????????????at?com.juli.basescjmodule.libs.base_tool.hardware_service.obu.action.system.SystemActionController.init(SystemActionController.java:38)?
?????????????????????????????????????????????????????????????????????????????????at?com.juli.basescjmodule.libs.base_tool.hardware_service.obu.action.system.SystemActionController.init(SystemActionController.java:32)?
?????????????????????????????????????????????????????????????????????????????????at?com.juli.basescjmodule.libs.basesdk.JLBaseActionContoller.initSDK(JLBaseActionContoller.java:83)?
?????????????????????????????????????????????????????????????????????????????????at?com.example.jiangxisdklibrary.JLScjContoller.initSDK(JLScjContoller.java:30)?
?????????????????????????????????????????????????????????????????????????????????at?com.example.jl.jiangxihfscj.ProApplication.onCreate(ProApplication.java:24)?
?????????????????????????????????????????????????????????????????????????????????at?android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1012)?
?????????????????????????????????????????????????????????????????????????????????at?android.app.ActivityThread.handleBindApplication(ActivityThread.java:4553)?
?????????????????????????????????????????????????????????????????????????????????at?android.app.ActivityThread.access$1500(ActivityThread.java:151)?
?????????????????????????????????????????????????????????????????????????????????at?android.app.ActivityThread$H.handleMessage(ActivityThread.java:1364)?
?????????????????????????????????????????????????????????????????????????????????at?android.os.Handler.dispatchMessage(Handler.java:102)?
?????????????????????????????????????????????????????????????????????????????????at?android.os.Looper.loop(Looper.java:135)?
?????????????????????????????????????????????????????????????????????????????????at?android.app.ActivityThread.main(ActivityThread.java:5254)?
?????????????????????????????????????????????????????????????????????????????????at?java.lang.reflect.Method.invoke(Native?Method)?
?????????????????????????????????????????????????????????????????????????????????at?java.lang.reflect.Method.invoke(Method.java:372)?
?????????????????????????????????????????????????????????????????????????????????at?com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:902)?
?????????????????????????????????????????????????????????????????????????????????at?com.android.internal.os.ZygoteInit.main(ZygoteInit.java:697)?
?????????????????????????????????????????????????????????????????????????????	Suppressed:?java.lang.ClassNotFoundException:?com.cz.basetool.ui_work.thread.ThreadManager
?????????????????????????????????????????????????????????????????????????????????at?java.lang.Class.classForName(Native?Method)
?????????????????????????????????????????????????????????????????????????????????at?java.lang.BootClassLoader.findClass(ClassLoader.java:781)
?????????????????????????????????????????????????????????????????????????????????at?java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
?????????????????????????????????????????????????????????????????????????????????at?java.lang.ClassLoader.loadClass(ClassLoader.java:504)
?????????????????????????????????????????????????????????????????????????????????		...?19?more
??????????????????????????????????????????????????????????????????????????????Caused?by:?java.lang.NoClassDefFoundError:?Class?not?found?using?the?boot?class?loader;?no?stack?available

???????? 而且在moduleC.jar也找不到這個類,準確的說是找不到moduleA和moduleB的類。

????????》》如果moduleC中有UI,將moduleC打成jar包,還會報UI相關(guān)找不到的錯誤。

????????


????????????????????????

????????解決辦法:https://www.jianshu.com/p/8f9cf6271c20

????????????????????????https://www.cnblogs.com/Jason-Jan/p/9192273.html

????????我的gradle的環(huán)境:compileSdkVersion 26?????buildToolsVersion "25.0.3"???

????????????????????


? ? ? ? ? ? ? ? 1.fat-aar.gradle:要打哪個module的jar包,就放到哪個module下面。

? ? ? ? ? ? ? ? 2.github上的這個庫,有個前提就是gradle插件必須是2.3.3,所以我把

????????????????????gradle要加上buildToolsVersion

????????????????????gradle要將implecation換成compile? ?

????????????????????

????????????????????工程的gradle插件變?yōu)?.3.3

classpath?'com.android.tools.build:gradle:2.3.3'

????????????????????properties的版本可以不用改,如果改成2.3.3還會報錯:

????????????????????https://www.jianshu.com/p/6a0bc5792860

????????????????默認aar的生成位置:

????????????????????? ?關(guān)于重構(gòu)的一些思想

????????????????默認也會生成jar文件,但是引用后發(fā)現(xiàn)里面只有R文件:

????????????????關(guān)于重構(gòu)的一些思想

????????????????那么,既然aar都能打出來,是不是改改fat-aar就能搞個fat-jar打個jar呢???????????????????????

????????? ??????還有一種方法:https://blog.csdn.net/anymyna/article/details/82054020??(將aar轉(zhuǎn)成jar),如果aar中有需要用到的資源文件或者so庫,則不能只用jar包。否則就會報not found錯誤。

????????????????


????????????????3.怎么混淆:https://www.jianshu.com/p/aa3e78a7a095?? ? ?(關(guān)于混淆的配置,只需要在最上層的merge模塊中配置即可

? ? ? ? ? ? ? ? 4.release的aar不出來咋辦:

?????????????????????執(zhí)行cleanBuildCache然后執(zhí)行assembleRelease? ?

????????????????????關(guān)于重構(gòu)的一些思想

? ? ? ? ? ? 5.假如打jar包的module要做修改,把jar包源碼的module重新依賴上,可能會出現(xiàn)錯誤:

????????????? ? ?com.android.dex.DexException: Multiple dex files define Lcom/某個類

????????????? ?這就有可能是你打jar的多個一級子module依賴了同一個二級子module導(dǎo)致的重復(fù)的引用的問題,因為降低gradle插件版本之前依賴module使用的implementation關(guān)鍵字,后來使用的

????????????????compile關(guān)鍵字。

????????????? 關(guān)于這兩個依賴module的關(guān)鍵字的區(qū)別:https://blog.csdn.net/hwra2008/article/details/78989640

????????????? 解決辦法:所以可以在root module里創(chuàng)建一個公共的變量,各個module通過變量使用2個版本的gradle語法。https://www.cnblogs.com/DreamRecorder/p/9198155.html

????????????? 但是root module本身好像無法使用自己定義的這個變量。

????????????

????????????????最后,一定要重新清理所有的module,否則可能有緩存。

? ? ? ? ? ? 6.aar打成功了,但是引用的時候出現(xiàn)了個問題:

????????????????????????????????????????????

????????????????????????????Error:Execution failed for task ':app:processDebugManifest'.

????????????????????????????> com.android.manifmerger.ManifestMerger2$MergeFailureException: org.xml.sax.SAXParseException; lineNumber: 18; columnNumber: 47; 與元素類型 "uses-

????????????????????????????permission" 相關(guān)聯(lián)的屬性 "tools:ignore" 的前綴 "tools" 未綁定。

????????????? ?發(fā)現(xiàn)原來一個module里有?

<!--????<uses-permission
????????android:name="android.permission.BLUETOOTH_PRIVILEGED"
????????tools:ignore="ProtectedPermissions"?/>-->

????????? ? ? ?把這個屬性重新注釋掉,清除各個module的build文件夾,重新打包即可。

? ? ? ? ? ? 7.module里引用了一個aar包aarA,再將這個module打成aar包aarB,去app里引用aarB,會找不到aarA包中的類。??

? ???????????? 解決辦法:將aarA解壓出來,將其classes.jar拷貝到aarB的libs目錄下。(離架構(gòu)又近了一步)

? ? ? ? ? ? 8.打aar的時候,非fat-aar所在的module的class并沒有打包進去,使用aar的時候,會報"not found class xxx"錯誤。

????????????? ?后來才發(fā)現(xiàn)在全局替換的時候 ,有個release的方法,碰巧fat-aar里也有這個字符串。所以,還是得看懂fat-aar的意思才行。

? ? ? ? ? ? 9.module嵌套依賴時,最好不要出現(xiàn),mb依賴ma,mc依賴ma,然后md同時依賴mb和mc的情況,這樣md打包aar可能就會報multiple dex files錯誤。

? ? ? ? ? ? ??

? ? ? ? ? ? ? ?****這個時候fat-aar得關(guān)鍵字embedded,也不支持exlude的語法。只能從內(nèi)部module出發(fā),修改compile加上exclude,防止重復(fù)引用。

???????????????????? 但是問題還是沒有解決,發(fā)現(xiàn)竟然是md重復(fù)引用了一個module的原因,而且用的是compile語法。

? ? ? ? ? ? ???所以遇到這種問題,第一,定位到底是哪個module重復(fù)引用了?

?

編寫SDK需要注意的地方

? ? ?1)考慮對宿主的兼容性

????????? 不可?引用過高版本的依賴,導(dǎo)致宿主依賴的低版本有轉(zhuǎn)換的相關(guān)的異常。

  • ? ? ? ? ? 比如在sdk中引用androidx庫,但是宿主用的是傳統(tǒng)的com.android.*的庫。SDK中用到了androidx的ContextCompat,但是宿主傳入的是低版本的Context。

    ? ? ?關(guān)閉AndroidX:https://blog.csdn.net/u013040819/article/details/94399117

??????????

???????????


將module與主程序解耦

????在module寫了一些功能,但是功能需要主程序去輔助完成。這樣就矛盾了,module又不能去依賴主程序,怎么調(diào)用主程序的東西呢?

? ? 解決辦法 1

????????在module里定義一個抽象類,module能實現(xiàn)的功能全部實現(xiàn),實現(xiàn)不了定義成抽象方法,將主程序完成某功能需要的參數(shù)回調(diào)出去。再在主程序里注入實現(xiàn):

? ? ? ? 主程序要干的事情:

? ? ? ?

IDeviceAction?{
????(openOutputDataSender?outputDataSender)(openOutputDataSender?outputDataSender)(openOutputDataSender?outputDataSender)(highVolumeOutputDataSender?outputDataSender)(volumeValueOutputDataSender?outputDataSender)(OutputDataSender?outputDataSender)}

????????

?????????moudle里半實現(xiàn)的抽象類:

BlueToothActionReceiver?BroadcastReceiver?IDeviceAction{
????String?=?String?=?String?=?(Context?contextIntent?intent)?{
????????(intent?!=?&&?intent.getAction().equals()){
????????????option?=?intent.getIntExtra(-)String?state?=?intent.getStringExtra()(option){
????????????????DeviceActionOption.:
????????????????????setGPS(state.equals()???:?OutputDataSender.())DeviceActionOption.:
????????????????????setRecord(state.equals()???:?OutputDataSender.())DeviceActionOption.:
????????????????????setSta(state.equals()???:?OutputDataSender.())DeviceActionOption.:
????????????????????(state.substring()){
????????????????????????:??????setVolume(OutputDataSender.()):??????setVolume(OutputDataSender.()):??????String?valume?=?state.substring()value?=?CodeTool.(valume)setVolume(valueOutputDataSender.())}

????????????????????DeviceActionOption.:
????????????????????getDeviceStatus(OutputDataSender.())}
????????}
????}
}


????????????在module里發(fā)廣播,這樣主程序就能完成未完成的功能。





重構(gòu)的實例場景

? 1)一個網(wǎng)絡(luò)請求接口(傳一個paperid得到試卷的題目)要在程序的多處調(diào)用,之前離職的程序員寫

? ?的代碼也沒有封裝:

? ?

/**
?*?從網(wǎng)絡(luò)獲取ListView的數(shù)據(jù)
?*/
private?void?getListData(String?p_id)?{
????if?(!NetWorkStatusUtil.isNetworkAvailable(context))?{
????????ToastUtil.showShort(context,?"請先鏈接網(wǎng)絡(luò)");
????????return;
????}
????LoadingDialog.showProgress(this,?"題目正在加載中,請稍等...",?true);
????String?url?=?HttpConfig.QUESTION;
????RequestParams?params?=?new?RequestParams();
????//?params.addBodyParameter("token",?MD5Encoder.getMD5());
????KLog.e("試卷頁面p_id:"?+?p_id);
????params.addQueryStringParameter("token",?MD5Encoder.getMD5());
????params.addQueryStringParameter("p_id",?p_id);
????//p_id=2是單選;
????//http://app.haopeixun.org/index.php?g=apps&m=paper&a=getExams&token=75c824f486b5fa5b60330697bdb03842&p_id=8
????//
????HttpUtils?httpUtils?=?new?HttpUtils();
????httpUtils.send(HttpMethod.GET,?url,?params,?new?RequestCallBack<String>()?{

????????@Override
????????public?void?onSuccess(ResponseInfo<String>?responseInfo)?{
????????????jsonString?=?responseInfo.result;
????????????Log.i(TAG,?"考題答案:?");
????????????KLog.json(jsonString);
????????????if?(!TextUtils.isEmpty(jsonString))?{
????????????????JSONObject?jsonObject;
????????????????try?{
????????????????????jsonObject?=?new?JSONObject(jsonString);
????????????????????int?resultCode?=?Integer.parseInt(jsonObject.getString("result"));
????????????????????KLog.e("結(jié)果碼:"?+?resultCode);
????????????????????switch?(resultCode)?{
????????????????????????case?0:
????????????????????????????if?(jsonString.isEmpty())?{
????????????????????????????????showEmptyView();
????????????????????????????????return;
????????????????????????????}
????????????????????????????parserData(jsonString);
????????????????????????????break;
????????????????????????default:
????????????????????????????showEmptyView();
????????????????????}
????????????????}?catch?(JSONException?e)?{
????????????????????showEmptyView();
????????????????????//?TODO?Auto-generated?catch?block
????????????????????e.printStackTrace();
????????????????}?finally?{
????????????????????LoadingDialog.dismissProgressDialog();
????????????????}
????????????}
????????}

????????@Override
????????public?void?onFailure(HttpException?arg0,?String?arg1)?{
????????????KLog.e(arg1);
????????????LoadingDialog.dismissProgressDialog();
????????????ToastUtil.showLong(context,?"請求考題失敗");
????????}
????});
}


private?void?parserData(String?jsonString)?{
????//?***********給user賦值******************
????qustionItems.clear();

????try?{
????????JSONObject?jsonObject?=?new?JSONObject(jsonString);
????????JSONArray?dataArray?=?jsonObject.getJSONArray("data");
????????totalScore?=?dataArray.length();
????????Log.i(TAG,?"總共題目數(shù)量:"?+?totalScore);
????????for?(int?i?=?0;?i?<?dataArray.length();?i++)?{
????????????QuestionBean?questionBean?=?new?QuestionBean();
????????????JSONObject?jsonObject2?=?dataArray.getJSONObject(i);
????????????questionBean.addtime?=?jsonObject2.getString("addtime");
????????????questionBean.e_analyze?=?jsonObject2.getString("e_analyze");
????????????questionBean.e_answer_count?=?jsonObject2.getString("e_answer_count");
????????????//?*********
????????????JSONObject?jsonObject3?=?jsonObject2.getJSONObject("e_answer");//?題選項對象
????????????questionBean.eanswer?=?questionBean.new?Eanswer();
????????????questionBean.eanswer.setA(jsonObject3.getString("A"));
????????????questionBean.eanswer.setB(jsonObject3.getString("B"));
????????????String?answerC?=?jsonObject3.optString("C");
????????????if?(answerC?!=?null?&&?!TextUtils.isEmpty(answerC))?{
????????????????questionBean.eanswer.setC(answerC);
????????????}

????????????String?answerD?=?jsonObject3.optString("D");
????????????if?(answerD?!=?null?&&?!TextUtils.isEmpty(answerD))?{
????????????????questionBean.eanswer.setD(answerD);
????????????}

????????????switch?(Integer.parseInt(questionBean.e_answer_count))?{
????????????????case?5:
????????????????????questionBean.eanswer.setE(jsonObject3.getString("E"));
????????????????????break;
????????????????case?6:
????????????????????questionBean.eanswer.setE(jsonObject3.getString("E"));
????????????????????questionBean.eanswer.setF(jsonObject3.getString("F"));
????????????????????break;
????????????????case?7:
????????????????????questionBean.eanswer.setE(jsonObject3.getString("E"));
????????????????????questionBean.eanswer.setF(jsonObject3.getString("F"));
????????????????????questionBean.eanswer.setG(jsonObject3.getString("G"));
????????????????????break;
????????????}
????????????//?**********

????????????questionBean.e_classid?=?jsonObject2.getString("e_classid");
????????????questionBean.e_exam_type?=?jsonObject2.getString("e_exam_type");
????????????questionBean.e_fenzhi?=?jsonObject2.getString("e_fenzhi");
????????????questionBean.e_good?=?jsonObject2.getString("e_good");
????????????questionBean.e_id?=?jsonObject2.getString("e_id");
????????????questionBean.e_probability?=?jsonObject2.getString("e_probability");
????????????questionBean.e_result?=?jsonObject2.getString("e_result");
????????????questionBean.e_status?=?jsonObject2.getString("e_status");
????????????questionBean.e_title?=?jsonObject2.getString("e_title");
????????????questionBean.e_typeid?=?jsonObject2.getString("e_typeid");
????????????questionBean.examid?=?jsonObject2.getString("examid");
????????????questionBean.id?=?jsonObject2.getString("id");
????????????questionBean.paperid?=?jsonObject2.getString("paperid");
????????????qustionItems.add(questionBean);
????????????StringBuilder?sb?=?new?StringBuilder();
????????????//sb.append("");
????????????uAnswerList.add(sb);
????????}
????}?catch?(JSONException?e)?{
????????e.printStackTrace();
????}?finally?{
????????LoadingDialog.dismissProgressDialog();
????}
????showDataView();
????KLog.e("集合大小"?+?qustionItems.size());
????if?(qustionItems.size()?>?0)?{
????????tv_total_questions.setText("1?/?共"?+?qustionItems.size()?+?"道題");
????}
????questionPagerAdapter.set(qustionItems,?uAnswerList);
????uAnswerStateAdapter.set(getAnswerCard());
}

???同樣的接口,我覺得沒有必要再往新的activity復(fù)制一遍,于是我將這個接口單獨封裝成一個類

? PaperListRequester中

? ??關(guān)于重構(gòu)的一些思想 ? ?在構(gòu)造PaperListRequester這個類的時候,我將Context和接口需要的參數(shù)pid傳遞進去。定義了

一個監(jiān)聽器ParseListener,監(jiān)聽內(nèi)部數(shù)據(jù)解析的結(jié)果。通過一個方法getData,并傳遞一個監(jiān)聽器返回結(jié)果。

? ?這是調(diào)用PaperListRequester這個類請求接口的代碼:

?關(guān)于重構(gòu)的一些思想? ?將請求、解析接口的邏輯封裝在PaperListRequester這個類中,一下代碼就看得順眼了。這也是Google及一些開源庫常用的工具類封裝手法。

? ?2)工程中有個ActivityA,作用是取一個數(shù)據(jù),但是這個取數(shù)據(jù)的過程,與activity里的handler相關(guān)聯(lián)。代碼量也比較多。這時候有個ActivityB,需要和

?? ??ActivityA重復(fù)一樣的工作,但是ActivityB本身類里邏輯比較復(fù)雜,如果直接把ActivityA里的代碼粘貼過來,會嚴重影響這個類的結(jié)構(gòu)。所以這里????

????就需要新建一個類C.java,用來封裝ActivityA的邏輯,通過接口返回數(shù)據(jù)。在ActivityB里調(diào)用C,只管請求和取數(shù)據(jù),中間的過程交由C.java去實現(xiàn)。

????實現(xiàn)解耦。

? ?

6.避免過多使用if-else

? ? 如果一個項目中,一個變量因素會影響多個地方的行為或者動作,可以將這此動作定義成接口。然后根據(jù)變量定義不同的實例實現(xiàn)接口。

? ? 實戰(zhàn)1:比如我的項目里,不同的設(shè)備串口號、其它的一些信息不一樣

? ?1-1:抽取接口

**
?*?Created?by?XinYi?on?2018/8/7.
?*?獲取設(shè)備的一些信息,因設(shè)備硬件版本不同而不同,需要兼容。
?*?1.移遠(28)
?*?2.旗文(06B)
?*/

public?interface?IDeviceContent?{
????/**
?????*?升麥串口號
?????*?@return
?????*/
????String?getSmSerialFileName();

????/**
?????*?OBD串口號
?????*?@return
?????*/
????String?getObdSerialFileName();

????/**
?????*?SD卡掛載,掃描的根目錄是否正確。
?????*?@see??SMCardManager.CardChecker#initFilter()
?????*?@param?fileName
?????*?@return
?????*/
????boolean?isSmScardPath(String?fileName);


}

?1-2:根據(jù)不同的平臺,實現(xiàn)接口,創(chuàng)建不同的實例:

? ? 平臺1:

/**
?*?Created?by?XinYi?on?2018/8/7.
?*?旗文(06B)?平臺
?*/

public?class?QWDeviceContent?implements?IDeviceContent?{
????@Override
????public?String?getSmSerialFileName()?{
????????return?"/dev/ttyMT2";
????}

????@Override
????public?String?getObdSerialFileName()?{
????????return?"/dev/ttyM×××";
????}

????@Override
????public?boolean?isSmScardPath(String?fileName)?{
????????return?fileName.conta×××("usbotg");
????}
}

? 平臺2:

/**
?*?Created?by?XinYi?on?2018/8/7.
?*?移遠(28)平臺
?*/

public?class?YYDeviceContent?implements?IDeviceContent?{
????@Override
????public?String?getSmSerialFileName()?{
????????return?"/dev/ttyHSL0";
????}

????@Override
????public?String?getObdSerialFileName()?{
????????return?"/dev/ttyHSL1";
????}

????@Override
????public?boolean?isSmScardPath(String?fileName)?{
????????return?(fileName.conta×××(Constants.SM_SDCARD_QW_PATH)?&&?fileName.length()?==?9);
????}
}

? 1-3:指定一個實際使用的實例

private?void?fillDataFromBuilder(Builder?builder)?{
????this.deviceType?=?builder.deviceType;
????this.areaId?=?builder.areaId;
????if(deviceType?==?DeviceType.TYPE_28){
????????deviceContent?=?new?YYDeviceContent();
????}else?if(deviceType?==?DeviceType.TYPE_06B){
????????deviceContent?=?new?QWDeviceContent();
????}
}

?這樣就避免了每一個方法返回的值都需要根據(jù)平臺用if-else判斷一下,一旦增加一個平臺,只需要創(chuàng)建一個平臺的實例即可。



避免多個異步接口多層嵌套回調(diào)

? ? ?我們調(diào)用的代碼是異步的,或者開一個線程,都會導(dǎo)致我們不由自主的使用異步回調(diào)。以我多年的開發(fā)經(jīng)驗,異步可以轉(zhuǎn)化成同步,定義方法最好用同步,這樣遇到復(fù)雜的嵌套調(diào)用邏輯,就會避免多層嵌套的問題,提高代碼的可閱讀性。


????????實際場景:下面這此設(shè)置項,每一項都有一個單獨的接口,都是異步回調(diào)。這樣如果有10多層異步嵌套,將會造成代碼閱讀性非常差。

????? ??

????????關(guān)于重構(gòu)的一些思想

????????于是我想到了“封裝”

????? ? #1.定義單項設(shè)置器的基類

/**
?*?Created?by?XinYi?on?2018/12/20.
?*?單個參數(shù)設(shè)置基類
?*/

public?abstract?class?BaseSingleRecordParamHandler?{
????protected?String?TAG?=?this.getClass().getSimpleName();

????protected?RecordParamsHandleController?recordParamsSetController;
????protected???String?param;

????/**
?????*?設(shè)置參數(shù)
?????*/
????abstract?void?handleSetParam();


????/**
?????*?獲取參數(shù)
?????*/
????abstract?void?handleGetParam();

????protected?YiYunController.YiYunActionCallBack?getYiYunActionCallBack(){
????????return?new?YiYunController.YiYunActionCallBack()?{
????????????@Override
????????????public?void?onSuccess(Object?obj)?{
????????????????BaseSingleRecordParamHandler.this.onSetParamSuccess();
????????????}

????????????@Override
????????????public?void?onFailure(Exception?e)?{
????????????????BaseSingleRecordParamHandler.this.onSetParamFailure(e);
????????????}
????????};
????}


????protected?BaseSingleRecordParamHandler?getNextSingleRecordParamHandler(){
????????return?recordParamsSetController.getNextSingleRecordParamHandler();
????}

????public?void?attachRecordParamsSetController(RecordParamsHandleController?recordParamsSetController)?{
????????this.recordParamsSetController?=?recordParamsSetController;
????}

????public?BaseSingleRecordParamHandler?setParam(String?param)?{
????????this.param?=?param;
????????return?this;
????}

????protected?void?onSetParamSuccess(){
????????BaseSingleRecordParamHandler?nextSingleRecordParamSetter?=?recordParamsSetController.getNextSingleRecordParamHandler();
????????if(nextSingleRecordParamSetter?!=?null){
????????????nextSingleRecordParamSetter.handleSetParam();
????????}else{
????????????recordParamsSetController.getParamsSetResultCallBack().onSuccess();
????????}
????}

????protected?void?onSetParamFailure(Exception?e){
????????recordParamsSetController.getParamsSetResultCallBack().onFailure(e);
????}


????protected?void?onGetParamSuccess(){
????????BaseSingleRecordParamHandler?nextSingleRecordParamSetter?=?recordParamsSetController.getNextSingleRecordParamHandler();
????????if(nextSingleRecordParamSetter?!=?null){
????????????nextSingleRecordParamSetter.handleSetParam();
????????}else{
????????????recordParamsSetController.getParamsGetResultCallBack().onSuccess(recordParamsSetController.getCollectedParams());
????????}
????}

????protected?void?onGetParamFailure(Exception?e){
????????recordParamsSetController.getParamsGetResultCallBack().onFailure(e);
????}


}

?????

????#2.定義設(shè)置控制器

????????? 控制器的好處:

? ? ? ? ? 1)通過調(diào)整設(shè)置器的add順序,可以輕松調(diào)整單項設(shè)置的先后順序。(想想如果用的是多層嵌套,那將是一場災(zāi)難。)

????????? 2)避免了多層嵌套回調(diào)

????????? 3)隔離了用戶與設(shè)置器

????????

/**
?*?Created?by?XinYi?on?2018/12/20.
?*?記錄儀參數(shù)設(shè)置控制器
?*/
public?class?RecordParamsHandleController?{
????private?static?final?String?TAG?=?"RecordParamsHandleContr";
????private?int?nextSetterIndex?=?0;???????????????????????//標記當前處理者的索引
????private?List<BaseSingleRecordParamHandler>?singleRecordParamSetterList?=?new?ArrayList<>();
????private?static?final?RecordParamsHandleController?ourInstance?=?new?RecordParamsHandleController();
????private?ParamsSetResultCallBack?paramsSetResultCallBack;
????private?ParamsGetResultCallBack?paramsGetResultCallBack;
????private?String?collectedParams?=?"";


????public?static?RecordParamsHandleController?getInstance()?{
????????return?ourInstance;
????}

????private?RecordParamsHandleController()?{

????}


????/**
?????*
?????*?@param?param???0:參數(shù)獲取或者設(shè)置標志??????1:正式參數(shù)
?????*/
????public?void?init(String?param[]){
????????nextSetterIndex?=?0;
????????collectedParams?=?"";
????????//注意下面的add是有順序的
????????singleRecordParamSetterList.clear();
????????singleRecordParamSetterList.add(RecordResolutionParamHandler.getInstance(this));
????????singleRecordParamSetterList.add(RecordMicParamHandler.getInstance(this));
????????singleRecordParamSetterList.add(RecordLoopParamHandler.getInstance(this));
????????singleRecordParamSetterList.add(RecordGsensorParamHandler.getInstance(this));
????????singleRecordParamSetterList.add(AMapParamHandler.getInstance(this));
????????singleRecordParamSetterList.add(ScreenIntensityParamHandler.getInstance(this));
????????singleRecordParamSetterList.add(RecordDurationParamHandler.getInstance(this));
????????singleRecordParamSetterList.add(ScreenSleepParamHandler.getInstance(this));
????????singleRecordParamSetterList.add(ResetTFCardParamHandler.getInstance(this));
????????singleRecordParamSetterList.add(ResetSystemParamHandler.getInstance(this));
????????singleRecordParamSetterList.add(RecordOsdParamHandler.getInstance(this));

????????LogController.d(TAG,?"init:?len?=?"?+?param.length);
????????//設(shè)置參數(shù)
????????if(param.length?>?1){
????????????for?(int?i?=?2;?i?<?param.length;?i++)?{
????????????????LogController.d(TAG,?"init:?i?=?"?+?i);
????????????????singleRecordParamSetterList.get(i-2).setParam(param[i]);
????????????}
????????}
????}

????/**
?????*?開始設(shè)置參數(shù)
?????*?@param?paramsSetResultCallBack
?????*/
????public?void?startSetParams(ParamsSetResultCallBack?paramsSetResultCallBack){
????????this.paramsSetResultCallBack?=?paramsSetResultCallBack;
????????singleRecordParamSetterList.get(0).handleSetParam();
????}


????/**
?????*?開始獲取參數(shù)
?????*?@param?paramsGetResultCallBack
?????*/
????public?void?startGetParams(ParamsGetResultCallBack?paramsGetResultCallBack){
????????this.paramsGetResultCallBack?=?paramsGetResultCallBack;
????????singleRecordParamSetterList.get(0).handleGetParam();
????}

????protected?BaseSingleRecordParamHandler?getNextSingleRecordParamHandler(){
????????nextSetterIndex++;
????????if(nextSetterIndex?+?1?>singleRecordParamSetterList.size()){
????????????return?null;
????????}
????????return?singleRecordParamSetterList.get(nextSetterIndex);
????}

????public?interface??ParamsSetResultCallBack{
????????void?onSuccess();
????????void?onFailure(Exception?e);
????}

????public?interface??ParamsGetResultCallBack{
????????void?onSuccess(String?param);
????????void?onFailure(Exception?e);
????}


????public?ParamsSetResultCallBack?getParamsSetResultCallBack()?{
????????return?paramsSetResultCallBack;
????}

????public?ParamsGetResultCallBack?getParamsGetResultCallBack()?{
????????return?paramsGetResultCallBack;
????}

????public?void?collectParams(String?param){
????????collectedParams?+=?param;
????}

????public?String?getCollectedParams()?{
????????return?collectedParams;
????}
}



向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI