小葵花媽媽課堂開課了:《Handler Looper Message 淺析》
Handler Looper Message Thread
首先要闡述幾者之間的關係。
Thread 可以擁有多個handler物件;
Thread 只能擁有一個Looper 和一個MessageQueue。
Looper 只能屬於一個Thread, 並且只能和MessageQueue 一一對應。 looper的在幾者中的作用是什麼呢!
Looper的作用就是起到 發動機的原理,當然它不是讓車跑起來,而是讓MessageQueue裡的message被執行。
那麼 Message被誰執行呢? 後文即會提到。
MessageQueue 也僅是和一個looper繫結,在出生的時候即決定了這件事,後面在程式碼中會解釋為什麼!
MessageQueue裡面存放就是 Message。
Looper
首先需要關注的是該方法。
public static void prepare() {
prepare(true);
}
引數為是否允許退出,答案是肯定的 true; 只有一種情況即主執行緒呼叫prepare時傳遞false,因為主執行緒不允許退出。
該方法即為 預熱發動機的入口。讓 Looper這臺機器進行啟動之前的準備工作。
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread" );
}
sThreadLocal.set(new Looper(quitAllowed));
}
分析一下 是如何判斷已經prepare的呢?
sThreadLocal.get() != null
那就需要看一下set是什麼東東。就是準備的是什麼呢?
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
這個value就是上文提到的 new Looper(quitAllowed)
createMap建立的是一個ThreadLocalMap。
每一個執行緒僅有一個ThreadLocalMap, 在該map中儲存內容為該執行緒本地變數的副本。ThreadLocalMap使用及注意事項以後單獨開講。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
當第一次sThreadLocal.get()時,會返回setInitialValue=null;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
當一個執行緒只能有一個Looper之後也就意味著只能有一個MessageQueue.class
Looper.loop即為啟動發動機的入口,啟動之後開始進行訊息輪詢,並且註釋說明一定要呼叫quit()退出輪詢。
Looper一直把MesageQueue所有的message執行完。
每執行完一個後即通過next拿到下一個message.
public static void loop() {
---
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
} //當佇列中沒有訊息之後 即退出。
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg); //msg.target即為執行message工具。
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// 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);
}
msg.recycleUnchecked();
}
}
MessageQueue
MessageQueue和Looper之間有個緊密的聯絡就是通過 MessageQueue.next()方法。以next方法為切入點介紹MessageQueue.class
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//natvie層進行阻塞,後文在Looper.c中介紹
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
// 當因為有 "同步分隔欄" 引起停滯後, 將要找到下一個非同步訊息,
// 同步分隔欄後面的同步訊息並不會執行
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
//如果當前的msg沒有準備好,那麼就下次輪詢進入到等待。
//計算等待時間
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
//標記當次輪詢不會被wait,不需要被喚醒
mBlocked = false;
//當在連結串列佇列中找到可執行msg,把當前message調出,並修復原連結
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
//標記當前msg被使用狀態
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
//如果looper呼叫了quit, messagequeue也進行退出操作。
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
// 引入了另外一個messagequeue的功能, idle handles的處理,
// 當佇列為空的時候或沒有任務可執行的時候,執行idle handles內容。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
// 既沒有idle handlers 和message可以處理那麼就需要阻塞,入隊時候就需要喚醒。
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 {
//執行idler中的回撥,並且有返回值,true意味著想要保持這個idle下次繼續執行,
//false則會從佇列中移除
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
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;
}
}
下面繼續介紹enqueueMessage,入隊操作由Handler.class執行。後文提到其中幾種入隊操作。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//如果looper已經條用quit,那麼就放棄入隊。
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
// 如果messagequeue中沒有message或者需要立即執行或者插入message時間優於對頭
// message所需要執行時間,那麼就把msg插入到對頭。
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
//通常情況下將目標message插入到隊裡中間時,是不需要喚醒佇列的,
//除非有一個"同步分隔欄"在對頭或者目標message是最早需要執行的非同步message。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
//找到最後一個位置,或者時間排序上晚於目標message的位置
break;
}
//當需要喚醒,但是 要插入目標message的前面所有位置的message
//只要有非同步訊息的話既不需要喚醒。
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// 將目標message插入到理想位置,修復整個連結
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
//此處為喚醒 Looper
nativeWake(mPtr);
}
}
return true;
}
上面介紹了MessageQueue的兩個主要方法next()和enqueueMessage(),其中涉及到了兩個native層的本地方法分別為:
nativePollOnce(ptr, nextPollTimeoutMillis);
nativeWake(mPtr);
那麼下面介紹一下這兩個方法。
方法在/frameworks/base/core/jni/android_os_MessageQueue.cpp中進行了定義。
static JNINativeMethod gMessageQueueMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "()V", (void*)android_os_MessageQueue_nativeInit },
{ "nativeDestroy", "()V", (void*)android_os_MessageQueue_nativeDestroy },
{ "nativePollOnce", "(II)V", (void*)android_os_MessageQueue_nativePollOnce },
{ "nativeWake", "(I)V", (void*)android_os_MessageQueue_nativeWake }
};
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jint ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(timeoutMillis);
}
最終呼叫到Looper::pollOnce====>Looper::pollInner
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
int Looper::pollInner(int timeoutMillis);
int Looper::pollInner(int timeoutMillis) {
---
#ifdef LOOPER_USES_EPOLL
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
//通過Epoll進行阻塞
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
#else
// Wait for wakeAndLock() waiters to run then set mPolling to true.
mLock.lock();
while (mWaiters != 0) {
mResume.wait(mLock);
}
mPolling = true;
mLock.unlock();
size_t requestedCount = mRequestedFds.size();
int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
#endif
---
}
其中最終運用epoll進行控制(epoll不再本文討論感興趣讀者可自行查詢!
)。下面引入《深入理解Android:卷II》對pollOnce解釋:
其中四個引數:
timeoutMillis引數為超時等待時間。如果值為–1,則表示無限等待,直到有事件發生為止。如果值為0,則無須等待立即返回。
outFd用來儲存發生事件的那個檔案描述符。
outEvents用來儲存在該檔案描述符上發生了哪些事件,目前支援可讀、可寫、錯誤和中斷4個事件。這4個事件其實是從epoll事件轉化而來的。後面我們會介紹大名鼎鼎的epoll。
outData用於儲存上下文資料,這個上下文資料是由使用者在新增監聽控制代碼時傳遞的,它的作用和pthread_create函式最後一個引數param一樣,用來傳遞使用者自定義的資料。
另外,pollOnce函式的返回值也具有特殊的意義,具體如下:
當返回值為ALOOPER_POLL_WAKE時,表示這次返回是由wake函式觸發的,也就是管道寫端的那次寫事件觸發的。
返回值為ALOOPER_POLL_TIMEOUT表示等待超時。
返回值為ALOOPER_POLL_ERROR表示等待過程中發生錯誤。
返回值為ALOOPER_POLL_CALLBACK表示某個被監聽的控制代碼因某種原因被觸發。這時,outFd引數用於儲存發生事件的檔案控制代碼,outEvents用於儲存所發生的事件。
MessageQueue還有其他公開方法:
用來新增IdleHandler,當沒有message需要立即處理時就會處理IdleHandler。
void addIdleHandler(@NonNull IdleHandler handler);
void removeIdleHandler(@NonNull IdleHandler handler);
用來新增需要監聽的檔案描述符fd
void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
@OnFileDescriptorEventListener.Events int events,
@NonNull OnFileDescriptorEventListener listener);
void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd);
Message.class
主要是handler要處理的信使,主要功能攜帶引數。下面主要介紹handler引數。
/**
* User-defined message code so that the recipient can identify
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
*/
public int what;
//定義在handler中要執行的事件
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg1;
//如果要儲存簡單的引數,使用arg1和arg2就可以
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg2;
/**
* An arbitrary object to send to the recipient. When using
* {@link Messenger} to send the message across processes this can only
* be non-null if it contains a Parcelable of a framework class (not one
* implemented by the application). For other data transfer use
* {@link #setData}.
*
* <p>Note that Parcelable objects here are not supported prior to
* the {@link android.os.Build.VERSION_CODES#FROYO} release.
*/
public Object obj;
//可儲存任意型別引數
/**
* Optional Messenger where replies to this message can be sent. The
* semantics of exactly how this is used are up to the sender and
* receiver.
*/
public Messenger replyTo;
//可實現跨程序通訊,後面會獨立章節進行講解。
/**
* Optional field indicating the uid that sent the message. This is
* only valid for messages posted by a {@link Messenger}; otherwise,
* it will be -1.
*/
public int sendingUid = -1;
//與Messenger 配合使用
/*package*/ int flags;
//0x00 非使用, 0x01被使用:當入隊和被回收的時候會設定為1
//0x10 表示為非同步
/*package*/ long when;
//延遲執行時間
/*package*/ Bundle data;
//儲存一些複雜資料
/*package*/ Handler target;
//執行該message的handler
/*package*/ Runnable callback;
//hanlder執行該message時,如果有callback即執行該callback
// sometimes we store linked lists of these things
/*package*/ Message next;
//儲存連結串列
主要解析一下該函式
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
//sPoolSync 同步鎖
synchronized (sPoolSync) {
//sPool指向連結串列的頭
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
//將sPool取出,並斷鏈
return m;
}
}
//如果鏈中沒有元素,重新分配
return new Message();
}
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
//連結串列不會無限增長
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
//將該message插入頭部
}
}
}
Handler
先比較前幾個Class, Handler比較簡單,成員只有以下幾個:
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
先看幾個比較重要的構造方法:
//常用的為無參構造形式
public Handler() {
this(null, false);
}
//這是無參構造方法呼叫的真正構造方法,
public Handler(Callback callback, boolean async) {
//FIND_POTENTIAL_LEAKS
//將此標誌設定為true以檢測擴充套件的Handler類, 擴充套件的handler類如果不是靜態的匿名,本地或成員類,
//則會產生洩漏。我們常見構造時的警告說明!至於消除警告方法一般是設定成靜態或弱引用。
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//mLooper是來自於sThreadLocal中ThreadLocalMap中 通過呼叫執行緒ID儲存的looper,唯一
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//mqueue來自looper, 也唯一
mQueue = mLooper.mQueue;
mCallback = callback;
//標示該handler傳送的資料是否為非同步資料。
mAsynchronous = async;
}
通過分析構造方法可驗證前文提到的
handler 僅對應一個looper MessageQueue,,翻過來不成立,也就是說會有多個handler繫結在同一個Looper中。
通過呼叫post(Runnable r); postDelayed(Runnable r, long delayMillis);sendMessage(Message msg);等方法傳送的時間,最終呼叫下面的方法。
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
//如果為非同步,則對每一個message進行設定。
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//呼叫enqueueMessage 進行入隊Message
return queue.enqueueMessage(msg, uptimeMillis);
}
還有另外一種入隊方法,需要介紹:
public final boolean sendMessageAtFrontOfQueue(Message msg) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//與enqueueMessage差別為uptimeMillis=0. 在messagequeue中當遇到when=0時,
//會將該message放在對頭進行處理
return enqueueMessage(queue, msg, 0);
}
在需要注意下,這個remove方法,當傳入null時可將MessageQueue中的所有資料remove掉。
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
回過頭來說一下上面的 同步分隔欄,
在Api 23 之, 通過MessageQueue 進行呼叫
/**
* Posts a synchronization barrier to the Looper's message queue.
*
* Message processing occurs as usual until the message queue encounters the
* synchronization barrier that has been posted. When the barrier is encountered,
* later synchronous messages in the queue are stalled (prevented from being executed)
* until the barrier is released by calling {@link #removeSyncBarrier} and specifying
* the token that identifies the synchronization barrier.
*
* This method is used to immediately postpone execution of all subsequently posted
* synchronous messages until a condition is met that releases the barrier.
* Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
* and continue to be processed as usual.
*
* This call must be always matched by a call to {@link #removeSyncBarrier} with
* the same token to ensure that the message queue resumes normal operation.
* Otherwise the application will probably hang!
*
* @return A token that uniquely identifies the barrier. This token must be
* passed to {@link #removeSyncBarrier} to release the barrier.
*
* @hide
*/
// 該方法為hide, 正常寫程式碼是呼叫不到的。
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
// The next barrier token.
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
// 同步分隔欄訊息沒有target, 並且arg1用來記錄token
private int mNextBarrierToken;
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
這個同步分隔message有什麼作用呢?
對開發者沒有明顯的提供,那麼就是在系統及別使用。在ViewRootImpl.java中進行了使用。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
為了讓View能夠有快速的佈局和繪製,ViewRootImpl在開始measure和layout ViewTree時,會向主執行緒的Handler新增同步分隔message,這樣後續的訊息佇列中的同步的訊息將不會被執行,以免會影響到UI繪製,但是隻有非同步訊息才能被執行。如果想要使用postSyncBarrier() 那麼就需要使用反射進行使用。
總結
Looper、MessageQueue 和 Thread 一一對應。
Handler 需要繫結到一個Looper中, 一個Looper可以有多個Handler。
這是第一文章,以後會多多寫的。歡迎各位指正問題!謝謝。
[email protected]
相關推薦
小葵花媽媽課堂開課了:《Handler Looper Message 淺析》
Handler Looper Message Thread 首先要闡述幾者之間的關係。 Thread 可以擁有多個handler物件; Thread 只能擁有一個Looper 和一個MessageQueue。 Looper 只能屬於一個Thread, 並
Android深入理解:Handler + Looper + Message
宣告:本文是一篇對Handler相關內容的整理(經過相當一段時間,幾次內容增減),有相當部分內容來源網路,其中融入部分作者本身的理解,並加以整理。如有涉及到哪位老師的原作,在此深表感謝! 目錄 Handler + Looper + Message:生產者 + 消費者 + 倉
WebApp 安全風險與防護課堂開課了!
本文由葡萄城技術團隊於原創並首發 轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。 2018 網路安全事故頻發,從資料洩露、資訊竊取,到 DDOS 攻擊、勒索病毒,不僅威脅的總數在增加,威脅態勢也變得更加多樣化,攻擊者在不斷開發新的攻擊途徑
Android非同步處理三:Handler+Looper+MessageQueue深入詳解
在《Android非同步處理一:使用Thread+Handler實現非UI執行緒更新UI介面》中,我們講到使用Thread+Handler的方式來實現介面的更新,其實是在非UI執行緒傳送訊息到UI執行緒,通知UI執行緒進行介面更新,這一篇我們將深入學習And
今天做到一道面試題:Handler+Looper+MessageQueue+Message的關係
Handler+Looper+MessageQueue+Message的關係 Handler+Looper+MessageQueue這三者的關係其實就是Android的訊息機制。這塊內容相比開發人員都不陌生,在面試中,或者日常開發中都會碰到,今天就來講這三者的關係。 概述
Android非同步處理:Handler+Looper+MessageQueue深入詳解
概述:Android使用訊息機制實現執行緒間的通訊,執行緒通過Looper建立自己的訊息循 環,MessageQueue是FIFO的訊息佇列,Looper負責從MessageQueue中取出訊息,並且分發到訊息指定目標Handler對 象。Handler物件繫結到執行緒的區域性變數Looper,封裝了傳送訊
Handler Looper Message的原始碼分析
Handler Handler是用來分發和處理訊息的,通常我們建立Handler都是使用其無引數的構造方法 public Handler() { this(null, false); } 其內部呼叫的是2個引數的構造方法 public Handler(Ca
Handler,Looper,Message,MessageQueue,HandlerThread使用總結(上)
在安卓程式中,經常會有一些耗時的操作例如下載,網路訪問等,如果將這些放在主執行緒執行,會很耗時,這樣可能會導致一個異常 叫ANR異常(Application Not Responding)將會阻塞UI執行緒,從而會導致程式無響應。因此我們會將一些耗時操作放在子執行緒進行,
小編帶你簡單了解一下加密技術原理:AES加密標準
數據 aes加密 體制 結構 方向 插值 空間 基於 領域 隨著因特網的發展,信息傳輸及存儲的安全問題成為影響因特網應用發展的重要因素。信息安全技術也就成為了人們研究因特網應用的新熱點。 信息安全的研究包括密碼理論與技術、安全協議與技術、安全體系結構理論、信息對抗理論與技術
小i機器人又雙叒叕接工作了:醫療智慧問詢機器人“貝貝”上崗
2018年10月31日,名叫“貝貝”的實體互動智慧問訊機器人在復旦大學附屬兒科醫院門診大廳上崗,現場解答患者疑問。不僅大大方便了廣大患兒及家長,而且著實減輕了門診問詢視窗工作人員的工作強度。 醫院向來人滿為患,特別是在兒童醫院,有寶寶生病的家長都是恨不得第一時間見到大夫。“貝貝”的及時響應在
真的炸了:讓人頭痛的小程式之『圖片懶載入』終極解決方案
轉載:請寫明原文連結及作者名 ‘小小小’ 小程式真的會取代一切?QQ群:139128168 ← 點選加群 微信小程式中,懶載入特效讓人頭疼不已,因為小程式完全沒法操作dom,所以位置的操作在小程式中,變得極其的難~~ 先看特效: 我們將其拆分為如
WebApp 安全風險與防護課堂(第二講)開課了!
本文由葡萄城技術團隊於原創並首發 轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。 在昨天的公開課中,由於參與的小夥伴們積極性和熱情非常高,我們的講師Carl(陳慶)把原定第二講的大部分也一併獻出了,所以原定三場的公開課也變為了兩場,本系列
再不努力提高效率,小姐姐都被人追走了:K8S一鍵部署瞭解一下?
隨著網際網路時代的不斷髮展,開發者可能會面臨這樣的困境:為了解決問題、提升開發效率而竭力研發出來的“創新”,似乎削弱了他們在公司的重要程度,甚至取代了他們原先的地位。比如,在雲原生時代,部分企業更願意選擇 K8s 來解決運維、彈性的問題,而不是組建一支需要耗費大量僱傭資金、管理資金的研發團隊。 對於 K8s,
讀車神探來了:上海車展大爆移動視頻商業化圖謀
上海車展 長安汽車 人民網 業內人士 國際車展 作為重磅國際車展之一的上海車展已經於上月28日正式閉幕,據不完全統計,展會期間共接待觀眾近26萬人,現場購車及訂車16000余輛,銷售金額近24.3億元人民幣。文/張書樂TMT行業觀察者、遊戲產業時評人,人民網、人民郵電報專欄作者但較以往車展
數據說話了:6萬一房成“全球第二貴“ 狂跌的深圳房價5月又回暖?
國家 swd src alt 數據庫 還在 繼續 webp 數據管理 深圳是一個房價不低的城市,這點沒人敢說否。的確,深圳經過20年的樓市發展,從05年的6000元,到2008年的1萬,到2005年的3.5萬,到2017年的5萬,這就是房價的歷程。 但是3月樓市
Android實戰技巧之三十八:Handler使用中可能引發的內存泄漏
sha 指向 ons har 引用 destroy 對象 from weak 問題描寫敘述 曾幾何時,我們用原來的辦法使用Handler時會有以下一段溫馨的提示: This Handler class should be static or le
微信小程序自學第三課:文件作用域,模塊化
數據 變量 span data ava 有效 函數 方法 oba 一、文件作用域 在 JavaScript 文件中聲明的變量和函數只在該文件中有效;不同的文件中可以聲明相同名字的變量和函數,不會互相影響。 通過全局函數 getApp() 可以獲取全局的應用實例,如果需要
強烈推薦 在線接口文檔管理工具 小幺雞 小團隊可以省掉測試了
des 管理工具 margin 之前 接口 示例 註意事項 order 阿裏雲 在朋友那兒看到一個不錯的在線文檔管理工具 主要特點 : 在線接口測試在線測試,方便前後端開發,降低錯誤率。支持:xml、json、txt、binary、websocket可視化編輯與分享
<小田吃餃子> LINUX:Contos7.0 / 7.2 LAMP+R 下載安裝Php篇
ssl itl 搭建 hle file 使用 mcr not soc 更新時間:2017-09-21 16:03 簡介 LAMP+R指Linux+Apache+Mysql+PHP+Redis是一組常用來搭建動態網站或者服務器的開源軟件,本身都是各自獨立的程序,但是因為常
<小田吃餃子> LINUX:Contos7.0 / 7.2 LAMP+R 下載安裝Redis篇
php+redis pac apache ron 在一起 tor blank amp .cn 更新時間:2017-09-21 15:38 簡介 LAMP+R指Linux+Apache+Mysql+PHP+Redis是一組常用來搭建動態網站或者服務器的開源軟件,本身都是各自獨