顯示具有 Thread 標籤的文章。 顯示所有文章
顯示具有 Thread 標籤的文章。 顯示所有文章

2018年2月12日 星期一

【Android】實際使用Socket傳輸資料

這是在「同一台裝置內的兩個不同APP/線程」要互傳資料時使用Socket的心得。

大致原則跟一般Java的Socket使用並無差異。


1.連線的兩頭都可以使用InetAddress這個物件內的static功能「getLocalHost」來取得IP,不然很有可能會失敗。

2.建議要有一頭使用ServerSocket來當聆聽連線請求端。

3.Socket建立後第一件事是「在迴圈內容重複檢查Connect是否已經建立起來」,因為InputStream/OutputStream本身不會檢查,資料丟進Stream中,即使另一端沒有東西接收也會執行。──建立一個while迴圈,重複檢查Socket的「isConnected」是否傳出false,如果是false就重複執行迴圈,直到是true為止。

4.連線完成務必關閉Stream和Socket。(ServerSocket本身不用關閉,要關閉從ServerSocket執行「accept」後取得的Socket。)

其餘細節跟標準Java Socket和Java Stream的使用沒有差別。

2017年9月8日 星期五

【J2EE】如何設計「Server要固定執行的工作」:ServletContextListener、Timer和TimerTask

ServletContextListener是種生命週期監視器。

寫好以後要在web.xml中用「<listener><listener-class>[ServletContextListener實作物件]</listener-class></listener>這組標籤完成設定。

裏頭主要有兩組介面, 「contextDestroyed(ServletContextEvent arg0)」和「contextInitialized(ServletContextEvent arg0)」,前者會在Server關閉或重開時被呼叫執行,後者則是啟動時被呼叫執行。


一般來說,要設定「Server要固定執行的工作」,要先使用這組功能在Server啟動時設定Timer的Schedule。

TimerTask就好像是「工作」的內容,Timer就是固定執行的時段,使用ServletContextListener就是將這兩者啟動並結合。(似乎也可以用一般Request的方式啟動,程式功能上完全沒問題。)


 Timer物件宣告方式很簡單。TimerTask是個抽象介面,但就是個Runnable。

 兩者都取得宣告後,Timer有組函數「schedule(TimerTask arg0, Date arg1, long arg2)」和「schedule(TimerTask arg0, long arg1, long arg2)」,這分別可以設定「TimerTask」(要執行的工作內容)、「Date」(具體啟動的日期),還有「距離現在多少毫秒後啟動」和「多少毫秒後重複執行」。

(「距離現在多少毫秒後啟動」又被稱為「DelayTime」。)

2016年5月10日 星期二

關於ActivityLifeCycleCallback

API文件在此。

這東西很有趣。

如果沒有它,「將共用資料擺在Application」這樣的認知或建議其實不太好用。



不過這是「我」。我認為應該要將Activity模組化後,統一套用「讀取或導入資料」的流程。

但是很多人只是需要「有個地方取用資料」。

將資料存在Application中後,用getApplication()取出即可......

很合理很有效率的想法!

但假設「不合用」或「不夠用」吧!

那我們就需要考慮一下LifeCycleCallback的用法。



這是個介面,裡面有完整的相對應了所有Activity LifeCycle的函數。

每個Activity的LifeCycle函數中,都會先在super()中執行所有被Application註冊的Callback,然後「才是」設計師複寫的物件內容。(LifeCycle不是建構子,其實設計師可以隨意調整super()執行的位置。)

(注意!是所有被註冊了的Callback都會被執行!)

(再注意!不是只有Application有能力註冊Callback,其他類別也有能力;相對的,也不是只有Application有能力反註冊Callback。因此一個Callback運作的開始和結束是很有彈性的!不會比Service差,但又比Service精準!)



所以假設......純假設.......

Callback中會在onResume啟動一個Thread,然後在onStop中將Thread關閉。(偶爾會發生從某一個Activity A返回前一個Activity時,Activity A的onDestroy會在下一個Activity的onResume之後才運作,這種時候可能會產生意外!如果允許Callback內產生多個Thread,那該關閉哪個Thread可能就是個要頭痛一下的問題了!...不是無解,也不複雜。)

這樣就可以達到讓背景任務可以順利跨Activity運行、而且又能對應當下的Activity產生變化。(例如視頻撥放的功能,可以將Activity視為「視頻線上存放點」的切換或記錄器,而下載功能和下載資料緩存則建立在Callback中,等於達到常駐的效果,而不會隨著每個Activity的啟動或關閉而有資源管理的問題。...這只是初步構想,還沒實驗過。)



再假設......純假設.......

