2018年1月25日 星期四

[Android] 監控網路連線狀態(Monitoring network connectivity status)

問題:
我的App想加入網路監控,當無法連線時要跳通知,連上線後通知要消失,要怎麼做比較好呢?

處理:Google 「android 網路連線偵測」 → Google 「android internet connection detect」

參考:
[網路(Network)偵測區]
DevAndroid-DetermineAndMonitorDevAndroid-isConnectedOrConnectingCSDN-isConnected_vs_isAvailableStackOverflow-isConnectedAlwaysFalseStackOverflow-isConnectedAlwayTrue
[網際網路(Internet)偵測區]
StackOverflow-NoInternetStackOverflow-PingHost
[監控(Monitor)及通知(Notify)前端區]
StackOverflow-PhoneStateListenerGadgetSaint-BroadcastReceiverMILKMIDI BLOG-BroadcastReceiverCC's 程式碼-BroadcatReceiver
[跳轉設定(Settings)]
簡書

說明:
這題可以分成幾部分:
  1. 如何確定有無網路?
  2. 如何偵測網路連線狀態改變且通知畫面端?
  3. 畫面端要怎麼處理?

其中,"1.如何確定有無網路?"

  • 有用"ConnectivityManager"跟"TelephonyManager"兩種方法,看DevAndroid推薦的是ConnectivityManager,大部分網友也是用這種,所以就只看這種。

  • ConnectivityManager裡面又有用isAvailable、isConnected、isConnectedOrConnecting、getState 這4種判斷是否連線,getState要自己寫判斷式就先跳過,另外三種只有isConnected是確定連上網路的,但看來有些機器會遇到問題誤判,所以用isConnectedOrConnecting

  • 那這樣就真的能跟主機連線了嗎?不一定,搞不好連到的網路其實不能連到你的主機或是網際網路,那怎辦? 用StackOverflow裡查到的法1 或 法2,在確定有網路連線後,連到特定站台(如果要確定自己站台可以,可以在自己站台放個小檔或測網用API),看是否會通,如果連的不是自己的站要小心,有些站台在特定國家連不到(像是Google在中國)

好了,接著"2.如何偵測網路連線狀態改變且通知畫面端?",

  • 有用"PhoneStateListener"跟"BroadcastReceiver"兩種,因為第1題選了ConnectivityManager,而且看DevAndroid跟大部分網友都是用BroadcastReceiver,所以就只看這種囉。

  • BroadcastReceiver的寫法跟用法可參考GadgetSaint 或 CC's 程式碼,注意喔,這兩個寫的通知畫面端方法不同,GadgetSaint用的是Listener(interface),CC用的是寫inner class 的Receiver(就可以在onReceive裡面處理Activity的東東);兩種寫法都記得在Activity裡registerReceiver,才能收到通知。

  • 如果不需要即時監控變化的話,也有些Dev是寫在每個Activity的onCreate測一次,或是每次連線前測一次而已,那就不用用到BroacastReceiver,只是要注意有沒有地方沒防呆該擋沒擋。
[更新] 
如果你的Target API在24以上(新專案應該都是了吧),然後在AndroidManifest裡加上<recevicer>裡面intent-filter是<action android:name = "android.net.conn.CONNECTIVITY_CHANGE" />的話,Android Studio會跳警告,說明在manifest註冊的CONNECTIVITY_CHANGE將不會收到通知,所以要不用在manifest裡加這個,在要用的Activity再註冊即可,可參考CC's程式碼,如果不想寫inner class的Receiver,那就加上interface,onReceive時用interface通知外面吧。

Android官方的建議是:參考這篇,用 JobScheduler(如果min>=21) 或 GcmNetworkManager,只是還沒時間研究,因為GCM的網頁又說請用FCM(無言,很多官網文章也不會即時更新的),所以min<21,就呵呵,反正單Activity註冊Receiver還能用,先這樣吧,有空研究再更新 。

接下來,"3.畫面端要怎麼處理?",看看其他Apps怎麼處理囉~
  • YouTube是在BottomBar下方,長出一個小View顯示網路狀態,且中間Fragment內容會變成無網路圖。

  • GMail是出現一個Snackbar顯示"未連線",Action是"重試"。

  • Facebook是出現一個Snackbar顯示"目前無法連線。",Action是"更多",點了"更多"是會問你要不要到"設定"。(可參考簡書做法)

  • Line是在Fragment上方呈現一個半透明小View,顯示"未連接至網路",右邊有個"x"可以關閉該通知。

  • PLAY是整個畫面呈現"沒有網際網路連線。請確定Wi-Fi行動數據已開啟,然後再試一次。",中間有個按鈕[重試],點Wi-Fi會到WiFi設定,點行動數據會到可用的網路設定。
