当初跟踪Camera的代码中的时候一直追到了HAL层,而在Framework中的代码看见了许很多多的Thread。它们普遍的特点就是有一个threadLoop方法。依照字面的意思应该是这个线程能够循环处理数据。相应我想到到了java上层中的HandlerThread,这个预计也差点儿相同,但当时心里总有一个疙瘩。想弄清楚它为什么能够循环。还有它究竟是怎么循环起来的?

Android中java世界的Thread

我们先来看看java是怎么创建一个线程的。这个是最舒服的,也是我最熟悉的。


new Thread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
...
}
}).start();

当然,你也能够在android中创建一个消息循环的HandlerThread

HandlerThread mThread = new HandlerThread("test");
mThread.start();
Handler mHandler = new Handler(mThread.getLooper()){ @Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
} };

上面中通过mHandler发送消息就能够在mThread中处理了。而且这个mThread不是UIThread,不会堵塞主线程。

HandlerThread是一个好东西,在源代码中处处可见,希望对此不熟悉的新手及时去学习下它的使用方法。

Linux下c语言的Thread

java世界的Thread非常方便,那么c呢?

Android基于linux所以,多线程编程也应该基于linux下的多线程。linux下的c语言用pthread。大家能够看这篇文章。

linux下C/C++,多线程pthread

我把里面的样例改良了一下

test.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h> //线程函数
void *test(void *ptr)
{
int i;
for(i=0;i<8;i++)
{
printf("the pthread running ,count: %d\n",i);
sleep(1);
} } int main(void)
{
pthread_t pId;
int i,ret;
//创建子线程,线程id为pId
ret = pthread_create(&pId,NULL,test,NULL); if(ret != 0)
{
printf("create pthread error!\n");
exit(1);
} for(i=0;i < 5;i++)
{
printf("main thread running ,count : %d\n",i);
sleep(1);
} printf("main thread will exit when pthread is over\n");
//等待线程pId的完毕
pthread_join(pId,NULL);
printf("main thread exit\n"); return 0; }

然后编译

gcc -o test test.c -lpthread
./test

执行结果例如以下

main thread running ,count : 0
the pthread running ,count: 0
main thread running ,count : 1
the pthread running ,count: 1
main thread running ,count : 2
the pthread running ,count: 2
main thread running ,count : 3
the pthread running ,count: 3
main thread running ,count : 4
the pthread running ,count: 4
main thread will exit when pthread is over
the pthread running ,count: 5
the pthread running ,count: 6
the pthread running ,count: 7
main thread exit

样例比較简单,主要是创建一个线程。然后主线程等待子线程执行完毕再退出。

Android Framework中的Thread

以下焦点回到文章的主题其中,我们来看看Framework中经常使用的Thread是个何种形态。

先看看活生生的样例。

在源代码中搜索threadLoop,当然也能够搜索thread,然后随便挑选一个Thread子类进行研究。

这里挑选

/frameworks/av/services/audioflinger/AudioWatchdog.h

#ifndef AUDIO_WATCHDOG_H
#define AUDIO_WATCHDOG_H #include <time.h>
#include <utils/Thread.h> namespace android { ...... class AudioWatchdog : public Thread { public:
AudioWatchdog(unsigned periodMs = 50) : Thread(false /*canCallJava*/), mPaused(false),
mPeriodNs(periodMs * 1000000), mMaxCycleNs(mPeriodNs * 2),
// mOldTs
// mLogTs initialized below
mOldTsValid(false), mUnderruns(0), mLogs(0), mDump(&mDummyDump)
{
#define MIN_TIME_BETWEEN_LOGS_SEC 60
// force an immediate log on first underrun
mLogTs.tv_sec = MIN_TIME_BETWEEN_LOGS_SEC;
mLogTs.tv_nsec = 0;
}
virtual ~AudioWatchdog() { } // Do not call Thread::requestExitAndWait() without first calling requestExit().
// Thread::requestExitAndWait() is not virtual, and the implementation doesn't do enough.
virtual void requestExit(); // FIXME merge API and implementation with AudioTrackThread
void pause(); // suspend thread from execution at next loop boundary
void resume(); // allow thread to execute, if not requested to exit // Where to store the dump, or NULL to not update
void setDump(AudioWatchdogDump* dump); private:
virtual bool threadLoop(); Mutex mMyLock; // Thread::mLock is private
Condition mMyCond; // Thread::mThreadExitedCondition is private
bool mPaused; // whether thread is currently paused ......
}; } // namespace android #endif // AUDIO_WATCHDOG_H