每個Callback類別都有對應的Interface可以讓Activity實作,然後在Application中註冊多個Callback,只是每個Callback在執行對應的生命週期函數以前,都會檢查傳入的Activity是否有實作自己對應的Interface...如果沒有就不執行,那就可以大幅簡化Activity要處理的工作。



但對我來說,這個介面真正實用之處在於它讓一個跨「多Activity」甚至「多APP」的Thread可以精準的知道「何時有Activity可以取用」。

舉例...

就假設要設計個最簡單的APP內建有獨立邏輯和功能的計時器吧!

計時流程如果要精準可靠,那就不能因為Activity切換而損失效能或時間失準。(注意!它有獨特邏輯,所以不是直接把手機時間拿出來顯示就好。)所以不能採用「每個Activity獨立啟動Thread」的做法。

但在UI1中,時間顯示在右上角,在UI2中,顯示在正中央,而UI3卻又顯示在左下角...所以上面說要記得設定一個Interface讓每個Activity設定自己顯時間值的方法,如果有這樣做,就可以將這個Thread寫在一個Callback中,讓Application在產生Acitivity前就先運行這個Thread。



研究完用法思考了一下...

凡事要在背景常駐處理的都寫個Service...會不會對系統負擔太大呢!

畢竟Service是個功能完整的Context!但LifeCycleCallback只是個簡化介面的實作。

如此一來,Android預設的幾個主要功能類別其實就越來越模糊了!遵守Activity、Service、BroadCast...這類的類別去設計規劃APP的架構,可能反而會是個阻礙。

但同樣的理由...當一隻APP背景存在數十個額外設定的Callback時,效能是不是一樣會變差?

所以設計一項服務真的要把各個功能切割成多個APP(其中有一個是所謂的Main APP)來完成,這樣每個APP前景背景都會變得很單純穩定!

2016年5月9日 星期一

Activity無法接觸的非主線呈Thread可主動更新Activity內容

會特地寫這篇心得,是因為Handler Leaking一直困擾我。

在Activity可直接控制的範圍內觸發的Thread,可以用Handler.obj的方式來解決。

但如果是Activity完全接觸不到的Thread內,甚至是「第三方APP」內的Thread,這時要怎麼將Thread工作的結果回傳到Activity中?

在Guide文件「Display a location address」中曾經短暫提到的一個物件「ResultReciever」就是被設計來解決這個問題的工具。



問題是按照Google文件一慣的原則,它們完全不認為這種概念需要獨立成一個章節來介紹,仔細查了一下...確實只有這篇Guide文件有提到過這個物件,剩下的就是要進入個別API說明才會發現了。



這東西其實很簡單...

ResultReciever物件是個Parcelable介面的實作,將它當作參數傳入Intent,再經由Intent傳入獨立Thread或IntentService中,就可以在Thread結束時,(似乎是有能力偵測這個線程結束被關閉,)執行使用者實作的onRecieverResult(int code, Bundle data)中的內容。

(若是Activity可以直接控制影響的範圍,大可以直接啟動,不需要經由Intent。談到Intent,自然就是跨Activity,而且兩個Activity之間無法互通互相影響的情況。)



但是我原始的想法是在ActivityA中設置一個Intent Reciever,(通常是BroadCastReciever,)然後讓Activity接觸不到的Thread將工作結果也用Intent回傳。

如果有這個機制,這個方法就顯得很笨重多餘。

2016年4月28日 星期四

【Android】開啟第三方APP

讓APP的內容或服務可以被其他APP開啟跟呼叫,在Android上是很重要的一項功能,──雖然沒什麼人真的在做。

因為技術上它有很多Google進階設計時模糊曖昧的地方!API或文件都沒有精準說明,也沒有範例可以參考。

所以下面的筆記只是個人使用心得,離參考文件還有很長的距離...



在AndroidManifest檔中加入的Activity設定值標籤內,可以再加入intent-filter的標籤。

預備要被第三方APP呼叫的Activity中,必須要在intent-filter標籤內加入額外的參數。(會這樣說是因為基本用法中,除了靠點擊啟動的Activity需要加入main和launcher參數以外,其餘的Activity並不需要加入額外的參數,甚至連intent-filter都可以不需要。)


<intent-filter>
     <action android:name="android.intent.action.SEND"/>
     <action android:name="android.intent.action.VIEW"/>
     <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
SEND跟VIEW似乎只要擇一加入即可,差別只在於Activity中如果有UI要顯示,「使用」這個Activity的Intent中若是傳入SEND,則這個UI不會被顯示。

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//!!!關鍵在這個Flag
intent.addCategory(Intent.CATEGORY_DEFAULT);           
ComponentName cn = new ComponentName("tw.android.demo2", "tw.android.demo2.MainActivity2");           
intent.setComponent(cn);
startActivity(intent);

其實呼叫第三方APP的Activity方法很多,但關鍵應該在於addFlag的動作!