可以看出大部分知名Apps都是採用持續監控網路狀態,且離線還是能進App操作,只是限制部分功能。

2018年1月22日 星期一

[Android] ImageView Out Of Memory Error

問題:
我要用App載入一些Full HD(1920*1080)的圖,結果跑的時候閃退了,OutOfMemoryError (OOM),怎辦? 我的手機有4G RAM耶,為什麼記憶體會用完?

處理:Google 「ImageView Out Of Memory」

參考:DevAndroid-largeHeap知乎壹讀DevAndroid-largeBitmap簡書CSDN

說明:
如果因為OOM閃退,就是記憶體用完了,在Android記憶體給每隻App是有限的,不會用到全部的RAM。

而每支手機配給App的HeapSize不同,要看手機的/system/build.prop檔,有很多App可檢視這檔(但不要亂改較好);以4G手機來說,標準的Heap是256M、如果你用了android:largeHeap=true,會變成給512M,但Google跟大部分Dev都不推薦用這招解決OOM問題,還是減少Memory使用才是正途。

所以啦,用以下方法吧:

  1. 想自己寫的話,用ImageFactory載入Res裡的圖,而且縮成要用的大小,顏色不講究也可改不一定用ARGB8888,可以改用RGB565,載完的Bitmap再來給ImageView用,詳見Google教學
  2. 不排斥用第三方包的話,可以用Picasso(Square主導)、Glide(Google主導)、Fresco(FB主導) 其中一個,比較文可看簡書CSDN,講結論就是:一般App用Glide、大圖超多的用Fresco、不喜歡Google跟FB的用Picasso。當然這三個除了載大圖功能還有其他功能,包括反覆載入、從網載入、從檔載入、生命週期及錯誤防呆 等優化,就看有沒有用到囉~


2018年1月16日 星期二

[Android] BottomBar遮住中間容器的內容,怎辦?

問題:
我用CoordinatorLayout包住Toolbar、FrameLayout、BottomNavigationView,但是我不想讓上下這2個bar根據捲動在那伸縮隱藏,而是要固定顯示,中間的FrameLayout我要拿來換Fragment,但是FrameLayout下方被Bottombar遮住了,無法完整顯示,怎辦?

處理:
Google「BottomNavigationView CoordinatorLayout」→ Google「BottomNavigationView 」

參考:StackOverflow1StackOverflow2StackOverflow3知乎CSDN

說明:
首先先說一下,CoordinatorLayout是拿來裝什麼的?
就是裝會伸縮滑動的原件,像是Toolbar、FAB、Snackbar等。

  1. 如果想做伸縮:目前Android官方的BottomNavigationView還不支援伸縮,所以官方範例也沒有裝在CoordinatorLayout裡面,真要做會伸縮的BottomBar只有用第三方,或是套用別人做好的BottomNavigationBehavior,可參考StackOverflow2,或是我之前po的這篇

  2.  如果想做固定:簡單說Bottombar不要在包CoordinatorLayout裡,用其他LinearLayout或RelativeLayout裝吧,看參考來源都是這樣;如果Toolbar跟BottomBar都想固定,但是想有要SnackBar功能在BottomBar上方,那就...用CoordinatorLayout包住中間的FrameLayout就好。

  3. 如果想根據設定,決定要捲不捲:哈,也許第三方包有,我沒找;我想出的辦法是...1:根據設定決定外部容器(讀不同layout)、2:外面都是CoordinatorLayout,但根據設定決定內容的margin、3:根據設定動態決定BottomBar要加到哪個容器。

話說,真的希望官方把BottomNavigationBehavior做進去元件呀...

[Android] Fragment用add加入後,Actvity重建時發生神奇疊圖事件,怎辦?

問題:
決定挑戰add加Fragment,結果在旋轉畫面後跟Activity被回收後,發現容器裡2個Fragment疊在一起了,怎辦呀?

處理:Google 「Fragment 重疊」

參考:CSDN簡書1簡書2腳本之家