我们能够看到AudioWatchDog确实是Thread的子类,那好。以下看实现。

/frameworks/av/services/audioflinger/AudioWatchdog.cpp

#define LOG_TAG "AudioWatchdog"
//#define LOG_NDEBUG 0 #include <utils/Log.h>
#include "AudioWatchdog.h" namespace android { bool AudioWatchdog::threadLoop()
{
{
AutoMutex _l(mMyLock);
if (mPaused) {
mMyCond.wait(mMyLock);
// ignore previous timestamp after resume()
mOldTsValid = false;
// force an immediate log on first underrun after resume()
mLogTs.tv_sec = MIN_TIME_BETWEEN_LOGS_SEC;
mLogTs.tv_nsec = 0;
// caller will check for exitPending()
return true;
}
}
struct timespec newTs;
int rc = clock_gettime(CLOCK_MONOTONIC, &newTs);
if (rc != 0) {
pause();
return false;
}
if (!mOldTsValid) {
mOldTs = newTs;
mOldTsValid = true;
return true;
}
time_t sec = newTs.tv_sec - mOldTs.tv_sec;
long nsec = newTs.tv_nsec - mOldTs.tv_nsec;
if (nsec < 0) {
--sec;
nsec += 1000000000;
}
mOldTs = newTs;
// cycleNs is same as sec*1e9 + nsec, but limited to about 4 seconds
uint32_t cycleNs = nsec;
if (sec > 0) {
if (sec < 4) {
cycleNs += sec * 1000000000;
} else {
cycleNs = 4000000000u;
}
}
mLogTs.tv_sec += sec;
if ((mLogTs.tv_nsec += nsec) >= 1000000000) {
mLogTs.tv_sec++;
mLogTs.tv_nsec -= 1000000000;
}
if (cycleNs > mMaxCycleNs) {
mDump->mUnderruns = ++mUnderruns;
if (mLogTs.tv_sec >= MIN_TIME_BETWEEN_LOGS_SEC) {
mDump->mLogs = ++mLogs;
mDump->mMostRecent = time(NULL);
ALOGW("Insufficient CPU for load: expected=%.1f actual=%.1f ms; underruns=%u logs=%u",
mPeriodNs * 1e-6, cycleNs * 1e-6, mUnderruns, mLogs);
mLogTs.tv_sec = 0;
mLogTs.tv_nsec = 0;
}
}
struct timespec req;
req.tv_sec = 0;
req.tv_nsec = mPeriodNs;
rc = nanosleep(&req, NULL);
if (!((rc == 0) || (rc == -1 && errno == EINTR))) {
pause();
return false;
}
return true;
} void AudioWatchdog::requestExit()
{
// must be in this order to avoid a race condition
Thread::requestExit();
resume();
} void AudioWatchdog::pause()
{
AutoMutex _l(mMyLock);
mPaused = true;
} void AudioWatchdog::resume()
{
AutoMutex _l(mMyLock);
if (mPaused) {
mPaused = false;
mMyCond.signal();
}
} } // namespace android

非常明显,它的核心方法就是threadLoop(),在本文中我们不关心它具体的功能。仅仅想确定它是怎么启动的呢?又是怎么循环执行的呢?带着疑问我又在源代码中搜索关键字AudioWatchdog

结果发现有两个地方引用了。

/frameworks/av/services/audioflinger/AudioFlinger.h
/frameworks/av/services/audioflinger/AudioFlinger.cpp

