您好,登錄后才能下訂單哦!
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)系,得到
????? ?時間的方法也不一樣,所以對于每一個列表都要定義一個比較器。
???? ??
????? ?我在想,同樣比較的是時間,為什么要定義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進行排序:
???????
? 使用接口,本質(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í)行成功才算成功。
? ??????
????????
????? ? ?問題就來了,現(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ū)別
????????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的生成位置:
????????????????????? ?
????????????????默認也會生成jar文件,但是引用后發(fā)現(xiàn)里面只有R文件:
????????????????
????????????????那么,既然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? ?
????????????????????
? ? ? ? ? ? 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中
? ?? ? ?在構(gòu)造PaperListRequester這個類的時候,我將Context和接口需要的參數(shù)pid傳遞進去。定義了
一個監(jiān)聽器ParseListener,監(jiān)聽內(nèi)部數(shù)據(jù)解析的結(jié)果。通過一個方法getData,并傳遞一個監(jiān)聽器返回結(jié)果。
? ?這是調(diào)用PaperListRequester這個類請求接口的代碼:
?? ?將請求、解析接口的邏輯封裝在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多層異步嵌套,將會造成代碼閱讀性非常差。
????? ??
????????
????????于是我想到了“封裝”
????? ? #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; ????} }
免責聲明:本站發(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)容。