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

[Android] signature version V1? V2?

問題:
我要匯出簽章APK了,結果Android Studio 3.0.1(其實AS2.2以上就有)叫我一定要至少選一個版本的簽章,我到底該選V1還是V2? 兩個有甚麼差別?

處理:Google 「android signature version」

參考:CSDNStackOverflow

回答:
簡單說,兩個都要勾,

因為V2是Android 7.0才出現的比較安全,而之前的只認得V1。

但如果7.0或以上版本的手機安裝這APK有問題,而且你檢查不出來為何,但V1簽的可過,那就只用V1吧。

詳見參考囉。

2018年1月11日 星期四

[Android] HandlerLeak 處理

問題:
我寫了一個Handler處理訊息,結果Android Studio跳警告說可能引起memory leaks,到底為什麼可能會引起memory leak?除了加上@SuppressLint("HandlerLeak")叫他不要跳警告外,還有甚麼辦法解掉呢?

處理:Google 「HandlerLeak」

參考:清屏網筆記社區簡書

清屏網的文章看來可能引發Leak的情況是:
「當我們通過Handler發送了一個延時消息,這個消息還未來得及處理,當前Activity銷毀了,該消息就會在主執行緒的訊息佇列中等待執行,等待時間到後處理該消息,該消息引用了當前Activity的Handler物件, Handler又引用了當前Activity的物件,這些引用物件會保持到該消息處理完,就導致了當前Activity無法被回收,從而導致了記憶體洩漏。」

那我寫的部分真的會真的會引起memory leak嗎?
可以用筆記社區裡教的方法用AS跟MAT測測看,如果用Android Studio 3.0以上,可用Android Profiler試試,如果該被回收的Activity沒被回收就是囉。

懶得一一測了,那要麼寫才不會引起leak呢?
可參考簡書的這篇文章,跟Android Studio的警告詳細內容寫的解法其實是同一個意思,重點是做個private static class繼承Handler,然後把Activity用WeakReference傳進自製Handler裡處理,目的都是不要直接參考到外部類別(Activity)避免回收不了。

2018年1月10日 星期三

[Android] 2018/1月份 版本分布

參考:Android dashboards


  1. 最重要的,API21+破80%了,如果有新App要做,可以考慮min用21(5.0)了,因為5.0才開始原生支援Material Design呀....還有JobScheduler呀...
  2. OREO(8.X)升為0.7%,出現在榜單4個月,8.1新進榜。
  3. 牛軋糖(7.X)+破27%,出現在榜單已15個月了...
  4. 棉花糖(6.X)以下全部下降。
  5. 牛軋糖(7.X)持續上升。
  6. 還有6.5%的使用中裝置不能玩PMGO跟熊大農場


話說,80%到90%要多久呢? 看之前紀錄...

API 19時:2016/9~2017/7=11個月。
API 18時:2016/7~2017/5=11個月。

我沒有API17+ 剛達到80%的資料了 XD

另外,API 20是Android手錶OS專用,所以沒有在這上面

2018年1月9日 星期二

[Android] 全螢幕載入畫面(splash screen)不要有Actionbar

問題:
我想做一個全螢幕載入畫面(splash screen)不要有Actionbar,但Android Studio預設的畫面都有Actionbar怎辦?

處理:Google 「android no actionbar」

參考:NoActionBar上下變色

解法:

  • 對要當載入畫面(splash screen)的Activity,在Manifest,另外指定它的android:theme,假設叫做FullscreenTheme好了。

  • 在style裡面寫這個FullscreenTheme,它的parent = "Theme.AppCompat.Light.NoActionBar" 或是 "Theme.AppCompat.DayNight.NoActionBar",看你AppTheme用哪種,用相對的NoActionBar。

  • 如果系統狀態列(status bar)想變色,就在FullscreenTheme裡加上colorPrimaryDark,指一個跟載入畫面背景類似但較深的顏色。

  • 如果系統按鈕列(navigation bar)想變色,就在FullscreenTheme 裡加上 android:navigationBarColor,指一個跟載入畫面背景類似但較深的顏色。只是這屬性是API21才出來的,所以min不是21的話,就放在values-v21的資料夾裡的style。

    [2018/2/21更新]  DevAndroid裡有說,"當您自訂瀏覽列和狀態列時,請讓兩者都變成透明,或者只修改狀態列。 在其他所有狀況下,瀏覽列都必須保持黑色。",所以除非上下透明,不然下就不改囉~