在AudioFlinger.h中MixerThread中有个AudioWatchdog的sp对象


class MixerThread : public PlaybackThread {
public:
MixerThread (const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output,
audio_io_handle_t id,
audio_devices_t device,
type_t type = MIXER);
virtual ~MixerThread(); protected: AudioMixer* mAudioMixer; // normal mixer
private: sp<AudioWatchdog> mAudioWatchdog; // non-0 if there is an audio watchdog thread };

我们再看代码

/frameworks/av/services/audioflinger/AudioFlinger.cpp

AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
audio_io_handle_t id, audio_devices_t device, type_t type)
: PlaybackThread(audioFlinger, output, id, device, type),
// mAudioMixer below
// mFastMixer below
mFastMixerFutex(0)
// mOutputSink below
// mPipeSink below
// mNormalSink below
{ ......
#ifdef AUDIO_WATCHDOG
// create and start the watchdog
mAudioWatchdog = new AudioWatchdog();
mAudioWatchdog->setDump(&mAudioWatchdogDump);
//AudioWatchdog的run方法在此调用,所以线程启动
mAudioWatchdog->run("AudioWatchdog", PRIORITY_URGENT_AUDIO);
tid = mAudioWatchdog->getTid();
err = requestPriority(getpid_cached, tid, kPriorityFastMixer);
if (err != 0) {
ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
kPriorityFastMixer, getpid_cached, tid, err);
}
#endif ......
}

删掉不相关代码,我们看到AudioWatchdog对象确实创建了,而且调用了它的run方法。在java中Thread的run方法就是启动,这个也应该如此。可是如之前的源代码所看到的AudioWatchdog.cpp中并没有实现run方法,怎么办呢?别紧张,它还有父类Thread.

/frameworks/native/include/utils/Thread.h


#ifndef _LIBS_UTILS_THREAD_H
#define _LIBS_UTILS_THREAD_H #include <stdint.h>
#include <sys/types.h>
#include <time.h> #if defined(HAVE_PTHREADS)
# include <pthread.h>
#endif #include <utils/Condition.h>
#include <utils/Errors.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <utils/ThreadDefs.h> // ---------------------------------------------------------------------------
namespace android {
// --------------------------------------------------------------------------- class Thread : virtual public RefBase
{
public:
// Create a Thread object, but doesn't create or start the associated
// thread. See the run() method.
Thread(bool canCallJava = true);
virtual ~Thread(); // Start the thread in threadLoop() which needs to be implemented.
virtual status_t run( const char* name = 0,
int32_t priority = PRIORITY_DEFAULT,
size_t stack = 0); // Ask this object's thread to exit. This function is asynchronous, when the
// function returns the thread might still be running. Of course, this
// function can be called from a different thread.
virtual void requestExit(); // Good place to do one-time initializations
virtual status_t readyToRun(); // Call requestExit() and wait until this object's thread exits.
// BE VERY CAREFUL of deadlocks. In particular, it would be silly to call
// this function from this object's thread. Will return WOULD_BLOCK in
// that case.
status_t requestExitAndWait(); // Wait until this object's thread exits. Returns immediately if not yet running.
// Do not call from this object's thread; will return WOULD_BLOCK in that case.
status_t join(); #ifdef HAVE_ANDROID_OS
// Return the thread's kernel ID, same as the thread itself calling gettid() or
// androidGetTid(), or -1 if the thread is not running.
pid_t getTid() const;
#endif protected:
// exitPending() returns true if requestExit() has been called.
bool exitPending() const; private:
// Derived class must implement threadLoop(). The thread starts its life
// here. There are two ways of using the Thread object:
// 1) loop: if threadLoop() returns true, it will be called again if
// requestExit() wasn't called.
// 2) once: if threadLoop() returns false, the thread will exit upon return.
virtual bool threadLoop() = 0; private:
Thread& operator=(const Thread&);
static int _threadLoop(void* user);
const bool mCanCallJava;
// always hold mLock when reading or writing
thread_id_t mThread;
mutable Mutex mLock;
Condition mThreadExitedCondition;
status_t mStatus;
// note that all accesses of mExitPending and mRunning need to hold mLock
volatile bool mExitPending;
volatile bool mRunning;
sp<Thread> mHoldSelf;
#ifdef HAVE_ANDROID_OS
// legacy for debugging, not used by getTid() as it is set by the child thread
// and so is not initialized until the child reaches that point
pid_t mTid;
#endif
}; }; // namespace android // ---------------------------------------------------------------------------
#endif // _LIBS_UTILS_THREAD_H
//

