1.Android Lint工具html
項目一期需求開發告一段落,爲了讓代碼更好,須要進一步去優化代碼結構和質量。java
a.是Android Studio提供的代碼掃描分析工具,在不運行程序或者寫任何測試用例的狀況下,幫助發現代碼結構和質量問題,並提供一些解決方案。安全
b.工做流程:根據預先配置的檢測標準檢查項目的源文件,發現潛在bug和可優化代碼,並將掃描結果顯示在控制檯或者Android Studio中的Event Log裏,流程圖以下:bash
其中,圖中幾個名詞含義:多線程
c.使用方法:這裏直接用Android Studio的GUI操做併發
step1:Lint使用路徑爲『工具欄 -> Analyze -> Inspect Code…』app
step2:設定要檢測的源文件ide
默認選項是Whole project(整個項目),還能夠選擇Custom scope自定義文件,有多個選項:Project Files(全部項目文件)、Project Production Files(項目的代碼文件)、Project Test Files(項目的測試文件)、Open Files(當前打開的文件)、Module 'app'(主要的ap 模塊)、Current File(當前文件)。函數
固然,還能選擇特定的文件,選擇右側的'...'高併發
點擊左上角'+'添加一個檢查範圍,可選項有Local(只能當前項目使用)和Shared(其餘項目也可以使用)。
這裏選擇Shared,會提示起規則名。
根據右側四個按鈕的提示:Include(包括當前文件夾內的文件,但不包括其子文件夾)、Include Recursively(包括當前文件夾,及其子文件夾內全部的文件夾)、Exclude(移除當前文件夾,但不包括子文件夾)、Exclude Recursively(移除當前文件夾、及其全部子文件夾),來制定規則。
這裏對app選擇了Include Recursively,可看到該文件夾及其子文件夾全部文件都被選中了,而且變成了綠色,右上角提示總共有385個文件夾要掃描。
step3:確認完成,等待檢測結果
結果顯示在底部Inspection對話框,能夠看到,除了以前說起的六個,還有Class structure(類結構,如全局變量可替換爲局部變量等)、Code maturity issues(代碼成熟度問題,如使用棄用的方法等)、Code style issues(代碼風格問題,如不必的符號等)、Compiler issues(編譯器問題,如集合缺乏泛型符號)、Control flow issues(控制流問題,如去掉不必的if/else語句)、Data flow issues(數據流問題,如無用的局部變量)、Declaration redundancy(聲明冗餘,如方法返回值可爲void等)、Error handling(錯誤處理,如異常處理等)、Spelling(拼寫)等等,只要打開相應的選項卡,就能夠在右側對話框看到具體描述和問題所在了。
推薦閱讀:Lint常見的問題及解決方案
2.SimpleDateFormat
a.SimpleDateFormat是一個用於格式化和分析數據的具體類。繼承關係以下:
Java.lang.Object
|
+----java.text.Format
|
+----java.text.DateFormat
|
+----java.text.SimpleDateFormat
複製代碼
b.經常使用方法:
先用其構造函數SimpleDateFormat(String str)
構造一個格式化日期的格式,再經過它的format(Date date)
將指定的日期對象格式化爲指定格式的字符串。
假設格式化日期的形式爲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.使用注意:
在阿里開發手冊中一段關於SimpleDateFormat的規範:
【強制】SimpleDateFormat是線程不安全的類,通常不要定義爲static變量,若是定義爲static,必須加鎖,或者使用DateUtils工具類。
緣由分析:在SimpleDateFormat內部持有一個Calendar對象的引用,若是把SimpleDateFormat定義爲static,可能存在多個Thread同時共享它,即共享對這個Calendar的引用。在高併發狀況下,易出現幻讀成員變量的現象。
//解決方案一:定義爲局部變量
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的類型:
//建立一個單一線程池:線程以隊列順序來執行。
ExecutorService threadPool = Executors.newSingleThreadExecutor();
複製代碼
//建立一個定長線程池,超出的線程會在隊列中等待。
ExecutorService threadPool = Executors.newFixedThreadPool(2);
複製代碼
//建立一個定長線程池,支持定時及週期性任務執行。
ExecutorService threadPool = Executors.newScheduledThreadPool(3);
複製代碼
//建立一個無界線程池:可進行線程自動回收,可存放線程數量最大值爲Integer.MAX_VALUE
ExecutorService threadPool = Executors.newCachedThreadPool();
複製代碼
b.在阿里開發手冊中說明了Executors各個方法的弊端:
newFixedThreadPool
和newSingleThreadExecutor
:主要問題是堆積的請求處理隊列可能會耗費很是大的內存,甚至OOM。newCachedThreadPool
和newScheduledThreadPool
:主要問題是線程數最大數是Integer.MAX_VALUE,可能會建立數量很是多的線程,甚至OOM。
c.解決方式:改用ThreadPoolExecutor建立線程池,便於明確線程池的運行規則,規避資源耗盡的風險。
其中,Executor
、ThreadPoolExecutor
、ScheduledExecutorService
和ScheduledThreadPoolExecutor
關係以下:
java.util.concurrent.Executor : 負責線程的使用與調度的根接口
|–ExecutorService:Executor的子接口,線程池的主要接口
|–ThreadPoolExecutor:ExecutorService的實現類
|–ScheduledExecutorService:ExecutorService的子接口,負責線程的調度
|–ScheduledThreadPoolExecutor:繼承了ThreadPoolExecutor實現了ScheduledExecutorService
複製代碼
d.ThreadPoolExecutor的構造函數:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
複製代碼
其中,各個參數的具體含義詳見要點提煉|開發藝術之線程,以及有關拒絕策略。
而後根據具體需求來建立ThreadPoolExecutor對象,並提供一系列參數來配置線程池便可。而不是直接用有默認配置的Executors建立,事實上看過源碼也知道Executors底層也是經過ThreadPoolExecutor去實現的。
推薦閱讀:JDK 源碼解析--Executors、ExecutorService、ThreadPoolExecutor 線程池
4.定時任務之Timer、ScheduledThreadPoolExecutor
a.Timer的常見使用:
//延遲2s後執行timer定時器內的任務
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
//do something
}
}, 2000);
複製代碼
//延遲2s後執行timer定時器內的任務,以後每隔1s執行一次
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
//do something
}
}, 2000,1000);
複製代碼
b.Timer管理延時任務的缺陷:
解決辦法:JDK5.0後推薦使用ScheduledThreadPoolExecutor代替Timer,顧名思義,ScheduledThreadPoolExecutor內部重用線程池,使用了多線程,使得單個任務的執行不會影響其餘線程。
c.ScheduledThreadPoolExecutor的常見使用:
//延遲2s後執行timer定時器內的任務,以後每隔1s執行一次,單位爲毫秒
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);//建立大小爲2的線程池
executor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
//do something
}
}, 2000, 1000, TimeUnit.MILLISECONDS);
複製代碼
其中,間隔單位的參數有:
TimeUnit.MILLISECONDS
:毫秒TimeUnit.SECONDS
:秒TimeUnit.MINUTES
:分鐘TimeUnit.HOURS
:小時TimeUnit.DAYS
:天推薦閱讀:深刻理解Java線程池:ScheduledThreadPoolExecutor
5.OnTouchListener、OnClickListener的衝突
a.優先度: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()
或對自定義控件重寫onTouchEvent()
會出現警告:
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方法或者設置了OnTouchListener,但沒有實現performClick並在檢測到點擊時調用它,view可能沒法正確處理輔助操做。理想狀況下,處理點擊操做的邏輯應放在View#performClick中,由於某些輔助功能服務會在發生單擊操做時調用performClick。
c.緣由:onClick()
會經過performClick()
完成點擊事件的,而在onTouch()
和onTouchEvent()
的ACTION_UP
過程當中會啓用一個新的線程來調用performClick()
,於是可能會屏蔽掉onClick()
中設置的事件。
d.解決辦法:
setOnTouchListener()
,那麼就在重寫onTouch()
的ACTION_UP
狀況下調用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:
//如下爲添加內容
button.performClick();
break;
}
return false;//若是返回true,因爲優先級較高,後面onTouchEvent、onClick將不會被觸發
}
});
複製代碼
onTouchEvent()
,那麼就在ACTION_UP
狀況下調用performClick()
。@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_CANCEL:
break;
//如下爲添加內容
case MotionEvent.ACTION_UP:
performClick();
break;
}
return true;
}
複製代碼
6.一點小感悟
這周的北京莫名的涼快,從上週末開始就斷斷續續的下了好幾場雨,相比於烈日,仍是喜歡清爽的雨吧。工做的節奏也彷佛跟着淅瀝瀝的雨慢了下來,後臺也纔跟着上線,距離下一期的開發還有些時日,也就空出一段可貴不被人打擾的日子,作了些對上一期代碼質量和性能評價總結等收尾的事情,在這種時候發現到的問題反倒讓本身學的更多、成長的更快。
分享一波開發流程,本身也參與到了大部分的過程,雖然沒有寫很厲害的代碼,可是能體驗一波完整的開發流程也是收穫頗多呢!以及不得不知的互聯網職位縮寫含義。
轉眼也來北京一個多月了,除了頭幾天不太適應北京夏日的高溫、以及從未見過如此擁擠的地鐵以外,好像就沒有特別的感概,時而恍惚覺得本身仍在那個美麗的濱海城市大連了,大連在個人眼裏也是如此的繁華和熱鬧。
不過很奇怪的是,貌似很多人還覺得春秋招只和應屆生有關係,聽聞我未畢業就實習都一副吃驚的樣子。事實上,如今一年一度的春招,爲了能在秋招前提早鎖定一批優秀人才,企業的重心反而是在面向大三/研二學生的暑期實習上,這就是爲何暑期實習的招聘流程和秋招幾乎一致,要求也是較高的。
可能得益於學校有意識的培養,身邊有不少同窗都在北京各大互聯網公司實習,還有一些老同窗在北京讀書,又趕上那麼多的可愛的小夥伴,因此只不過換了個地方繼續生活,有何談孤單呢!