大概就這樣,其實上下系統列也可藏,但看了FB、LINE、Youtube等App,都是上下有秀,所以就學他們囉~


2018年1月8日 星期一

[Android] 讓 Snackbar 不要被 BottomNavigationView (BottomBar) 遮住

問題:
根據Material Design的指導,Snackbar要在BottomBar之後但要長出頭,但實際直接叫Snackbar他躲在BottomBar後面說,怎麼辦?

處理:
Google 「BottomNavigationView  Snackbar」
找到幾種作法...

解法:

  1. 繼承改寫CoordinatorLayout.Behavior並在layout指給BottomBar的app:layout_behavior,詳細作法參考某日本Dev的GitHub某馬其頓Dev的Blog。這個做法較能重複利用,用的地方也較簡潔。
  2. 另一個方法是,用SnackBar時,自己抓它的View改它的padding(內部間距)或margin(外部距離),寫個method或是如果有要繼承改寫BottomNavigationView或Snackbar的話,可順便把方法加入。可參考某中國Dev的Blog
  3. 使用第三方BottomBar。
  4. 不要用Snackbar了,用Toast跟Dialog吧。


找資料時,也在StackOverflow看到網友反應的悲劇,4.4以下Snackbar遮BottomBar,5.0以上BottomBar遮Snackbar;

嘛....Google都是先設計UI規範,再實做元件 = =;

所以要Material Design各元件有簡單官方解,看來還有得等,官方解出來前,只能各自發揮創意或是先用有完整解的第三方元件了。

[Android] BottomNavigationView (BottomBar)跟 FloatingActionButton(FAB) 在一起

問題:
我畫面上想要有 下方快捷列(BottomBar)也想要有 懸浮按鈕(FAB),但直接加到Layout好像位置怪怪,有啥辦法可讓FAB正確在BottomBar上方呢?

做法:Google "BottomNavigationView" "FloatingActionButton"

參考:StackOverflow

解法:其實跟StackOverflow裡寫的一樣,只是我寫中文...

  1. 到FAB所在的layout
  2. 在FAB外的CoordinatorLayout 加上BottomBar在FAB下面
  3. 在CoordinatorLayout 跟BottomNavigationView地的屬性裡加上app:layout_insetEdge="bottom"
喔喔,厲害了,FAB長出來了,BottomBar也正常,耶 XD

Android Studio 3.0.1 專案雙開

問題:
我想要寫這個專案時, 參考另一個專案的寫法,Android Studio有辦法可以同時開兩個專案嗎?

參考:好心人教導。

解法:
  1. 先開第一個專案,這相信有用過Android Studio的都會。
  2. 去File > Open
  3. 跳出來一個對話框,選擇New Window
耶~ 成功雙開,可以開始 參考了 XD

2018年1月7日 星期日

Android Studio 新專案加入 Git版控

問題:用Android Studio新建的專案,想加入Git版控,避免改壞要倒回,有什麼好工具?

處理:
嘛....以免費的Git工具來講:

  1. Git 官方工具 是最新最快的,只是Windows版預設要另開視窗打指令。
  2. SourceTree 是免費Git GUI工具中功能最多的,也有定期更新,只是GUI部分,反應有點慢,然後Win版 跟 Mac版 介面略有不同。
  3. TortoiseGit 是有用過TortoiseSVN(小烏龜SVN)的人會覺得熟悉的介面,只是只有Win版。
  4. Android Studio整合Git,要先裝1的Git工具,然後在AS裡設定位置,即可整合使用,可在AS下方的console打git指令或是一些AS提供的簡單GUI可操作,也有來自IntelliJ的差異合併工具的樣子。中文教學點這 繁中AS3.0Git教學簡中AS2.3Git教學,官方好像沒教學文。