能够看到确实有run方法。那以下看看它的实现

status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
Mutex::Autolock _l(mLock); if (mRunning) {
// thread already started
return INVALID_OPERATION;
} // reset status and exitPending to their default value, so we can
// try again after an error happened (either below, or in readyToRun())
mStatus = NO_ERROR;
mExitPending = false;
mThread = thread_id_t(-1); // hold a strong reference on ourself
mHoldSelf = this; mRunning = true; bool res;
if (mCanCallJava) {
res = createThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
} else {
res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
} if (res == false) {
mStatus = UNKNOWN_ERROR; // something happened!
mRunning = false;
mThread = thread_id_t(-1);
mHoldSelf.clear(); // "this" may have gone away after this. return UNKNOWN_ERROR;
} // Do not refer to mStatus here: The thread is already running (may, in fact
// already have exited with a valid mStatus result). The NO_ERROR indication
// here merely indicates successfully starting the thread and does not
// imply successful termination/execution.
return NO_ERROR; // Exiting scope of mLock is a memory barrier and allows new thread to run
}

run()方法中有这么一段

if (mCanCallJava) {
res = createThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
} else {
res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
}

mCanCallJava的意思是能不能被JNI层调用,然后依据值去创建Thread,这里有两个分支,我们就选择createThreadEtc()

终于代码会走到这里

int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
void *userData,
const char* threadName,
int32_t threadPriority,
size_t threadStackSize,
android_thread_id_t *threadId)
{
......
entryFunction = (android_thread_func_t)&thread_data_t::trampoline;
userData = t;
}
#endif if (threadStackSize) {
pthread_attr_setstacksize(&attr, threadStackSize);
} errno = 0;
pthread_t thread;
//在此创建,文章开头我有写样例怎么用c语言创建一个线程
int result = pthread_create(&thread, &attr,
(android_pthread_entry)entryFunction, userData);
pthread_attr_destroy(&attr);
if (result != 0) {
ALOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n"
"(android threadPriority=%d)",
entryFunction, result, errno, threadPriority);
return 0;
} ......
return 1;
}

删除了不相关代码,大家看看是不是非常熟悉啊。我在文章開始的部分就写出了linux下c语言pthread创建线程的样例,大家能够回头看看。也就是pthread_create()

这里面传进来的entryFunction是Thread中的_threadLoop()

int Thread::_threadLoop(void* user)
{
Thread* const self = static_cast<Thread*>(user); sp<Thread> strong(self->mHoldSelf);
wp<Thread> weak(strong);
self->mHoldSelf.clear(); #ifdef HAVE_ANDROID_OS
// this is very useful for debugging with gdb
self->mTid = gettid();
#endif bool first = true; do {
bool result;
if (first) {
first = false;
self->mStatus = self->readyToRun();
result = (self->mStatus == NO_ERROR); if (result && !self->exitPending()) {
// Binder threads (and maybe others) rely on threadLoop
// running at least once after a successful ::readyToRun()
// (unless, of course, the thread has already been asked to exit
// at that point).
// This is because threads are essentially used like this:
// (new ThreadSubclass())->run();
// The caller therefore does not retain a strong reference to
// the thread and the thread would simply disappear after the
// successful ::readyToRun() call instead of entering the
// threadLoop at least once.
//调用threadLoop()
result = self->threadLoop();
}
} else {
result = self->threadLoop();
} // establish a scope for mLock
{
Mutex::Autolock _l(self->mLock);
if (result == false || self->mExitPending) {
self->mExitPending = true;
self->mRunning = false;
// clear thread ID so that requestExitAndWait() does not exit if
// called by a new thread using the same thread ID as this one.
self->mThread = thread_id_t(-1);
// note that interested observers blocked in requestExitAndWait are
// awoken by broadcast, but blocked on mLock until break exits scope
self->mThreadExitedCondition.broadcast();
break;
}
} // Release our strong reference, to let a chance to the thread
// to die a peaceful death.
strong.clear();
// And immediately, re-acquire a strong reference for the next loop
strong = weak.promote();
} while(strong != 0); return 0;
}

