你应该知道的那些Android小经验
做Android久了,就會(huì)踩很多坑,被坑的多了就有經(jīng)驗(yàn)了,閑暇之余整理了部分,現(xiàn)挑選一些重要或者偏門的“小”經(jīng)驗(yàn)做個(gè)記錄。
查看SQLite日志
| 1 2 | adb shell setprop log.tag.SQLiteLog V adb shell setprop log.tag.SQLiteStatements V |
因?yàn)閷?shí)現(xiàn)里用了Log.isLoggable(TAG, Log.VERBOSE)做了判斷,LessCode的LogLess中也參考了這種機(jī)制:LogLess。
使用這種方法就可以在Release版本也能做到查看應(yīng)用的打印日志了。
PNG優(yōu)化
APK打包會(huì)自動(dòng)對(duì)PNG進(jìn)行無損壓縮,如果自行無損壓縮是無效的。
當(dāng)然進(jìn)行有損壓縮是可以的:https://tinypng.com/
Tcpdump抓包
有些模擬器比如genymotion自帶了tcpdump,如果沒有的話,需要下載tcpdump:
http://www.strazzere.com/android/tcpdump
把tcpdump push到/data/local下,抓包命令:
| 1 | adb shell /data/local/tcpdump -i any -p -s 0 -w /sdcard/capture.pcap |
查看簽名
很多開發(fā)者服務(wù)都需要綁定簽名信息,用下面的命令可以查看簽名:
| 1 | keytool -list -v -keystore release.jks |
注意,這個(gè)是需要密碼的,可以查看MD5, SHA1,SHA256等等。
單例模式(懶漢式)的更好的寫法
特別說到這個(gè)問題,是因?yàn)榫W(wǎng)上很多這樣的代碼:
| 1 2 3 4 5 6 7 8 9 10 11 | public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } |
這種寫法線程不安全,改進(jìn)一下,加一個(gè)同步鎖:
| 1 2 3 4 5 6 7 8 9 10 | public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } |
網(wǎng)上這樣的代碼更多,可以很好的工作,但是缺點(diǎn)是效率低。
實(shí)際上,早在JDK1.5就引入volatile關(guān)鍵字,所以又有了一種更好的雙重校驗(yàn)鎖寫法:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } } |
注意,別忘記volatile關(guān)鍵字哦,否則就是10重,100重也可能還是會(huì)出問題。
上面是用的最多的,還有一種靜態(tài)內(nèi)部類寫法更推薦:
| 1 2 3 4 5 6 7 8 9 10 | publlic class Singleton { private Singleton() {} private static class SingletonLoader { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonLoader.INSTANCE; } } |
多進(jìn)程Application
是不是經(jīng)常發(fā)現(xiàn)Application里的方法執(zhí)行了多次?百思不得其解。
因?yàn)楫?dāng)有多個(gè)進(jìn)程的時(shí)候,Application會(huì)執(zhí)行多次,可以通過pid來判斷那些方法只執(zhí)行一次,避免浪費(fèi)資源。
隱式啟動(dòng)Service
這是Android5.0的一個(gè)改動(dòng),不支持隱式的Service調(diào)用。下面的代碼在Android 5.0+上會(huì)報(bào)錯(cuò):Service Intent must be explicit:
| 1 2 3 | Intent serviceIntent = new Intent(); serviceIntent.setAction("com.jayfeng.MyService"); context.startService(serviceIntent); |
可改成如下:
| 1 2 3 | // 指定具體Service類,或者有packageName也行 Intent serviceIntent = new Intent(context, MyService.class); context.startService(serviceIntent); |
fill_parent的壽命
在Android2.2之后,支持使用match_parent。你的布局文件里是不是既有fill_parent和match_parent顯得很亂?
如果你現(xiàn)在的minSdkVersion是8+的話,就可以忽略fill_parent,統(tǒng)一使用match_parent了,否則請(qǐng)使用fill_parent。
ListView的局部刷新
有的列表可能notifyDataSetChanged()代價(jià)有點(diǎn)高,最好能局部刷新。
局部刷新的重點(diǎn)是,找到要更新的那項(xiàng)的View,然后再根據(jù)業(yè)務(wù)邏輯更新數(shù)據(jù)即可。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | private void updateItem(int index) { int visiblePosition = listView.getFirstVisiblePosition(); if (index - visiblePosition >= 0) { //得到要更新的item的view View view = listView.getChildAt(index - visiblePosition); // 更新界面(示例參考) // TextView nameView = ViewLess.$(view, R.id.name); // nameView.setText("update " + index); // 更新列表數(shù)據(jù)(示例參考) // list.get(index).setName("Update " + index); } } |
強(qiáng)調(diào)一下,最后那個(gè)列表數(shù)據(jù)別忘記更新, 不然數(shù)據(jù)源不變,一滾動(dòng)可能又還原了。
系統(tǒng)日志中幾個(gè)重要的TAG
| 1 2 3 4 5 6 7 8 | // 查看Activity跳轉(zhuǎn) adb logcat -v time | grep ActivityManager // 查看崩潰信息 adb logcat -v time | grep AndroidRuntime // 查看Dalvik信息,比如GC adb logcat -v time | grep "D\/Dalvik" // 查看art信息,比如GC adb logcat -v time | grep "I\/art" |
一行居中,多行居左的TextView
這個(gè)一般用于提示信息對(duì)話框,如果文字是一行就居中,多行就居左。
在TextView外套一層wrap_content的ViewGroup即可簡(jiǎn)單實(shí)現(xiàn):
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 套一層wrap_content的ViewGroup --> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> </LinearLayout> </RelativeLayout> |
setCompoundDrawablesWithIntrinsicBounds()
網(wǎng)上一大堆setCompoundDrawables()方法無效不顯示的問題,然后解決方法是setBounds,需要計(jì)算大小…
不用這么麻煩,用setCompoundDrawablesWithIntrinsicBounds()這個(gè)方法最簡(jiǎn)單!
計(jì)算程序運(yùn)行時(shí)間
為了計(jì)算一段代碼運(yùn)行時(shí)間,一般的做法是,在代碼的前面加個(gè)startTime,在代碼的后面把當(dāng)前時(shí)間減去startTime,這個(gè)時(shí)間差就是運(yùn)行時(shí)間。
這里提供一種寫起來更方便的方法,完全無時(shí)間邏輯,只是加一個(gè)打印log就夠了。
| 1 2 3 4 | // 測(cè)試setContentView()的時(shí)間 Log.d("TAG", "Start"); setContentView(R.layout.activity_http); Log.d("TAG", "End"); |
沒有計(jì)算時(shí)間的邏輯,這能測(cè)出來?
把日志過濾出來,運(yùn)行命令“adb logcat -v time | grep TAG”:
| 1 2 | 03-18 14:47:25.477 D/TAG (14600): Start 03-18 14:47:25.478 D/TAG (14600): End |
通過-v time參數(shù),可以比較日志左邊的時(shí)間來算出中間的代碼運(yùn)行的時(shí)間。
JAVA引用類型一覽表
對(duì)象引用:強(qiáng)引用 > 軟引用 > 弱引用 > 虛引用。
| 強(qiáng)引用 | 從來不會(huì) | 對(duì)象的一般狀態(tài) | JVM停止運(yùn)行時(shí)終止 |
| 軟引用 | 在內(nèi)存不足時(shí) | 對(duì)象緩存 | 內(nèi)存不足時(shí)終止 |
| 弱引用 | 在垃圾回收時(shí) | 對(duì)象緩存 | GC運(yùn)行后終止 |
| 虛引用 | 在垃圾回收時(shí) | 對(duì)象跟蹤 | GC運(yùn)行后終止 |
Context使用場(chǎng)景
為了防止Activity,Service等這樣的Context泄漏于一些生命周期更長的對(duì)象,可以使用生命周期更長的ApplicationContext,但是不是所有的Context的都能替換為ApplicationContext
這是網(wǎng)上流傳的一份表格:
| Show Dialog | 否 | 是 | 否 | 否 | 否 |
| Start Activity | 否 | 是 | 否 | 否 | 否 |
| Layout Inflation | 否 | 是 | 否 | 否 | 否 |
| Start Service | 是 | 是 | 是 | 是 | 是 |
| Bind Service | 是 | 是 | 是 | 是 | 否 |
| Send Broadcast | 是 | 是 | 是 | 是 | 是 |
| Regist BroadcastReceiver | 是 | 是 | 是 | 是 | 否 |
| Load Resource Value | 是 | 是 | 是 | 是 | 是 |
圖片緩存大小
現(xiàn)在很多圖片庫需要給圖片設(shè)置一個(gè)最大緩存,但是這個(gè)值設(shè)置多少合適呢?
高端機(jī)和低端機(jī)的配置顯然應(yīng)該不同,可以考慮設(shè)置一個(gè)動(dòng)態(tài)值。
建議設(shè)置為應(yīng)用可用內(nèi)存的1/8:
| 1 | int memoryCache = (int) (Runtime.getRuntime().maxMemory() / 8); |
系統(tǒng)內(nèi)置的一些工具類
在AOSP源碼全局搜了一下包含Util關(guān)鍵字的類,整理出這個(gè)列表供大家參考:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | // 系統(tǒng) ./android/database/DatabaseUtils.java ./android/transition/TransitionUtils.java ./android/view/animation/AnimationUtils.java ./android/view/ViewAnimationUtils.java ./android/webkit/URLUtil.java ./android/bluetooth/le/BluetoothLeUtils.java ./android/gesture/GestureUtils.java ./android/text/TextUtils.java ./android/text/format/DateUtils.java ./android/os/FileUtils.java ./android/os/CommonTimeUtils.java ./android/net/NetworkUtils.java ./android/util/MathUtils.java ./android/util/TimeUtils.java ./android/util/ExceptionUtils.java ./android/util/DebugUtils.java ./android/drm/DrmUtils.java ./android/media/ThumbnailUtils.java ./android/media/ImageUtils.java ./android/media/Utils.java ./android/opengl/GLUtils.java ./android/opengl/ETC1Util.java ./android/telephony/PhoneNumberUtils.java // 設(shè)計(jì)和支持庫 ./design/src/android/support/design/widget/ViewGroupUtils.java ./design/src/android/support/design/widget/ThemeUtils.java ./design/src/android/support/design/widget/ViewUtils.java ./design/lollipop/android/support/design/widget/ViewUtilsLollipop.java ./design/base/android/support/design/widget/AnimationUtils.java ./design/base/android/support/design/widget/MathUtils.java ./design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java ./v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchUIUtil.java ./v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchUIUtilImpl.java ./v7/recyclerview/src/android/support/v7/util/MessageThreadUtil.java ./v7/recyclerview/src/android/support/v7/util/AsyncListUtil.java ./v7/recyclerview/src/android/support/v7/util/ThreadUtil.java ./v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java ./v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java ./v7/recyclerview/tests/src/android/support/v7/util/ThreadUtilTest.java ./v7/appcompat/src/android/support/v7/graphics/drawable/DrawableUtils.java ./v7/appcompat/src/android/support/v7/widget/DrawableUtils.java ./v7/appcompat/src/android/support/v7/widget/ThemeUtils.java ./v7/appcompat/src/android/support/v7/widget/ViewUtils.java ./v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java ./v4/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java ./v4/jellybean/android/support/v4/app/BundleUtil.java ./v4/jellybean/android/support/v4/app/NavUtilsJB.java ./v4/java/android/support/v4/app/NavUtils.java ./v4/java/android/support/v4/database/DatabaseUtilsCompat.java ./v4/java/android/support/v4/graphics/ColorUtils.java ./v4/java/android/support/v4/text/TextUtilsCompat.java ./v4/java/android/support/v4/util/TimeUtils.java ./v4/java/android/support/v4/util/DebugUtils.java ./v4/java/android/support/v4/content/res/TypedArrayUtils.java |
這么多工具類,一定可以找到對(duì)你有用的。
ClipPadding
這個(gè)不多說,ListView的ClipPadding設(shè)為false,就能為ListView設(shè)置各種padding而不會(huì)出現(xiàn)丑陋的滑動(dòng)“禁區(qū)”了。
強(qiáng)大的dumpsys
dumpsys可以查看系統(tǒng)服務(wù)和狀態(tài),非常強(qiáng)大,可通過如下查看所有支持的子命令:
| 1 | dumpsys | grep "DUMP OF SERVICE" |
這里列舉幾個(gè)稍微常用的:
| activity | 顯示所有的activities的信息 |
| cpuinfo | 顯示CPU信息 |
| window | 顯示鍵盤,窗口和它們的關(guān)系 |
| meminfo | 內(nèi)存信息(meminfo $package_name or $pid 使用包名或者進(jìn)程id顯示內(nèi)存信息) |
| alarm | 顯示Alarm信息 |
| statusbar | 顯示狀態(tài)欄相關(guān)的信息(找出廣告通知屬于哪個(gè)應(yīng)用) |
| usagestats | 每個(gè)界面啟動(dòng)的時(shí)間 |
bugreport命令
很多人都用過adb logcat,但是如果想要更詳細(xì)的信息,logcat則無能為力。
所以大多數(shù)手機(jī)廠商測(cè)試更多的是用adb bugreport來抓log給開發(fā)人員分析。
| 1 2 3 | // 除了log,還包括啟動(dòng)后的系統(tǒng)狀態(tài),包括進(jìn)程列表,內(nèi)存信息,VM信息等等 // 而且不像logcat是一直打印的,bugreport命令輸出到當(dāng)前時(shí)間就停止結(jié)束了。 adb bugreport > main.log |
dpi文件夾的換算比例
之前的ldpi基本可以拋棄了,主流的dpi已經(jīng)從很早之前的mdip轉(zhuǎn)移到了xhdpi了,特別提醒。
| mdpi(160dp) | 320P | 1 | 1 |
| hdpi(240dp) | 480P | 1 | 1.5 |
| xhdpi(320dp) | 720P | 1 | 2 |
| xxhdpi(480dpi) | 1080P | 1 | 3 |
更新媒體庫文件
以前做ROM的時(shí)候經(jīng)常碰到一些第三方軟件(某音樂APP)下載了新文件或刪除文件之后,但是媒體庫并沒有更新,因?yàn)檫@個(gè)是需要第三方軟件主動(dòng)觸發(fā)。
| 1 2 3 | // 通知媒體庫更新單個(gè)文件狀態(tài) Uri fileUri = Uri.fromFile(file); sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,fileUri)); |
媒體庫會(huì)在手機(jī)啟動(dòng),SD卡插拔的情況下進(jìn)行全盤掃描,不是實(shí)時(shí)的而且代價(jià)比較大,所以單個(gè)文件的刷新很有必要。
Monkey參數(shù)
大家都知道,跑monkey的參數(shù)設(shè)置有一些要注意的地方,比如太快了不行不切實(shí)際,太慢了也不行等等,這里給出一個(gè)參考:
| 1 | adb shell monkey -p <packageName> -s 1000 --ignore-crashes --ignore-timeouts --ignore-security-exceptions --pct-trackball 0 --pct-nav 0 --pct-majornav 0 --pct-anyevent 0 -v --throttle 300 1200000000 |
一邊跑monkey,一邊抓log吧。
小結(jié)
無論是大經(jīng)驗(yàn)還是小經(jīng)驗(yàn),有用就是好經(jīng)驗(yàn)。
原文地址: http://jayfeng.com/2016/03/18/%E4%BD%A0%E5%BA%94%E8%AF%A5%E7%9F%A5%E9%81%93%E7%9A%84%E9%82%A3%E4%BA%9BAndroid%E5%B0%8F%E7%BB%8F%E9%AA%8C/
總結(jié)
以上是生活随笔為你收集整理的你应该知道的那些Android小经验的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Bitmap面面观
- 下一篇: 理解Android Java垃圾回收机制