本周知識清單如下:
- Android Lint工具
- SimpleDateFormat
- 線程池之Executors羊精、ThreadPoolExecutor
- 定時任務(wù)之Timer云挟、ScheduledThreadPoolExecutor
- OnTouchListener硼控、OnClickListener的沖突
- 一點小感悟
1.Android Lint工具
項目一期需求開發(fā)告一段落,為了讓代碼更好框仔,需要進一步去優(yōu)化代碼結(jié)構(gòu)和質(zhì)量机打。
a.是Android Studio提供的代碼掃描分析工具,在不運行程序或者寫任何測試用例的情況下猿棉,幫助發(fā)現(xiàn)代碼結(jié)構(gòu)和質(zhì)量問題磅叛,并提供一些解決方案。
b.工作流程:根據(jù)預(yù)先配置的檢測標準檢查項目的源文件铺根,發(fā)現(xiàn)潛在bug和可優(yōu)化代碼宪躯,并將掃描結(jié)果顯示在控制臺或者Android Studio中的Event Log里,流程圖如下:
其中位迂,圖中幾個名詞含義:
- App Source Files:包括Java代碼访雪、XML代碼、icon及ProGuard配置文件等
- lint.xml:Lint檢測的執(zhí)行標準配置文件
- lint Tool:靜態(tài)代碼掃描工具掂林,可運行在命令行或者Android Studio中
- Correctness:正確性臣缀,如硬編碼、使用過時API等
- Usability :可用性泻帮,如不在文本字段上指定輸入的類型等
- Security: 安全性精置,如WebView中允許使用JavaScriptInterface等
- Accessibility :可達性,如圖片沒有使用contentDescription等锣杂。
- Performance: 性能脂倦,如靜態(tài)引用番宁、循環(huán)引用等
- Internationalization:國際化,如直接使用漢字赖阻、沒有使用資源引用等
c.使用方法:這里直接用Android Studio的GUI操作
step1:Lint使用路徑為『工具欄 -> Analyze -> Inspect Code…』
step2:設(shè)定要檢測的源文件
默認選項是Whole project(整個項目)蝶押,還可以選擇Custom scope自定義文件,有多個選項:Project Files(所有項目文件)火欧、Project Production Files(項目的代碼文件)棋电、Project Test Files(項目的測試文件)、Open Files(當前打開的文件)苇侵、Module 'app'(主要的ap 模塊)赶盔、Current File(當前文件)。
當然榆浓,還能選擇特定的文件于未,選擇右側(cè)的'...'
點擊左上角'+'添加一個檢查范圍,可選項有Local(只能當前項目使用)和Shared(其他項目也可使用)哀军。
這里選擇Shared沉眶,會提示起規(guī)則名。
根據(jù)右側(cè)四個按鈕的提示:Include(包括當前文件夾內(nèi)的文件杉适,但不包括其子文件夾)、Include Recursively(包括當前文件夾柳击,及其子文件夾內(nèi)所有的文件夾)猿推、Exclude(移除當前文件夾,但不包括子文件夾)捌肴、Exclude Recursively(移除當前文件夾蹬叭、及其所有子文件夾),來制定規(guī)則状知。
這里對app選擇了Include Recursively秽五,可看到該文件夾及其子文件夾所有文件都被選中了,并且變成了綠色饥悴,右上角提示總共有385個文件夾要掃描坦喘。
step3:確認完成,等待檢測結(jié)果
結(jié)果顯示在底部Inspection對話框西设,可以看到瓣铣,除了之前提及的六個,還有Class structure(類結(jié)構(gòu)贷揽,如全局變量可替換為局部變量等)棠笑、Code maturity issues(代碼成熟度問題,如使用棄用的方法等)禽绪、Code style issues(代碼風(fēng)格問題蓖救,如沒必要的符號等)洪规、Compiler issues(編譯器問題,如集合缺少泛型符號)循捺、Control flow issues(控制流問題淹冰,如去掉沒必要的if/else語句)、Data flow issues(數(shù)據(jù)流問題巨柒,如無用的局部變量)樱拴、Declaration redundancy(聲明冗余,如方法返回值可為void等)洋满、Error handling(錯誤處理晶乔,如異常處理等)、Spelling(拼寫)等等牺勾,只要打開相應(yīng)的選項卡正罢,就可以在右側(cè)對話框看到具體描述和問題所在了。
推薦閱讀:Lint常見的問題及解決方案
2.SimpleDateFormat
a.SimpleDateFormat是一個用于格式化和分析數(shù)據(jù)的具體類驻民。繼承關(guān)系如下:
Java.lang.Object
|
+----java.text.Format
|
+----java.text.DateFormat
|
+----java.text.SimpleDateFormat
b.常用方法:
先用其構(gòu)造函數(shù)SimpleDateFormat(String str)
構(gòu)造一個格式化日期的格式翻具,再通過它的format(Date date)
將指定的日期對象格式化為指定格式的字符串。
假設(shè)格式化日期的形式為yyyyy-MM-dd hh:mm:ss a E
回还,含義:
-
yyyyy
:年裆泳,可匹配如 : 2018。Y和y都表示年柠硕。 -
MM
:月工禾,可匹配如 : 07。 -
dd
:日蝗柔,可匹配如13闻葵。 -
hh
:時,可匹配如08癣丧。注意:大寫H表示24進制計時槽畔,小寫h表示12進制計時。 -
mm
:分胁编,可匹配如23厢钧。 -
ss
:秒,可匹配如18掏呼。 -
a
:上/下午坏快,上午為AM、下午為PM憎夷。 -
E
:星期莽鸿,可匹配如Fri。
c.使用注意:
在阿里開發(fā)手冊中一段關(guān)于SimpleDateFormat的規(guī)范:
【強制】SimpleDateFormat是線程不安全的類,一般不要定義為static變量祥得,如果定義為static兔沃,必須加鎖,或者使用DateUtils工具類级及。
原因分析:在SimpleDateFormat內(nèi)部持有一個Calendar對象的引用乒疏,如果把SimpleDateFormat定義為static,可能存在多個Thread同時共享它饮焦,即共享對這個Calendar的引用怕吴。在高并發(fā)情況下,易出現(xiàn)幻讀成員變量的現(xiàn)象县踢。
//解決方案一:定義為局部變量
private static final String FORMAT = "yyyy-MM-dd HH:mm:ss";
public String getFormat(Date date){
SimpleDateFormat sdf = new SimpleDateFormat(FORMAT);
return sdf.format(date);
//解決方案二:加鎖
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void getFormat(){
synchronized (sdf){
sdf.format(new Date());
….;
}
//解決方案三:使用ThreadLocal使得每個線程都有自己的SimpleDateFormat對象
private static final ThreadLocal<DateFormat> df = newThreadLocal<DateFormat>() {
@Override
protected DateFormatinitialValue() {
return newSimpleDateFormat("yyyy-MM-dd");
}
};
推薦閱讀:SimpleDateFormat線程不安全及解決辦法
3.線程池之Executors转绷、ThreadPoolExecutor
a.Executors的類型:
//創(chuàng)建一個單一線程池:線程以隊列順序來執(zhí)行。
ExecutorService threadPool = Executors.newSingleThreadExecutor();
//創(chuàng)建一個定長線程池硼啤,超出的線程會在隊列中等待议经。
ExecutorService threadPool = Executors.newFixedThreadPool(2);
//創(chuàng)建一個定長線程池,支持定時及周期性任務(wù)執(zhí)行谴返。
ExecutorService threadPool = Executors.newScheduledThreadPool(3);
//創(chuàng)建一個無界線程池:可進行線程自動回收煞肾,可存放線程數(shù)量最大值為Integer.MAX_VALUE
ExecutorService threadPool = Executors.newCachedThreadPool();
b.在阿里開發(fā)手冊中說明了Executors各個方法的弊端:
newFixedThreadPool
和newSingleThreadExecutor
:主要問題是堆積的請求處理隊列可能會耗費非常大的內(nèi)存,甚至OOM嗓袱。newCachedThreadPool
和newScheduledThreadPool
:主要問題是線程數(shù)最大數(shù)是Integer.MAX_VALUE籍救,可能會創(chuàng)建數(shù)量非常多的線程,甚至OOM索抓。
c.解決方式:改用ThreadPoolExecutor創(chuàng)建線程池钧忽,便于明確線程池的運行規(guī)則,規(guī)避資源耗盡的風(fēng)險逼肯。
其中,Executor
桃煎、ThreadPoolExecutor
篮幢、ScheduledExecutorService
和ScheduledThreadPoolExecutor
關(guān)系如下:
java.util.concurrent.Executor : 負責(zé)線程的使用與調(diào)度的根接口
|–ExecutorService:Executor的子接口,線程池的主要接口
|–ThreadPoolExecutor:ExecutorService的實現(xiàn)類
|–ScheduledExecutorService:ExecutorService的子接口为迈,負責(zé)線程的調(diào)度
|–ScheduledThreadPoolExecutor:繼承了ThreadPoolExecutor實現(xiàn)了ScheduledExecutorService
d.ThreadPoolExecutor的構(gòu)造函數(shù):
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
其中三椿,各個參數(shù)的具體含義詳見要點提煉|開發(fā)藝術(shù)之線程,以及有關(guān)拒絕策略奢入。
然后根據(jù)具體需求來創(chuàng)建ThreadPoolExecutor對象棒呛,并提供一系列參數(shù)來配置線程池即可司蔬。而不是直接用有默認配置的Executors創(chuàng)建,事實上看過源碼也知道Executors底層也是通過ThreadPoolExecutor去實現(xiàn)的蛋叼。
推薦閱讀:JDK 源碼解析--Executors、ExecutorService、ThreadPoolExecutor 線程池
4.定時任務(wù)之Timer狈涮、ScheduledThreadPoolExecutor
a.Timer的常見使用:
//延遲2s后執(zhí)行timer定時器內(nèi)的任務(wù)
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
//do something
}
}, 2000);
//延遲2s后執(zhí)行timer定時器內(nèi)的任務(wù)狐胎,之后每隔1s執(zhí)行一次
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
//do something
}
}, 2000,1000);
b.Timer管理延時任務(wù)的缺陷:
- Timer內(nèi)部只創(chuàng)建了一個線程歌馍,因此如果存在多個任務(wù)握巢,且任務(wù)時間過長,超過了兩個任務(wù)的間隔時間松却,會影響其他任務(wù)的執(zhí)行暴浦。
- Timer創(chuàng)建的線程沒有處理異常,因此當多線程并行處理定時任務(wù)時晓锻,Timer運行多個TimeTask時歌焦,只要其中之一沒有捕獲拋出的異常,其它任務(wù)便會自動終止運行带射。
解決辦法:JDK5.0后推薦使用ScheduledThreadPoolExecutor代替Timer同规,顧名思義,ScheduledThreadPoolExecutor內(nèi)部重用線程池窟社,使用了多線程券勺,使得單個任務(wù)的執(zhí)行不會影響其他線程。
c.ScheduledThreadPoolExecutor的常見使用:
//延遲2s后執(zhí)行timer定時器內(nèi)的任務(wù)灿里,之后每隔1s執(zhí)行一次关炼,單位為毫秒
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);//創(chuàng)建大小為2的線程池
executor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
//do something
}
}, 2000, 1000, TimeUnit.MILLISECONDS);
其中,間隔單位的參數(shù)有:
-
TimeUnit.MILLISECONDS
:毫秒 -
TimeUnit.SECONDS
:秒 -
TimeUnit.MINUTES
:分鐘 -
TimeUnit.HOURS
:小時 -
TimeUnit.DAYS
:天
推薦閱讀:深入理解Java線程池:ScheduledThreadPoolExecutor
5.OnTouchListener匣吊、OnClickListener的沖突
a.優(yōu)先度:onTouch()
>onTouchEvent()
>onClick()
//setOnTouchListener需要重寫onTouch()
bt.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//do something
return false;
}
});
//setOnClickListener需要重寫onClick()
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//do something
}
});
@Override
public boolean onTouchEvent(MotionEvent event) {
//do something
return super.onTouchEvent(event);
}
b.Warning:當對一個控件使用setOnTouchListener()
或?qū)ψ远x控件重寫onTouchEvent()
會出現(xiàn)警告:
If a View that overrides onTouchEvent or uses an OnTouchListener does not also implement performClick and call it when clicks are detected, the View may not handle accessibility actions properly. Logic handling the click actions should ideally be placed in View#performClick as some accessibility services invoke performClick when a click action should occur.
大意是:如果重寫了view的onTouchEvent方法或者設(shè)置了OnTouchListener儒拂,但沒有實現(xiàn)performClick并在檢測到點擊時調(diào)用它,view可能無法正確處理輔助操作色鸳。理想情況下社痛,處理點擊操作的邏輯應(yīng)放在View#performClick中,因為某些輔助功能服務(wù)會在發(fā)生單擊操作時調(diào)用performClick命雀。
c.原因:onClick()
會通過performClick()
完成點擊事件的蒜哀,而在onTouch()
和onTouchEvent()
的ACTION_UP
過程中會啟用一個新的線程來調(diào)用performClick()
,因而可能會屏蔽掉onClick()
中設(shè)置的事件吏砂。
d.解決辦法:
- 如果是使用
setOnTouchListener()
撵儿,那么就在重寫onTouch()
的ACTION_UP
情況下調(diào)用performClick()
。
bt.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
//以下為添加內(nèi)容
button.performClick();
break;
}
return false;//如果返回true狐血,由于優(yōu)先級較高淀歇,后面onTouchEvent、onClick將不會被觸發(fā)
}
});
- 如果是重寫
onTouchEvent()
匈织,那么就在ACTION_UP
情況下調(diào)用performClick()
浪默。
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_CANCEL:
break;
//以下為添加內(nèi)容
case MotionEvent.ACTION_UP:
performClick();
break;
}
return true;
}
6.一點小感悟
這周的北京莫名的涼快,從上周末開始就斷斷續(xù)續(xù)的下了好幾場雨,相比于烈日浴鸿,還是喜歡清爽的雨吧井氢。工作的節(jié)奏也似乎跟著淅瀝瀝的雨慢了下來,后臺也才跟著上線岳链,距離下一期的開發(fā)還有些時日花竞,也就空出一段難得不被人打擾的日子,做了些對上一期代碼質(zhì)量和性能評價總結(jié)等收尾的事情掸哑,在這種時候發(fā)現(xiàn)到的問題反倒讓自己學(xué)的更多约急、成長的更快。
分享一波開發(fā)流程苗分,自己也參與到了大部分的過程厌蔽,雖然沒有寫很厲害的代碼,但是能體驗一波完整的開發(fā)流程也是收獲頗多呢摔癣!以及不得不知的互聯(lián)網(wǎng)職位縮寫含義奴饮。
轉(zhuǎn)眼也來北京一個多月了,除了頭幾天不太適應(yīng)北京夏日的高溫择浊、以及從未見過如此擁擠的地鐵之外戴卜,好像就沒有特別的感概,時而恍惚以為自己仍在那個美麗的濱海城市大連了琢岩,大連在我的眼里也是如此的繁華和熱鬧投剥。
不過很奇怪的是,貌似不少人還以為春秋招只和應(yīng)屆生有關(guān)系担孔,聽聞我未畢業(yè)就實習(xí)都一副吃驚的樣子江锨。事實上,現(xiàn)在一年一度的春招糕篇,為了能在秋招前提前鎖定一批優(yōu)秀人才啄育,企業(yè)的重心反而是在面向大三/研二學(xué)生的暑期實習(xí)上,這就是為什么暑期實習(xí)的招聘流程和秋招幾乎一致拌消,要求也是較高的灸撰。
可能得益于學(xué)校有意識的培養(yǎng),身邊有很多同學(xué)都在北京各大互聯(lián)網(wǎng)公司實習(xí)拼坎,還有一些老同學(xué)在北京讀書,又遇上那么多的可愛的小伙伴完疫,所以只不過換了個地方繼續(xù)生活泰鸡,有何談孤單呢!