_threadLoop()这种方法就是Thread的最大秘密,它是一个while循环。

1、创建线程时。会sp和wp一次线程本身。

2、假设是第一次执行会执行线程的readyToRun()方法,再执行threadLoop(),否则,直接执行threadLoop()。

3、threadLoop()方法有返回值,假设threadLoop()返回false的时候,线程会做清理工作。然后退出while循环,结束执行。

所以在这里。我開始时的疑问—为什么线程Thread中的threadLoop()能够循环处理数据就到此做了说明。

Thread被创建,

Thread中的run被调用。

__threadLoop()被调用,

readyToRun()被调用。

然后循环调用threadLoop()。



而且在threadLoop()返回false时。能够退出循环。

!!

!!!

还有。最关键的一点是threadLoop能够循环事实上是由于调用它的_threadLoop()方法里面有一个while循环

特殊情况

有的时候Android Framework中Thread的run()方法非常难发如今哪里被调用。如SurfaceFlinger它也是一个Thread子类。

在源代码中搜索能够发现它的创建位置

class SurfaceFlinger : public BinderService<SurfaceFlinger>,
public BnSurfaceComposer,
private IBinder::DeathRecipient,
private Thread,
private HWComposer::EventHandler
{
public:
static char const* getServiceName() {
return "SurfaceFlinger";
} SurfaceFlinger(); /* ------------------------------------------------------------------------
* Thread interface
*/
virtual bool threadLoop();
virtual status_t readyToRun();
virtual void onFirstRef(); }; // ---------------------------------------------------------------------------
}; // namespace android #endif // ANDROID_SURFACE_FLINGER_H

去找它创建的地方

/frameworks/base/cmds/system_server/library/system_init.cpp

extern "C" status_t system_init()
{
ALOGI("Entered system_init()"); sp<ProcessState> proc(ProcessState::self()); sp<IServiceManager> sm = defaultServiceManager();
ALOGI("ServiceManager: %p\n", sm.get()); char propBuf[PROPERTY_VALUE_MAX];
property_get("system_init.startsurfaceflinger", propBuf, "1");
if (strcmp(propBuf, "1") == 0) {
// Start the SurfaceFlinger
SurfaceFlinger::instantiate();
} // And now start the Android runtime. We have to do this bit
// of nastiness because the Android runtime initialization requires
// some of the core system services to already be started.
// All other servers should just start the Android runtime at
// the beginning of their processes's main(), before calling
// the init function.
ALOGI("System server: starting Android runtime.\n");
AndroidRuntime* runtime = AndroidRuntime::getRuntime(); ALOGI("System server: starting Android services.\n");
JNIEnv* env = runtime->getJNIEnv();
if (env == NULL) {
return UNKNOWN_ERROR;
}
jclass clazz = env->FindClass("com/android/server/SystemServer");
if (clazz == NULL) {
return UNKNOWN_ERROR;
}
jmethodID methodId = env->GetStaticMethodID(clazz, "init2", "()V");
if (methodId == NULL) {
return UNKNOWN_ERROR;
}
env->CallStaticVoidMethod(clazz, methodId); ALOGI("System server: entering thread pool.\n");
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
ALOGI("System server: exiting thread pool.\n"); return NO_ERROR;
}

我们能够看到

 SurfaceFlinger::instantiate();

