A. SystemClock.sleep和Thread.sleep的區別
在java中我們處理線程同步問題時,處理延遲可能會使用Thread類的sleep方法,這里拋開concurrent類的一些方法,其實 android平台還提供了一個SystemClock.sleep方法,它們有什麼區別呢?
我們每次調用Thread.sleep時可能會出現InterruptedException異常,而SystemClock.sleep方法則不會,在 SDK上有這樣的描述,它將會忽略中斷異常。
Three different clocks are available, and they should not be confused:
System.currentTimeMillis() is the standard "wall" clock (time and date) expressing milliseconds since the epoch. The wall clock can be set by the user or the phone network (see setCurrentTimeMillis(long)), so the time may jump backwards or forwards unpredictably. This clock should only be used when correspondence with real-world dates and times is important, such as in a calendar or alarm clock application. Interval or elapsed time measurements should use a different clock. If you are using System.currentTimeMillis(), consider listening to the ACTION_TIME_TICK, ACTION_TIME_CHANGED and ACTION_TIMEZONE_CHANGED Intent broadcasts to find out when the time changes.
uptimeMillis() is counted in milliseconds since the system was booted. This clock stops when the system enters deep sleep (CPU off, display dark, device waiting for external input), but is not affected by clock scaling, idle, or other power saving mechanisms. This is the basis for most interval timing such as Thread.sleep(millls), Object.wait(millis), and System.nanoTime(). This clock is guaranteed to be monotonic, and is the recommended basis for the general purpose interval timing of user interface events, performance measurements, and anything else that does not need to measure elapsed time ring device sleep. Most methods that accept a timestamp value expect the uptimeMillis() clock.
elapsedRealtime() is counted in milliseconds since the system was booted, including deep sleep. This clock should be used when measuring time intervals that may span periods of system sleep.
There are several mechanisms for controlling the timing of events:
Standard functions like Thread.sleep(millis) and Object.wait(millis) are always available. These functions use the uptimeMillis() clock; if the device enters sleep, the remainder of the time will be postponed until the device wakes up. These synchronous functions may be interrupted with Thread.interrupt(), and you must handle InterruptedException.
SystemClock.sleep(millis) is a utility function very similar to Thread.sleep(millis), but it ignores InterruptedException. Use this function for delays if you do not use Thread.interrupt(), as it will preserve the interrupted state of the thread.
B. Android應用性能優化的內容簡介
今天的Android應用開發者經常要想盡辦法來提升程序性能。由於應用越來越復雜,這個問題也變得越來越棘手。本書主要介紹如何快速高效地優化應用,讓應用變得穩定高效。你將學會利用Android SDK和NDK來混合或單獨使用Java、C/C++來開發應用。書中還特別講解了如下內容:
· 一些OpenGL的優化技術以及RenderScript(Android的新特性)的基礎知識;
· 利用SDK來優化應用的Java代碼的技巧;
· 通過高效使用內存來提升性能的技巧;
· 延長電池使用時間的技巧;
· 使用多線程的時機及技巧;
· 評測剖析代碼的技巧。
把本書的內容學以致用,你的編程技術就會得到關鍵性的提升,寫出的應用就會更為健壯高效,從而廣受用戶好評,並最終獲得成功。
目錄
第1章Java代碼優化1.1Android如何執行代碼1.2優化斐波納契數列1.2.1從遞歸到迭代1.2.2BigInteger1.3緩存結果1.4API等級1.5數據結構1.6響應能力1.6.1推遲初始化1.6.2StrictMode1.7SQLite1.7.1SQLite語句1.7.2事務1.7.3查詢
第1章Java代碼優化1.1Android如何執行代碼1.2優化斐波納契數列1.2.1從遞歸到迭代1.2.2BigInteger1.3緩存結果1.4API等級1.5數據結構1.6響應能力1.6.1推遲初始化1.6.2StrictMode1.7SQLite1.7.1SQLite語句1.7.2事務1.7.3查詢1.8總結
第2章NDK入門2.1NDK里有什麼2.2混合使用Java和C/C++代碼2.2.1聲明本地方法2.2.2實現JNI粘合層2.2.3創建Makefile2.2.4實現本地函數2.2.5編譯本地庫2.2.6載入本地庫2.3Application.mk2.3.1為(幾乎)所有設備優化2.3.2支持所有設備2.4Android.mk2.5使用C/C++改進性能2.6本地Acitivity2.6.1構建缺失的庫2.6.2替代方案2.7總結
第3章NDK進階3.1匯編3.1.1最大公約數3.1.2色彩轉換3.1.3並行計算平均值3.1.4ARM指令3.1.5ARM NEON3.1.6CPU特性3.2C擴展3.2.1內置函數3.2.2向量指令3.3技巧3.3.1內聯函數3.3.2循環展開3.3.3內存預讀取3.3.4用LDM/STM替換LDR/STD3.4總結
第4章高效使用內存4.1說說內存4.2數據類型4.2.1值的比較4.2.2其他演算法4.2.3數組排序4.2.4定義自己的類4.3訪問內存4.4排布數據4.5垃圾收集4.5.1內存泄漏4.5.2引用4.6API4.7內存少的時候4.8總結
第5章多線程和同步5.1線程5.2AsyncTask5.3Handler和Looper5.3.1Handler5.3.2Looper5.4數據類型5.5並發5.6多核5.6.1為多核修改演算法5.6.2使用並發緩存5.7Activity生命周期5.7.1傳遞信息5.7.2記住狀態5.8總結
第6章性能評測和剖析6.1時間測量6.1.1System.nanoTime()6.1.2Debug.threadCpuTimeNanos()6.2方法調用跟蹤6.2.1Debug.startMethodTracing()6.2.2使用Traceview工具6.2.3DDMS中的Traceview6.2.4本地方法跟蹤6.3日誌6.4總結
第7章延長電池續航時間7.1電池7.2禁用廣播接收器7.3網路7.3.1後台數據7.3.2數據傳輸7.4位置7.4.1注銷監聽器7.4.2更新頻率7.4.3多種位置服務7.4.4篩選定位服務7.4.5最後已知位置7.5感測器7.6圖形7.7提醒7.8WakeLock7.9總結
第8章圖形8.1布局優化8.1.1相對布局8.1.2合並布局8.1.3重用布局8.1.4ViewStub8.2布局工具8.2.1層級視圖8.2.2layoutopt8.3OpenGL ES8.3.1擴展8.3.2紋理壓縮8.3.3Mipmap8.3.4多APK8.3.5著色8.3.6場景復雜性8.3.7消隱8.3.8渲染模式8.3.9功耗管理8.4總結
第9章RenderScript9.1概覽9.2Hello World9.3Hello Rendering9.3.1創建渲染腳本9.3.2創建RenderScriptGL Context9.3.3展開RSSurfaceView9.3.4設置內容視圖9.4在腳本中添加變數9.5HelloCompute9.5.1Allocation9.5.2rsForEach9.5.3性能9.6自帶的RenderScript API9.6.1rs_types.rsh9.6.2rs_core.rsh9.6.3rs_cl.rsh9.6.4rs_math.rsh9.6.5rs_graphics.rsh9.6.6rs_time.rsh9.6.7rs_atomic.rsh9.7RenderScript與NDK對比9.8總結
C. 求助,Android裡面的seekbar怎麼垂直顯示
把seekbar復寫了。。旋轉90度就可以了。
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.SeekBar;
public class SlideBar extends SeekBar {
private int oHeight = 100;
private int oWidth = 30;
private int oProgress = -1;
private int oOffset = -1;;
private float xPos = -1;
private float yPos = -1;
private int top = -1;
private int bottom = -1;
private int left = -1;
private int right = -1;
public SlideBar(Context context) {
super(context);
}
public SlideBar(Context context, AttributeSet attrs) {
super(context, attrs);
oOffset = this.getThumbOffset();
oProgress = this.getProgress();
}
public SlideBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = View.MeasureSpec.getSize(heightMeasureSpec);
oHeight = height;
this.setMeasuredDimension(oWidth, oHeight);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldw, oldh);
}
// 設置layout
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
left = l;
right = r;
top = t;
bottom = b;
}
// 畫出滾動條
protected void onDraw(Canvas c) {
// 旋轉
c.rotate(90);
//控制左右位置
c.translate(0,-30);
super.onDraw(c);
}
public boolean onTouchEvent(MotionEvent event) {
xPos = event.getX();
yPos = event.getY();
float progress = 1-(yPos-this.getTop())/(this.getBottom()- this.getTop());
oOffset = this.getThumbOffset();
oProgress = this.getProgress();
Log.d("offset" + System.nanoTime(), new Integer(oOffset).toString());
Log.d("progress" + System.nanoTime(), new Integer(oProgress).toString());
float offset;
offset = progress * (this.getBottom() - this.getTop());
this.setThumbOffset((int)offset);
Log.d("offset_postsetprogress" + System.nanoTime(), new Integer(oOffset).toString());
Log.d("progress_postsetprogress" + System.nanoTime(), new Integer(oProgress).toString());
// this.setProgress((int)(100*event.getY()/this.getBottom()));
this.setProgress((int)(100 * progress));
return true;
}
}
D. android中mediamuxer和mediacodec的區別
Android中MediaMuxer和MediaCodec用例
在Android的多媒體類中,MediaMuxer和MediaCodec算是比較年輕的,它們是JB 4.1和JB 4.3才引入的。前者用於將音頻和視頻進行混合生成多媒體文件。缺點是目前只能支持一個audio track和一個video track,而且僅支持mp4輸出。不過既然是新生事物,相信之後的版本應該會有大的改進。MediaCodec用於將音視頻進行壓縮編碼,它有個比較牛X的地方是可以對Surface內容進行編碼,如KK 4.4中屏幕錄像功能就是用它實現的。
注意它們和其它一些多媒體相關類的關系和區別:MediaExtractor用於音視頻分路,和MediaMuxer正好是反過程。MediaFormat用於描述多媒體數據的格式。MediaRecorder用於錄像+壓縮編碼,生成編碼好的文件如mp4, 3gpp,視頻主要是用於錄制Camera preview。MediaPlayer用於播放壓縮編碼後的音視頻文件。AudioRecord用於錄制PCM數據。AudioTrack用於播放PCM數據。PCM即原始音頻采樣數據,可以用如vlc播放器播放。當然了,通道采樣率之類的要自己設,因為原始采樣數據是沒有文件頭的,如:
vlc --demux=rawaud --rawaud-channels 2 --rawaud-samplerate 44100 audio.pcm
回到MediaMuxer和MediaCodec這兩個類,它們的參考文檔見http://developer.android.com/reference/android/media/MediaMuxer.html和http://developer.android.com/reference/android/media/MediaCodec.html,里邊有使用的框架。這個組合可以實現很多功能,比如音視頻文件的編輯(結合MediaExtractor),用OpenGL繪制Surface並生成mp4文件,屏幕錄像以及類似Camera app里的錄像功能(雖然這個用MediaRecorder更合適)等。
這里以一個很無聊的功能為例,就是在一個Surface上畫圖編碼生成視頻,同時用MIC錄音編碼生成音頻,然後將音視頻混合生成mp4文件。程序本身沒什麼用,但是示例了MediaMuxer和MediaCodec的基本用法。本程序主要是基於兩個測試程序:一個是Grafika中的SoftInputSurfaceActivity和HWEncoderExperiments。它們一個是生成視頻,一個生成音頻,這里把它們結合一下,同時生成音頻和視頻。基本框架和流程如下:
首先是錄音線程,主要參考HWEncoderExperiments。通過AudioRecord類接收來自麥克風的采樣數據,然後丟給Encoder准備編碼:
AudioRecord audio_recorder;
audio_recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, buffer_size);
// ...
audio_recorder.startRecording();
while (is_recording) {
byte[] this_buffer = new byte[frame_buffer_size];
read_result = audio_recorder.read(this_buffer, 0, frame_buffer_size); // read audio raw data
// …
presentationTimeStamp = System.nanoTime() / 1000;
audioEncoder.offerAudioEncoder(this_buffer.clone(), presentationTimeStamp); // feed to audio encoder
}
這里也可以設置AudioRecord的回調(通過())來觸發音頻數據的讀取。offerAudioEncoder()里主要是把audio采樣數據送入音頻MediaCodec的InputBuffer進行編碼:
ByteBuffer[] inputBuffers = mAudioEncoder.getInputBuffers();
int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(this_buffer);
...
mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, this_buffer.length, presentationTimeStamp, 0);
}
下面,參考Grafika-SoftInputSurfaceActivity,並加入音頻處理。主循環大體分四部分:
try {
// Part 1
prepareEncoder(outputFile);
...
// Part 2
for (int i = 0; i < NUM_FRAMES; i++) {
generateFrame(i);
drainVideoEncoder(false);
drainAudioEncoder(false);
}
// Part 3
...
drainVideoEncoder(true);
drainAudioEncoder(true);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
} finally {
// Part 4
releaseEncoder();
}
第1部分是准備工作,除了video的MediaCodec,這里還初始化了audio的MediaCodec:
MediaFormat audioFormat = new MediaFormat();
audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
...
mAudioEncoder = MediaCodec.createEncoderByType(AUDIO_MIME_TYPE);
mAudioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mAudioEncoder.start();
第2部分進入主循環,app在Surface上直接繪圖,由於這個Surface是從MediaCodec中用createInputSurface()申請來的,所以畫完後不用顯式用queueInputBuffer()交給Encoder。drainVideoEncoder()和drainAudioEncoder()分別將編碼好的音視頻從buffer中拿出來(通過dequeueOutputBuffer()),然後交由MediaMuxer進行混合(通過writeSampleData())。注意音視頻通過PTS(Presentation time stamp,決定了某一幀的音視頻數據何時顯示或播放)來同步,音頻的time stamp需在AudioRecord從MIC採集到數據時獲取並放到相應的bufferInfo中,視頻由於是在Surface上畫,因此直接用dequeueOutputBuffer()出來的bufferInfo中的就行,最後將編碼好的數據送去MediaMuxer進行多路混合。
注意這里Muxer要等把audio track和video track都加入了再開始。MediaCodec在一開始調用dequeueOutputBuffer()時會返回一次INFO_OUTPUT_FORMAT_CHANGED消息。我們只需在這里獲取該MediaCodec的format,並注冊到MediaMuxer里。接著判斷當前audio track和video track是否都已就緒,如果是的話就啟動Muxer。
總結來說,drainVideoEncoder()的主邏輯大致如下,drainAudioEncoder也是類似的,只是把video的MediaCodec換成audio的MediaCodec即可。
while(true) {
int encoderStatus = mVideoEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
...
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
encoderOutputBuffers = mVideoEncoder.getOutputBuffers();
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat newFormat = mAudioEncoder.getOutputFormat();
mAudioTrackIndex = mMuxer.addTrack(newFormat);
mNumTracksAdded++;
if (mNumTracksAdded == TOTAL_NUM_TRACKS) {
mMuxer.start();
}
} else if (encoderStatus < 0) {
...
} else {
ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
...
if (mBufferInfo.size != 0) {
mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo);
}
mVideoEncoder.releaseOutputBuffer(encoderStatus, false);
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
break;
}
}
}
第3部分是結束錄制,發送EOS信息,這樣在drainVideoEncoder()和drainAudioEncoder中就可以根據EOS退出內循環。第4部分為清理工作。把audio和video的MediaCodec,MediaCodec用的Surface及MediaMuxer對象釋放。
最後幾點注意:
1. 在AndroidManifest.xml里加上錄音許可權,否則創建AudioRecord對象時鐵定失敗:
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
2. 音視頻通過PTS同步,兩個的單位要一致。
3. MediaMuxer的使用要按照Constructor -> addTrack -> start -> writeSampleData -> stop 的順序。如果既有音頻又有視頻,在stop前兩個都要writeSampleData()過。
Code references:
Grafika: https://github.com/google/grafika
Bigflake: http://bigflake.com/mediacodec/
HWEncoderExperiments:https://github.com/OnlyInAmerica/HWEncoderExperiments/tree/audioonly/HWEncoderExperiments/src/main/java/net/openwatch/hwencoderexperiments
Android test:http://androidxref.com/4.4.2_r2/xref/cts/tests/tests/media/src/android/media/cts/
http://androidxref.com/4.4.2_r2/xref/pdk/apps/TestingCamera2/src/com/android/testingcamera2/CameraRecordingStream.java