標籤:
本手冊適合至少有初級經驗的開發人員查閱或複習相關知識使用,新手可能會看不懂。
1、java代碼最佳化1.1安卓如何執行代碼
dvm:.java->.class->.dex->.apk
斐波那契數列的遞推公式是f(n)=f(n-1)+f(n-2),特徵方程為:x2=x+1,解該方程得(1+sqrt(5))/2,(1-sqrt(5))/2.所以f(n)=Ax1n+Bx2n,帶入f(0)=0,f(1)=1得A=sqrt(5)/5,B=-sqrt(5)/5.則f(n)求出。
用這個類來解決溢出問題。
斐波那契數列有如下性質:
(fn fn-1)=(fn-1 fn-2)*A,可以得出A=(1 1;1 0)
遞推可得:(fn fn-1)=(fn-1 fn-2)*A=(fn-2 fn-3)*A2=…=(f1 f0)*An-1
-
private LruCache<integer, string=""> mJsonCache; /** * 緩衝圖片資訊 */ private LruCache<integer, bitmap=""> mBitmapCache; public Util() { mJsonCache = new LruCache<integer, string="">(1 * 1024 * 1024); mBitmapCache = new LruCache<integer, bitmap="">(2 * 1024 * 1024); }
2、NDK
主要由c/c++編寫,是安卓原生開發套件。
1、建立本地方法:
public native String encode(String text, int length);public native String decode(String text, int length);}
2、實現JNI粘合層:
使用jdk的javah工具自動產生。
- 在項目根目錄下建立jni檔案夾
- 在jni檔案中建立一個c檔案
在java代碼中,建立一個本地方法helloFromC
public native String helloFromC();
在jni中定義函數實現這個方法,函數名必須為
jstring Java_com_zhilinghui_helloworld1_MainActivity_helloFromC(JNIEnv* env, jobject obj)
返回一個字串,用c定義一個字串
char* cstr = "hello from c";
把c的字串轉換成java的字串
jstring jstr = (*env)->NewStringUTF(env, cstr);return jstr;
- 在jni中建立Android.mk檔案
- 在c檔案中添加
安卓中如何使用c/c++代碼(總結)
前面可以說的有點亂,這裡簡單概括一下:
- 步驟一:
在java中定義一個c方法的介面 ,相當於在java代碼中定義了一個介面 介面的實現方法是C語言實現的。
public native String hello();
步驟二:
實現C代碼
方法名 嚴格按照jni的規範
步驟三:
建立android.mk 告訴編譯器 如何把c代碼打包成函數庫
LOCAL_PATH := $(call my-dir)
步驟四:
把c代碼 打包成函數庫 用到了安裝的環境 到相應目錄下使用ndk-build打包
步驟五:
在java代碼中 引入庫函數
- 步驟六:
使用方法
3、NDK進階
彙編最佳化:安卓 NDK內建了GCC編譯器的功能。
要確保使用正確版本的objdump反編譯目標檔案和庫。
ARM模式和Thumb模式。
- 色彩轉換:圖形程式中最常用的操作是把顏色從一種格式轉換為另一種,ARGB8888和RGB565.
- RAM指令
提高效能的技巧
- 內嵌函式:即直接在調用處實現替換調用。在函數定義前加上”inline“關鍵字就可以了。
- 迴圈展開:展開可以積極調度(或管道化)迴圈以掩蓋一些延遲。如果有足夠的空閑寄存器使變數保持活動狀態,因為通過展開相關性鏈展露了關鍵路徑,這將非常有用。
- 記憶體預讀取:1.GCC的——builtin_prefetch();2、在ARM彙編代碼中使用PLD和PLDW指令,還可以使用PLI指令。
- 用LDM/STM替換LDR/STD:使用單個指令載入多個寄存器比用多個LDR指令載入寄存器快。
4、高效使用記憶體
java的char是16位(UTF-16),java的long是64位,而c的long是32位,long long是64位。
訪問記憶體:
減少資料緩衝讀未命中幾率的方法:1、在有大量資料存放區在數組中時,使用儘可能小的資料類型;2、選擇順序訪問而不是隨機訪問,最大限度的重用已在緩衝中的資料。
垃圾收集:記憶體流失
只有當某個對象不再引用時,它的記憶體才會被回收,當該被釋放的對象引用仍然存在的時候就會發生OOM。
例如:旋轉螢幕時。Eclipse中可以在DDMS中的Heap及Allocation Tracker跟蹤。
也可以使用StricMode類來檢測潛在在記憶體流失.
引用
軟可及對象,垃圾收集器自己決定想回收舊回收。弱可及對象,基本會被回收。虛引用,幾乎很少用,可以用來註冊引用隊列。
- 垃圾收集
可能會在不定的時間觸發,幾乎無法控制其發生,例如堆被佔滿不能進行記憶體配置時,在分配新對象要進行記憶體回收。
API
- ActivityManager的getMemoryInfo()、getMemoryClass()、getLargeMemoryClass();
- Debug的dumHprofData()、getMemoryInfo、getNativeHeapAllocatedSize()、getNativeHeapSize().
-
ActivityManager am=(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);ActivityManager.MemoryInfo memInfo=new ActivityManager.MemoryInfo();am.gettMemoryInfo(memInfo);
5、多線程和同步線程
Thread對象。run()方法可以被複寫,Runnable對象傳遞給Thread建構函式。記得調用start()。
線程優先順序,一般是1-10(最高),預設是5.而Linux的優先順序是從-20到19(最低).
AsyncTask
AsyncTask啟動任務棧執行的輸入參數,背景工作執行的百分比,背景工作執行返回iud結果,匿名類。
例如:後台下載檔案時重新整理前台進度條。
Handler和Looper
他們是多線程應用線程間通訊的基石。
Handler
- Handler:處理者,負責Message的發送及處理。使用Handler時,需要實現handleMessage(Message msg)方法來對特定的Message進行處理,例如更新UI等。
Handler在run()方法中建立,因為它需要被綁定到指定的Looper,這Looper就是在run()方法中調用Looper.prepare(0建立的。線上程產生之前,調用getHandler()返回null.
Looper
有訊息迴圈的線程。
- Looper:訊息泵,不斷地從MessageQueue中抽取Message執行。因此,一個MessageQueue需要一個Looper。
Message:訊息,其中包含了訊息ID,訊息處理對象以及處理的資料等,由MessageQueue統一列隊,終由Handler處理。
MessageQueue:訊息佇列,用來存放Handler發送過來的訊息,並按照FIFO規則執行。當然,存放Message並非實際意義的儲存,而是將Message以鏈表的方式串聯起來的,等待Looper的抽取。
Thread:線程,負責調度整個訊息迴圈,即訊息迴圈的執行場所
Activity生命週期
7個生命週期
調用AsyncTask.cancel()會在doInbackground()返回後觸發onCancelled(),而不是onPoseExecute().
6、效能評測和剖析
DDMS中的Traceview
查看跟蹤代碼的執行時間,分析哪些是耗時操作 ;可以用於跟蹤方法的調用,尤其是Android Framework層的方法調用關係
TraceView羅列出了是所有監聽到的方法,當然也包括Android系統很多方法的耗時,如何在這麼多方法裡面尋找到自己關心的? 可以通過TraceView 底部的find 來尋找,通常Android app都是有包名的,可以先針對某些關心的列排序後,在通過包名進行一個個尋找,這些就省去自己篩選出自己app 方法耗時排行的時間。
7、延長電池續航時間
一般螢幕和wifi很耗電。
測量電池用量
可以通過檢索固定的Intent。在應用啟動時就擷取電池當前電量,運行一段時間,在退出時再次擷取電池電量。但是這個值並不準確,因為還有其他應用運行。
禁用廣播接收器
只有在需要時才啟用廣播接收器。
- 因為BatteryManager廣播的是一個sticky的intent實體,這就意味著你不用非得註冊一個廣播接收者來讓你的程式接受這個廣播,你可以僅僅就是通過調用registerReceiver這個方法,在需要添加廣播接收者位置的參數上傳入null,當然你也可以建立一個廣播接收者,並在註冊廣播接收者的時候傳入。
網路
最終解決問題,AT&T與來自密西根大學的同事們的工作人員進行了深入的端到端資料轉送路徑的綜合調查,最終發現是在裝置和蜂窩網路之間複雜的相互作用的問題的根源,相互作用,是很難看到,給出分層性網路架構,故意隱藏低層協議的開發人員在應用程式層的工作。
更多文章可以參考:http://www.research.att.com/editions/201106_home.html
位置
使用
WakeLock
例如,在使用者觀看視頻或電影時,cpu需要做視頻解碼,同時保持螢幕開啟,讓使用者能夠觀看。
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); WakeLock sCpuWakeLock = pm.newWakeLock( PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,"okTag"); if (sCpuWakeLock!= null) { sCpuWakeLock.release(); sCpuWakeLock = null; }
- PARTIAL_WAKE_LOCK:保持CPU 運轉,螢幕和鍵盤燈有可能是關閉的。
SCREEN_DIM_WAKE_LOCK:保持CPU 運轉,允許保持螢幕顯示但有可能是灰的,允許關閉鍵盤燈
SCREEN_BRIGHT_WAKE_LOCK:保持CPU 運轉,保持螢幕高亮顯示,允許關閉鍵盤燈
FULL_WAKE_LOCK:保持CPU 運轉,保持螢幕高亮顯示,鍵盤燈也保持亮度
ACQUIRE_CAUSES_WAKEUP:不會喚醒裝置,強制螢幕馬上高亮顯示,鍵盤燈開啟。有一個例外,如果有notification彈出的話,會喚醒裝置。
- ON_AFTER_RELEASE:WakeLock 被釋放後,維持螢幕亮度一小段時間,減少WakeLock 迴圈時的閃爍情況
如果申請了partial wakelock,那麼即使按Power鍵,系統也不會進Sleep,如Music播放時 如果申請了其它的wakelocks,按Power鍵,系統還是會進Sleep
但如果不領會Android設計者的意圖而濫用Wake Lock API,為了自身程式在背景正常工作而長時間阻止AP進入休眠狀態,就會成為待機電池殺手。
提醒
AlarmManage有一個AlarmManagerService,該服務程式主要維護app註冊下來的各類Alarm,並且一直監聽Alarm裝置,一旦有Alarm觸發,或者是Alarm事件發生,AlarmManagerService就會遍曆Alarm列表,找到相應的註冊Alarm並發出廣播。
Alarm Manager會維持一個cpu的wake lock。這樣能保證電話休眠時,也能處理alarm的廣播。一旦alarm receiver的onReceive() 方法執行完,wake lock會迅速被釋放。如果在receiver中開啟一個service,有可能service還沒啟動,wake lock已經被釋放了。所以此時要實現單獨的wake lock策略。
有4種Alarm類型:
1)RTC_WAKEUP
在指定的時刻(設定Alarm的時候),喚醒裝置來觸發Intent。
2)RTC
在一個顯式的時間觸發Intent,但不喚醒裝置。
3)ELAPSED_REALTIME
從裝置啟動後,如果流逝的時間達到總時間,那麼觸發Intent,但不喚醒裝置。流逝的時間包括裝置睡眠的任何時間。注意一點的是,時間流逝的計算點是自從它最後一次啟動算起。
4)ELAPSED_REALTIME_WAKEUP
從裝置啟動後,達到流逝的總時間後,如果需要將喚醒裝置並觸發Intent。
感應器、圖形
這裡不做詳細的介紹了
8、圖形布局最佳化
-
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/add"/> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/delete"/>
* 使用<include />標籤重用布局
<include layout="@layout/titlebar"/>
延後載入
<ViewStub android:id="@+id/viewstub_demo_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dip" android:layout_marginRight="5dip" android:layout_marginTop="10dip" android:layout="@layout/viewstub_demo_text_layout"/>
布局工具
在sdk->tools目錄下,有hierarchyviewer和layoutopt,可以查看和分析布局
OpenGL ES
OpenGL ES (為OpenGL for Embedded System的縮寫) 為適用於嵌入式系統的一個免費二維和三維圖形庫。
任何複雜的2D或是3D圖形都是通過這三種幾何圖形構造而成的。 OpenGL ES提供了兩類方法來繪製一個空間幾何圖形:
public abstract void glDrawArrays(int mode, int first, int count) 使用VetexBuffer 來繪製,頂點的順序由vertexBuffer中的順序指定。
public abstract void glDrawElements(int mode, int count, int type, Buffer indices) ,可以重新定義頂點的順序,頂點的順序由indices Buffer 指定。
mode列表:GL_POINTS 繪製獨立的點、GL_LINE_STRIP繪製一條線段、GL_LINE_LOOP繪製一條封閉線段(首位相連)、GL_LINES繪製多條線段、GL_TRIANGLES繪製多個三角形(兩兩不相鄰)、GL_TRIANGLE_STRIP繪製多個三角形(兩兩相鄰)、GL_TRIANGLE_FAN以一個點為頂點繪製多個相鄰的三角形 對應頂點除了可以為其定義座標外,還可以指定顏色,材質,法線(用於光照處理)等。 glEnableClientState 和 glDisableClientState 可以控制的pipeline開關可以有:GL_COLOR_ARRAY (顏色) ,GL_NORMAL_ARRAY (法線), GL_TEXTURE_COORD_ARRAY (材質), GL_VERTEX_ARRAY(頂點), GL_POINT_SIZE_ARRAY_OES等。 對應的傳入顏色,頂點,材質,法線的方法如下: glColorPointer(int size,int type,int stride,Buffer pointer) glVertexPointer(int size, int type, int stride, Buffer pointer) glTexCoordPointer(int size, int type, int stride, Buffer pointer) glNormalPointer(int type, int stride, Buffer pointer) OpenGL ES 內部存放圖形資料的Buffer有COLOR ,DEPTH (深度資訊)等,在繪製圖形只前一般需要清空COLOR 和 DEPTH Buffer。
通用的矩陣變換指令 這裡介紹對應指定的座標系(比如viewmodel, projection或是viewport) Android OpenGL ES支援的一些矩陣運算及操作。 矩陣本身可以支援加減乘除,對角線全為1的4X4 矩陣成為單位矩陣Identity Matrix 。
將當前矩陣設為單位矩陣的指令 為glLoadIdentity().
矩陣相乘的指令glMultMatrix*() 允許指定任意矩陣和當前矩陣相乘。
選擇當前矩陣種類glMatrixMode(). OpenGL ES 可以運行指定GL_PROJECTION,GL_MODELVIEW等座標系,後續的矩陣操作將針對選定的座標。
將當前矩陣設定成任意指定矩陣glLoadMatrix*()
在棧中儲存當前矩陣和從棧中恢複所存矩陣,可以使用glPushMatrix()和glPopMatrix()
特定的矩陣變換平移glTranslatef(),旋轉glRotatef() 和縮放glScalef()
紋理壓縮
具體可以在官網看:http://developer.android.com/training/graphics/opengl/index.html
著色、情境複雜性、消隱、渲染、
只有當情境變化才渲染幀。
9、RenderScript
RenderScript 是一種低級的高效能程式設計語言,用於3D渲染和處理密集型計算(3D播放等和關於CPU密集型的計算)。一直以來Android 在繪圖效能的表現一直差強人意,引入NDK之後才有所改善,而在Honeycomb 中發布了RenderScript 這一殺手級在Framework 後,大大的增加了Android 本地語言的執行能力和計算能力。
RenderScript 在機器上進行第一遍編譯,然後在目標裝置上進行最後一遍編譯(Just-In-Time Compiling),因而帶來更高效的原生二進位代碼。這也就是意味著,凡是支援RenderScript 的裝置都可以運行你的代碼。不用管什麼架構。
目前 ,RenderScript 帶來的代碼只能在主處理器上運行,它會自動產生可利用多個核心的代碼(如果裝置上有多個核心)。就因此,編譯出來的程式是針對該機器的最佳最佳化,這解決了Device Fragmentation,也就是說開發人員再也不必擔心使用者的手機、平板夠不夠好、有沒有GPU…等等問題,全都交給RenderScript 去擔心就好。沒有GPU,RenderScript 寫好的程式就交由CPU來處理(背後的編譯技術其實是使用的LLVM)。
安卓效能最佳化手冊