但它本身并没有实现instantiate()方法,那之类找它的父类了。

/frameworks/native/include/binder/BinderService.h

namespace android {

template<typename SERVICE>
class BinderService
{
public:
static status_t publish(bool allowIsolated = false) {
sp<IServiceManager> sm(defaultServiceManager());
return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated);
} static void publishAndJoinThreadPool(bool allowIsolated = false) {
sp<IServiceManager> sm(defaultServiceManager());
sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated);
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
} static void instantiate() { publish(); } static status_t shutdown() {
return NO_ERROR;
}
}; }; // namespace android
// ---------------------------------------------------------------------------
#endif // ANDROID_BINDER_SERVICE_H

会调用publish()方法。

而SERVICE在这里是一个模板类。在这里SERVICE自然相应SurfaceFlinger

所以publish()会向ServiceManager加入一个Service这个Service就是Surfaceflinger。

然后我们看SurfaceFlinger的构造函数

/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

SurfaceFlinger::SurfaceFlinger()
: BnSurfaceComposer(), Thread(false),
mTransactionFlags(0),
mTransactionPending(false),
mAnimTransactionPending(false),
mLayersRemoved(false),
mRepaintEverything(0),
mBootTime(systemTime()),
mVisibleRegionsDirty(false),
mHwWorkListDirty(false),
mDebugRegion(0),
mDebugDDMS(0),
mDebugDisableHWC(0),
mDebugDisableTransformHint(0),
mDebugInSwapBuffers(0),
mLastSwapBufferTime(0),
mDebugInTransaction(0),
mLastTransactionTime(0),
mBootFinished(false)
{
ALOGI("SurfaceFlinger is starting"); // debugging stuff...
char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.showupdates", value, "0");
mDebugRegion = atoi(value); property_get("debug.sf.ddms", value, "0");
mDebugDDMS = atoi(value);
if (mDebugDDMS) {
if (!startDdmConnection()) {
// start failed, and DDMS debugging not enabled
mDebugDDMS = 0;
}
}
ALOGI_IF(mDebugRegion, "showupdates enabled");
ALOGI_IF(mDebugDDMS, "DDMS debugging enabled");
}

可是遗憾的是没有发现run()方法的影踪,没有办法仅仅得去父类构造方法看

结果发现也没有!

。。

没有办法,继续在源代码中搜索SurfaceFlinger。结果发现与之相关的信息大多是sp<SurfaceFlinger>

就看看sp吧。

sp是Android在c++中搞得相似java中弱引用、强引用的一套指针概念,那应该是方便回收吧。

而Android Framework中的c++世界,RefBase这个类有点像java中的Object.

而sp是一个模板类。

这部分内容。请看点击以下链接

Android中的sp和wp指针

上面的链接讲得还算具体。这里纠结sp的过多细节。长话短说。总之第一次对SurfaceFlinger引用调用sp<SurfaceFlinger>时会调用SurfaceFlinger的onFirstRef()方法。

那好。看代码吧

void SurfaceFlinger::onFirstRef()
{
mEventQueue.init(this);
//run方法在此被调用
run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY); // Wait for the main thread to be done with its initialization
mReadyToRunBarrier.wait();
}

看见没有?run()方法在这里调用了。

所以,在Framework中假设你找不到一个Thread在何处被启动,那么去它的onFirstRef()方法中去看看吧