說明:
會發生Fragments重疊的情況,一定是用add,而且加過2個Fragment或以上,然後發生了Activity重建的事件(會跑到onSaveInstanceState > onCreate)。

原因在於沒記住Fragment hidden狀態下又重建重加,就蓋囉~

解法有好幾種,可看參考,我的作法是

  • 融合CSDN的設定轉向不重建Activity(android:configChanges)
  • 加上腳本之家的存取目前頁面index,存的地方也是onSaveInstanceState,只是我讀的地方是onCreate,避免onCreate處理完onRestoreInstanceState又處理一次。
  • 用index去FragmentManager裡先找看有沒有加過,沒有才根據index新建Fragment。再看這fragment的isAdded()來決定要add還是要show。


2018年1月15日 星期一

[Android] Fragment 更換,該用add,還是replace呢?

問題:
我有個主頁面,想根據選單變更裡面的Fragment,發現有兩種換fragment的方法,該用哪種呢?

處理:Google 「fragment add replace」

參考:泡在網上的日子幫庫編程StackOverflowAndroid Advenced

說明:主要是看需求...兩個都能達成對方的行為,只是要注意寫法

  1. 用add換的話,容器裡有多個frgments,要自己注意寫hide/show事件很煩、要自己處理frgment疊圖事件很煩、要從fragmentManager裡找出來避免重複加很煩,那到底有啥好處?
    =>不會跑全部生命週期,秀過一次再秀較快,也會保留之前狀態,適合幾個會切換的fragments組合。
     
  2. 用replace換的話,容器裡只有一個fragment,寫法較簡單,一開始碰fragment不想花時解add問題的,就用replace吧;
    =>只是它每次會跑fragment的全部生命週期,所以重複換頁較add慢一點(慢多少看你生命週期做多少事),要回復之前狀態要自己寫。

上面有個重點:「生命週期 (life cycle)」,如果要在Fragment的特定生命週期做事,且確保用的方法是符合需求的,有跑到該跑的週期,沒跑到不該跑的程式。

至於哪些會跑到哪些不會跑到,因為case很多,就參考Android Advenced吧~
或著自己印Log囉~

2018年1月14日 星期日

[Android] android.support.v4.app.fragment vs android.app.fragment 該用哪個?

問題:
我想在App裡用Fragment做切畫面,結果發現有2個包都有Fragment到底該用哪個?

處理:
Google 「android.support.v4.app.fragment  android.app.fragment」


說明:
  1. 照一般說法,如果你的min>=API 11(Android 3.0),應該就是用android.app.fragment。
    現在大部分App min都至少15(Android 4.0.3)起跳了吧,也有看過min設21 (Android 5)的。以2018/01的分布來看,80%涵蓋選21、90%涵蓋選19、95%涵蓋選17,怎樣都超過11呀...
  2. 那實際上呢? 因為Fragment及其相關類別(FragmentManager、FragmentTransaction)還在一直加功能,如果Min不等於Target,有些新功能在android.app.fragment用不了,或是要認裝置版本跑;用v4包的話,寫法就都一樣了。就看有沒有要用到後來的新功能,或是為那新功能自己寫實作在Base之類。

其他要注意的點,就是選好要用哪邊的話就全套都用那邊了...
Fragment、FragmentManager、FragmentTransaction、FragmentPagerAdapter、Activity

這次你選擇哪一邊呢?(咦? 元件東西軍嗎?)

[Android] Error:All flavors must now belong to a named flavor dimension.

問題:
為了用Android Profiler功能,我把Gradle Plugin版本升到3.0.1了(因為AS說GP2.4以上才支援),結果編版時出現錯誤訊息"Error:All flavors must now belong to a named flavor dimension.",該怎麼辦?

處理:
1. 去他提供的網址看。
2. Google 錯誤訊息。

參考:DevAndroid-Migrate to 3.0DevAndroid-Build VariantsStackOverflow

回答:
  1. 簡單說,不想用flavor dimension功能的話,
    就在android {}裡面寫flavorDimensions "default",詳見StackOverflow
  2. 如果真的要一次編出一堆設定組合的APK的時候,建議可用flavor dimension功能,
    假設有A、B、C三個維度,他可編出維度A*維度B*維度C的數量的APK,就不用每個口味寫一堆設定了,詳見DevAndroid

到底為什麼要編那麼多種設定組合呀....QQ