上面四種來說1跟4都是比較官方的做法,

只是1、2差異工具(DiffTool)跟合併工具(MergeTool)要自己指定囉~
兩平台都有人大推的是Beyond Compare 只是這是要錢工具(不想後悔併錯可考慮BUY下去 XD)

不想花錢的話,
Win可灌TortoiseSVN或TortoiseGit,然後用他的TortoiseMerge(沒單獨出真可惜...),只是記得第一次使用前先設為UTF-8編碼(不然會是Win的編碼方式);

Mac的話似乎內建差異跟合併工具FileMerge,但有時呆呆,會併錯,可參考這個Beyond Compare在Mac的替代方案 ,或是看SourceTree官方推薦的工具

至於Git的相關概念跟流程操作,那是一個要自己做過幾次才能理解的東東...

Android App 桌面圖示(Launcher icon) 各size製作

問題:
一個App的桌面icon,要做好多尺寸,除了Visual Designer(VD)作好最大尺寸,一個一個縮之外,還有甚麼好辦法呢?

處理:
在Google搜尋了「Android icon」第一頁結果發現以下能用的方法:

  1. Android Asset Studio>Launcher icon generator
    這是線上版icon產生器,可以打字、傳圖、選icon來決定前景,背景可選色、形狀、縮邊,還能對前景作陰影特效;會產生512px 商店用的、192px xxxhdpi用的~48px mdpi用的,res部分資料夾結構會自動生成。
  2. Image Asset Studio
    官網介紹這是Android Studio內建功能,看來除了跟第一個一樣的功能,還能生出Android 8.0 Oreo開始才有的adaptive launcher icon(前後景分開,後面可變形)。
    詳細操作,網頁裡有寫 (如果是英文苦手,可在該網頁下方切簡體中文看),下面只寫簡單操作步驟:
    1-檢視方式選Android檢視。
    2-在res點右鍵,選New > Image Asset。
    3-剩下就看Image Asset Studio介面操作唄。


嗯嗯....所以說,

  • 如果要設計前後景分開的Material Design風格的icon,記得跟VD說可以只給前景的圖跟背景色碼即可,剩下的Image Asset Studio 可完成。當然,如果VD們會操作更好,因為還有縮邊、位置等問題,不是每個Dev都那麼眼利。
  • 如果還是設計傳統的含背景icon,那就請VD做最大尺寸(512*512)吧,再來縮,如果VD會縮那當然更好囉~ 如果iOS也要用一樣的,那最大尺寸要給1024*1024喔。


耶~ 又解決一個問題!

2018年1月6日 星期六

Android Studio 3.0.1 改package name

問題:Android Studio 3.0.1生出來的package名稱架構不合預期,要改package name,但用到地方很多,除了手動搬,有啥好招?

參考:http://happycodingandlearning.blogspot.tw/2017/07/android-android-studio-packagename.html

作法:
  1. 改gradle的applicationId
  2. 改manifest的package
  3. 對每一層package資料夾分別refactor>rename(這裡太多層感覺會QQ) 
  4. 原本的較多層,就把原目標層的檔在AS中,全拉到新目標層,AS會跳選項幫你處理關聯參考;原本的資料夾若空了,可按右鍵>Delete。
  5. 原本的較少層,就在Project檢視>點右邊小齒輪>點掉Compact Empty Middle Package的勾,才能顯示空的package資料夾;然後在目前最後層,點右鍵>New>Package;加好資料夾,就把原目標層的檔在AS中,全拉到新目標層,AS會跳選項幫你處理關聯參考。
嘛....大概這樣,做個筆記,我不太花時間寫圖文並茂的blog

但還是希望能幫到遇到同問題的人囉。