Android Framework中的线程Thread及它的threadLoop方法的更多相关文章

  1. Java中的线程Thread方法之---interrupt()

    前几篇都介绍了Thread中的几个方法,相信大家都发现一个相似点,那就是sleep,join,wait这样的阻塞方法都必须捕获一个InterruptedException异常,顾名思义就是一个线程中断 ...

  2. Android开发中,那些让您觉得相见恨晚的方法、类或接口

    Android开发中,那些让你觉得相见恨晚的方法.类或接口本篇文章内容提取自知乎Android开发中,有哪些让你觉得相见恨晚的方法.类或接口?,其实有一部是JAVA的,但是在android开发中也算常 ...

  3. Android Framework中Thread类

    Thread类是Android为线程操作而做的一个封装.代码在Thread.cpp中,其中还封装了一些与线程同步相关的类. Thread类 Thread类的构造函数中的有一个canCallJava T ...

  4. Android 线程Thread的2种实现方法

    在讲解之前有以下三点要说明: 1.在Android中有两种实现线程Thread的方法: ①扩展java.long.Thread类: ②实现Runnable()接口: 2.Thread类是线程类,它有两 ...

  5. 【转】android ddms中查看线程释疑

    原文网址:http://www.mobiletrain.org/lecture/doc/android/2011-05/457.html 大家都用过ddm,如果你用ddms查看一个程序的所有线程,你会 ...

  6. Java中的线程Thread总结

    首先来看一张图,下面这张图很清晰的说明了线程的状态与Thread中的各个方法之间的关系,很经典的! 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口. 要注意的是Threa ...

  7. Android Framework中的Application Framework层介绍

    Android的四层架构相比大家都很清楚,老生常谈的说一下分别为:Linux2.6内核层,核心库层,应用框架层,应用层.我今天重点介绍一下应用框架层Framework,其实也是我自己的学习心得. Fr ...

  8. Java中的线程Thread方法之---suspend()和resume()

    前篇说到了Thread中的join方法,这一篇我们就来介绍一下suspend()和resume()方法,从字面意义上可以了解到这两个方法是一对的,suspend()方法就是将一个线程挂起(暂停),re ...

  9. Android开发中,那些让你觉得相见恨晚的方法、类或接口

    Throwable类中的getStackTrace()方法,根据这个方法可以得到函数的逐层调用地址,其返回值为StackTraceElement[],而在StackTraceElement类中有四个方 ...

随机推荐

  1. JPA的配置文件

    一.引入包 <dependencies> <!-- https://mvnrepository.com/artifact/org.hibernate.javax.persistenc ...

  2. Linux开机自动启动某一程序

    Linux开机启动程序详解我们假设大家已经熟悉其它操作系统的引导过程,了解硬件的自检引导步骤,就只从Linux操作系统的引导加载程序(对个人电脑而言通常是LILO)开始,介绍Linux开机引导的步骤. ...

  3. Mysql官方文档中争对安全添加列的处理方法。Mysql Add a Column to a table if not exists

    Add a Column to a table if not exists MySQL allows you to create a table if it does not exist, but d ...

  4. stingray中的需要注意的地方

    TBLROWS中嵌套的单引号需要加倍用作转译

  5. IE浏览器兼容方案

    1.使用高版本渲染模式 <meta http-equiv=”X-UA-Compatible” content=”IE=edge,chrome=1″/> 2.css hack (1)条件注释 ...

  6. sqlserver 建表语句,获取建表语句的存储过程,包括排序规则,索引,字段说明,支持同时生成多个表

    先创建一个分割表名的分割函数 --表值函数用以截取字符串 --如果为其添加一列主键id,则其顺序就会固定了 create FUNCTION [Split](@text NVARCHAR(max)) ) ...

  7. great tips in soapui

    from this site :http://onebyteatatime.wordpress.com/2009/04/18/soapui-tips-n-tricks-part-2/

  8. 在spring boot微服务中使用JWS发布webService

    发布时间:2018-11-22   技术:Java+spring+maven   概述 在springboot微服务中使用JWS发布webService,在服务启动时自动发布webservice接口. ...

  9. 使用PowerDesigner生成SQL语句

    0.我使用的PowerDedigner的版本如下: 1.tools>>Resource>>DBMS,然后选中你的数据库版本,双击或点击左上角的properties. 2.再选择 ...

  10. Swift3.0 - 实现剪切板代码拷贝及跨应用粘贴

    有个需求,点击某个按钮,实现一段内容的拷贝,然后到其他应用内,直接长按粘贴. 实现如下: /// 测试剪切板,实现代码拷贝内容 func testPasteBoard(str:String) { // ...