(還沒機會使用到其他型態的Flag,如果有機會用到,會補上結果。)

另外,ComponentName的實體建構子中要傳入兩個參數,一個是第三方APP project名稱,一個則是Activity的完整專案路徑與名稱。

(會強調完整專案路徑與名稱,是因為邏輯上來說,Activity可以分散在多個子project路徑內,例如tw.android.demo2下可以有tw.android.demo2.test,但將Activity放在裏頭,不知道為何AndroidManifest檔內的設定會失敗...一直沒有機會來回頭釐清這一段的正確使用方式和原則。──好像有點本末倒置,不去先釐清更基本的東西,卻在研究比較進階的使用方法。)




另一個奇妙的問題是...當Activity被啟動後,啟動的目的為何?

絕對不是僅僅為了看個UI介面。(有時候會有選單來選擇資料回傳,這時候當然就是要操作UI。但這裡不是指這種情況。)

例如要使用不對外公開的API來跟後台交換資料,這時候就需要建立Thread來完成網路傳輸工作。

奇妙的是...

這種讓第三方啟動的Activity竟然無法在基本生命週期階段中啟動獨立Thread。(寫了個Thread程式,卻無法被啟動,但也沒看到錯誤訊息產生。)

所以這時候就需要使用Service,特別推薦IntentService。(前者可能要等任務完成、關閉Activity後才會跟著關閉,但後者會先在任務完成後關閉自己、同時執行關閉Activity的工作。)



........

怎麼使用IntentService?

別鬧了!去別的地方找資料。

2015年8月6日 星期四

關於Thread多線程

過去幾個禮拜在做「在Google Map上記錄使用者行走路徑」的功能...

(基於業務需求,不能附上詳細的程式碼。)

製作的過程中碰到最重要的幾個問題:

1.GPS的精準度和效率。
2.如何在行走過程中,開啟和關閉地圖,(地圖只是記錄行走路徑的附屬功能,可以獨立開啟跟關閉,)但又不會影響紀錄的效率和整體操作的流暢性。
3.紀錄行走的路徑資料量吃光了記憶體。

關於1,結果有點無解。

關於3,用即時的SQL讀寫來解決。

關於2,解決的過程中刷新了我對Thread的應用和設計心得.......




Thread的意義在於「不要讓單一項工作/任務吃光所有CPU的運算資源」,所以它會建議設計師在迴圈或程式碼中埋下Thread.sleep,來讓目前的工作/任務先暫時停止工作、把結果保留下來,然後處理其他工作。



我個人接觸Thread的初體驗是遊戲設計中的「畫面更新」功能。

畫面更新速率要穩定!(不穩定可以視為Thread設計失敗!)

但這是「畫面更新」的要點,而不是「Thread設計」的基本技巧,導致我一直以來在Thread的設計上,都會將Thread設計得過於僵硬強勢!──Thread結構都類似「畫面更新」的「無止盡的執行下去、直到程式關閉為止。」(這樣的Thread執行或許很穩定,但卻很難關掉、很難即刻反映APP的生命週期或開關。)

我想這是因為遊戲中都會將要運算和更新的資料量控制在一個穩定的範圍內,例如敵人數量要穩定、圖層數量要穩定。

所以不管是遊戲數值的運算、或畫面的更新,這兩個Thread的運算效率通常都會很穩定。(偶爾會有「爆擊」、「連續技」、「超大範圍攻擊」等事件發生,.......能否處理好就看工程師的功力了。)



但這次在更新地圖路徑的繪圖需求上,卻是完全彈性的!

資料量從一開始可能十筆二十筆,在五分鐘後會爆增到一百筆兩百筆,再十分鐘後又會增加到八百筆九百筆。

資料的量非常不穩定,所以「遊戲軟體」中「畫面更新」的Thread設計原則必然不適用!

將「數據運算」和「畫面更新」做成兩個完全平行、且穩定持續運作的Thread並不適合在這個運算資料量一直增加的設計中。



所以兩個Thread要從平行改為主從式設計。

一個Thread為穩定持續運作,另一個則被前面的Thread動態的生成啟動或關閉,...但在我這次的工作中,只有啟動、不需要關閉。(「畫面更新」Thread不需要循環執行,單一的功能執行完、則整個Thread關閉丟入記憶體回收列中.....,下次要更新畫面時,重啟一個Thread。)

這還是需要一些參數上的設計,例如「畫面更新開始」和「畫面更新結束」,畫面更新如果開始,就主Thread就不會再發出畫面更新的Thread,直到畫面更新結束為止。

(其實這是可以同一個參數解決的。)



這樣設計後,畫面的更新效率或許不太理想,時不時「使用者當下位置」跟「路徑線」會有點差距.......

但APP的整體操作完全不受影響。