編輯:關於Android編程
從一接觸Android開始,就聽說了消息機制,那時候對消息機制的理解也只是看看別人博客,了解一些概念和相關的類而已,並沒有深入的去了解和探究。直到最近,重新過了一遍消息機制和相關源碼,這裡做簡單的整理和記錄,畢竟好記性不如爛筆頭。如果有什麼問題,還請大家指出。(注:源碼版本 4.0)
基礎示例:
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
//報內存洩露警告的handler
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {//處理發送過來的消息
if (msg.arg1 == 1) {
Log.i(TAG, "1消息已被接收");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sendMessageToHandler();
}
/**
* 向Handler發送消息
*/
private void sendMessageToHandler() {
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.arg1 = 1;
handler.sendMessage(message);
}
}).start();
}
}
使用過Handler的朋友都知道,上面的使用方式Android Lint會給出內存洩露警告,具體內容如下:
This Handler class should be static or leaks might occur (anonymous android.os.Handler) less...
Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.
Handler
該類負責Message
的發送和處理
發送消息的核心源碼如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
//關鍵代碼。發送消息的實質,其實是向消息隊列中插入一條消息
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
處理消息的核心源碼如下:
//該方法對Message進行了分發處理
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
...
private final void handleCallback(Message message) {
message.callback.run();
}
...
public interface Callback {
public boolean handleMessage(Message msg);
}
...
public void handleMessage(Message msg) {
}
Message
該類負責Message對象的創建和釋放
Message的創建obtain()
//該方法有很多的重載方法,都是在此方法的基礎上擴展而來,其他方法不再列舉
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
Message的釋放
public void recycle() {
clearForRecycle();
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
void clearForRecycle() {
flags = 0;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
when = 0;
target = null;
callback = null;
data = null;
}
ThreadLocal
ThreadLocal的作用是提供線程內的局部變量,這種變量在線程的生命周期內起作用,減少同一個線程內多個函數或者組件之間一些公共變量的傳遞的復雜度。這裡給出參考博客鏈接:http://qifuguang.me/2015/09/02/[Java%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83]%E8%A7%A3%E5%AF%86ThreadLocal/
ThreadLocal中的set()
//獲取當前線程的Values對象,如果當前線程的Values不存在,則重新實例化一個
//否則將value添加到table數組中
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
ThreadLocal中的get()
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
//Values中的put()方法是將索引和值在相鄰位置進行存儲的,具體實現查看put()源碼
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
Values 中的put()
void put(ThreadLocal key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
//數組中相鄰的位置存儲索引和值
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
Looper
該類是一個循環控制器
Looper的准備
static final ThreadLocal sThreadLocal = new ThreadLocal();
...
private Looper() {
mQueue = new MessageQueue();//創建消息隊列對象
mRun = true;
mThread = Thread.currentThread();//獲取當前線程
}
...
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//將當前線程的Looper對象傳遞給ThreadLocal
sThreadLocal.set(new Looper());
}
Looper循環控制器的啟動
public static Looper myLooper() {
//從ThreadLocal中獲取Looper對象,這裡可看出他們操作的是同一個ThreadLocal對象
return sThreadLocal.get();
}
...
public static void loop() {
//啟動loop方法必須有looper對象,否則會報下面的異常
Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//獲取Looper中的消息隊列
MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//無限循環,從MessageQueue中不斷的獲取Message
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
//唯一退出循環的條件
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
long wallStart = 0;
long threadStart = 0;
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
wallStart = SystemClock.currentTimeMicro();
threadStart = SystemClock.currentThreadTimeMicro();
}
//分發Message到Handler中進行處理
msg.target.dispatchMessage(msg);
if (logging != null) {
long wallTime = SystemClock.currentTimeMicro() - wallStart;
long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
if (logging instanceof Profiler) {
((Profiler) logging).profile(msg, wallStart, wallTime,
threadStart, threadTime);
}
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
//釋放Message
msg.recycle();
}
}
}
退出Looper quite()
//向MessageQueue中插入一個target為null的Message,會退出Looper
//退出loop()的唯一條件 if (msg.target == null){return;}
public void quit() {
Message msg = Message.obtain();
// NOTE: By enqueueing directly into the message queue, the
// message is left with a null target. This is how we know it is
// a quit message.
mQueue.enqueueMessage(msg, 0);
}
MessageQueue
該類負責Message的插入,取出,以及移除
插入Message方法enqueueMessage()源碼
//Handler中的sendMessage()方法最終會調用此方法,所以說發送消息其實是向MessageQueue中插入一條消息
final boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
final boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
取出和移除
final Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
//又一個無限循環
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr, nextPollTimeoutMillis);
//當有Message時,取出Message,否則該方法將會一直堵塞
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
final Message msg = mMessages;
if (msg != null) {
final long when = msg.when;
if (now >= when) {
mBlocked = false;
mMessages = msg.next;
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;
} else {
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
}
} else {
nextPollTimeoutMillis = -1;
}
// If first time, then get the number of idlers to run.
if (pendingIdleHandlerCount < 0) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount == 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf("MessageQueue", "IdleHandler threw exception", t);
}
//當有Message時,返回Message後,刪除Message
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
首先,使用Handler必須保證其所在的線程中具有Looper對象,但是有的朋友會說,我在Activity中並沒有創建Looper對象,但是我也可以使用Handler啊。其實Activity所在的主線程(ActivityThread)也就是UI線程是特殊的線程,該線程在創建的時候就已經創建了Looper對象,看源碼
ActivityThread的main()方法
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Process.setArgV0("");
Looper.prepareMainLooper();//准備工作
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();//啟動循環控制器
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper中的prepareMainLooper()方法是專門用與主線程的
public static void prepareMainLooper() {
prepare();//最後還是調用了prepare(),從而創建了Looper對象,並設置給了ThreadLocal
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed = false;
}
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
以上工作准備完成後,Looper,ThreadLocal已經創建完畢,並且建立了關聯關系。接下來創建Handler對象,獲取Message對象(Message.obtain()),使用Handler中的sendMessage()方法向MessageQueue中插入Message(enqueueMessage()),這時候Looper對象中處於堵塞狀態的next()方法檢測到Message後,將其從MessageQueue中取出,傳遞給Handler的dispatchMessage()方法後,清除該Message(msg.recycle();),最後dispatchMessage()方法,將Message分發到run()方法或者handlerMessage(Message msg)中進行處理。
最後來一張消息機制的示意圖
內存洩露會在什麼情況下產生呢?這裡給出一個示例
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
Log.i(TAG,"post delay");
}
},10*60*1000);
finish();
}
示例中,當Activity被finish()掉,Message 將存在於消息隊列中長達10分鐘的時間才會被執行到。這個Message持有一個對Handler的引用,Handler也會持有一個對於外部類(SampleActivity)的隱式引用,這些引用在Message被執行前將一直保持,這樣會保證Activity的上下文不被垃圾回收機制回收,同時也會洩露應用程序的資源(views and resources)。
Handler的警告提示中已經說明,因為靜態匿名內部類不會持有外部類的隱式引用,因此我們創建一個靜態的Handler子類。如果需要調用外部類(例如:Activity)的方法,就讓Handler持有一個Activity的弱引用(WeakReference),這樣就不會洩露Activity的上下文了。示例:
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
private MyHandler myHandler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sendMessageToHandler();
}
/**
* 向Handler發送消息
*/
private void sendMessageToHandler() {
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.arg2 = 2;
myHandler.sendMessage(message);
}
}).start();
}
/**
* MyHandler的處理方法
*
* @param msg 消息
*/
private void handlerMessage(Message msg) {
if (msg.arg2 == 2) {
Log.i(MainActivity.TAG, "2已接收到消息");
}
}
private static class MyHandler extends Handler {
//弱引用,避免Handler持有外部類的引用,即MainActivity的引用,
// 這樣會導致MainActivity的上下文及資源無法被回收,引發內存洩露的情況發生
private WeakReference weakReference;
public MyHandler(MainActivity mainActivity) {
weakReference = new WeakReference<>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
MainActivity mainActivity = weakReference.get();
mainActivity.handlerMessage(msg);
}
}
}
繼上一篇介紹了如何使用Gallery控件之後,本文就來講一下Gallery 與ImageSwitcher的結合使用。本文所述實例代碼將實現一個簡單的浏覽圖片的功能。先貼出
今天使用了ded做逆向分析,瞬間比Apktool高大上了,功能太強大了,不過還有升級版,明天研究。吼吼~ 1.安裝ded 下載鏈接:http://siis.
主要實現辦法:動態加載各級下拉值的適配器 在監聽本級下拉框,當本級下拉框的選中值改變時,隨之修改下級的適配器的綁定值
怎麼給手機qq設密碼手勢?下面小編就來告訴大家蘋果手機qq怎麼設置手勢密碼,感興趣的朋友就一起來看看吧!蘋果手機qq設置手勢密碼教程1.在你的手機上面登陸你