Android Thread之threadLoop方法——(追IMS原始碼時不知怎麼執行到threadLoop方法的,這篇文章有一個很好的解釋)
Android Framework中的執行緒Thread及它的threadLoop方法
在Framework中的Thread普遍的特點就是有一個 threadLoop方法。它到底是怎麼迴圈起來的。
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 stubsuper.handleMessage(msg); } };
上面中通過mHandler傳送訊息就可以在mThread中處理了,並且這個mThread不是UIThread,不會阻塞主執行緒。
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; 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"); pthread_join(pId,NULL); printf("main thread exit\n"); return0; }
然後編譯
gcc -o test test.c -lpthread./test
執行結果如下:
main thread running ,count : 0the pthread running ,count: 0
main thread running ,count : 1the pthread running ,count: 1
main thread running ,count : 2the pthread running ,count: 2
main thread running ,count : 3the pthread running ,count: 3
main thread running ,count : 4the pthread running ,count: 4
main thread will exit when pthread isoverthe pthread running ,count: 5the pthread running ,count: 6the 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.virtualvoid
requestExit();
// FIXME merge API and implementation with AudioTrackThreadvoid
pause(); // suspend thread from execution at next loop boundaryvoid
resume(); // allow thread to execute, if not requested to exit
// Where to store the dump, or NULL to not updatevoid
setDump(AudioWatchdogDump* dump);
private:
virtual bool threadLoop();
Mutex mMyLock; // Thread::mLock is private
Condition mMyCond; // Thread::mThreadExitedCondition is privatebool 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 {
void AudioWatchdogDump::dump(int fd)
{
char buf[32];
if (mMostRecent != 0) {
// includes NUL terminator
ctime_r(&mMostRecent, buf);
} else {
strcpy(buf, "N/A\n");
}
fdprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s",
mUnderruns, mLogs, buf);
}
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()returntrue;
}
}
struct timespec newTs;
int rc = clock_gettime(CLOCK_MONOTONIC, &newTs);
if (rc != 0) {
pause();
returnfalse;
}
if (!mOldTsValid) {
mOldTs = newTs;
mOldTsValid = true;
returntrue;
}
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();
returnfalse;
}
returntrue;
}
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();
}
}
void AudioWatchdog::setDump(AudioWatchdogDump *dump)
{
mDump = dump != NULL ? dump : &mDummyDump;
}
} // 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 mixerprivate:
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);
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( constchar* 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.virtualvoid requestExit();
// Good place to do one-time initializationsvirtual 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;
#endifprotected:
// exitPending() returns trueifrequestExit() has been called.
boolexitPending() 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&);
staticint _threadLoop(void* user);
constbool 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 mLockvolatilebool mExitPending;
volatilebool 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, orin 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;
}
#endifif (threadStackSize) {
pthread_attr_setstacksize(&attr, threadStackSize);
}
errno = 0;
pthread_t thread;
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);
return0;
}
......
return1;
}
刪除了不相關程式碼,大家看看是不是很熟悉啊。我在文章開始的部分就寫出了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 gdbself->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.
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 scopeself->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);
return0;
}
_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時,可以退出迴圈。
特殊情況
有的時候Android Framework中Thread的run()方法很難發現在哪裡被呼叫。如SurfaceFlinger它也是一個Thread子類。在原始碼中搜索可以發現它的建立位置
class SurfaceFlinger : public BinderService<SurfaceFlinger>,
public BnSurfaceComposer,
private IBinder::DeathRecipient,
private Thread,
private HWComposer::EventHandler
{
public:
staticcharconst* getServiceName() {
return"SurfaceFlinger";
}
SurfaceFlinger();
/* ------------------------------------------------------------------------
* Thread interface
*/virtualbool threadLoop();
virtual status_t readyToRun();
virtualvoid 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
namespaceandroid {
template<typename SERVICE>
classBinderService
{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();
}
staticvoid 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...charvalue[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
就看看sp吧。
sp是Android在c++中搞得類似java中弱引用、強引用的一套指標概念,那應該是方便回收吧。
而Android Framework中的c++世界,RefBase這個類有點像java中的Object.
而sp是一個模板類。
總之呼叫sp時會呼叫SurfaceFlinger的onFirstRef()方法。
那好,看程式碼吧
void SurfaceFlinger::onFirstRef()
{
mEventQueue.init(this);
run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);
// Wait for the main thread to be done with its initialization
mReadyToRunBarrier.wait();
}
看見沒有?run()方法在這裡呼叫了。
所以,在Framework中如果你找不到一個Thread在何處被啟動,那麼去它的onFirstRef()方法中去看看吧
相關推薦
Android Thread之threadLoop方法——(追IMS原始碼時不知怎麼執行到threadLoop方法的,這篇文章有一個很好的解釋)
Android Framework中的執行緒Thread及它的threadLoop方法 在Framework中的Thread普遍的特點就是有一個 threadLoop方法。它到底是怎麼迴圈起來的。 Android中java世界的Thread 先來看看java是怎麼建
Android介面回撥機制(有圖有真相,如果你一直接觸介面回撥,但是自己又一直理解不了,這篇文章會讓你秒懂)
本人自己也經常在開發中遇到介面回撥機制,說來慚愧,雖然一直在用介面,但是隻是去複製別人的程式碼,一直弄不明白介面回撥的原理,甚至自己也努力去看了很多篇文章,幾乎看了所有的介面回撥的例子,什麼延時問答啊,孔融讓梨啊,都看了,例子都能背下來了,兩三個月了還沒理解。看來本人頭腦不
如何快速入門python,這篇文章幫你指明方向(零基礎的福音)
一個 交流群 企業 小項目 調用 不錯 數據類型 數據 入門 這是曾經在悟空問答回答的一個問題,後來效果還不錯,所以發出來,裏面結合了當年的學習經驗和一些行業老師的建議,希望幫助更多有興趣的人。(第三點福利) Python語言這幾年大火,在世界編程語言排行中Python也位
過載操作符(cin cout 都在這篇文章裡出現了 注意區別)
// A code block #include “iostream” using namespace std; class Date { private: int year,month,day; public: Date(int a=0,int b=0,int c=0) { this-&g
關於深度學習中的注意力機制,這篇文章從例項到原理都幫你參透了(很系統,重點看)
最近兩年,注意力模型(Attention Model)被廣泛使用在自然語言處理、影象識別及語音識別等各種不同型別的深度學習任務中,是深度學習技術中最值得關注與深入瞭解的核心技術之一。 本文以機器翻譯為例,深入淺出地介紹了深度學習中注意力機制的原理及關鍵計算機制,同時也抽
演算法設計與分析(六)(上週第五週的寫錯標題,這才是真正的第六週)
Merge k Sorted Lists 題目 Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. Example:
【Android開發經驗】APP的快取檔案到底應該存在哪?看完這篇文章你應該就自己清楚了
只要是需要進行聯網獲取資料的APP,那麼不管是版本更新,還是圖片快取,都會在本地產生快取檔案。那麼,這些快取檔案到底放在什地方合適呢?系統有沒有給我們提供建議的快取位置呢?不同的快取位置有什麼不同呢?今天這篇文章就是主要來說明這個問題的。 首先,我們要知道,在A
解決:百度編輯器UEditor,怎麼將圖片儲存到圖片伺服器,或者上傳到ftp伺服器的問題(如果你正在用UE,這篇文章值得你看下)
在使用百度編輯器ueditor的時候,怎麼將圖片儲存到另一個伺服器,或者上傳到ftp伺服器?這個問題,估計很多使用UE的人會遇到。而且我百度過,沒有找到這個問題的解決方案。那麼:本篇文章就很適合你了。因為本文解決了這個問題。 1.當你使用UEditor(以下簡稱UE)的
android深入之設計模式(一)托付模式
-h listen back != new 聚合 string static data- (一)托付模式簡單介紹 托付模式是主要的設計模式之中的一個。托付。即是讓還有一個對象幫你做事情。 更多的模式,如狀態模式、策略模式、訪問者模式本質上是在更特殊的場合採用了托
Android開發之布局文件裏實現OnClick事件關聯處理方法
intent dsm nbsp ext 關聯 you vertica findview 時間 一般監聽OnClickListener事件,我們都是通過Button button = (Button)findViewById(....); button.se
Android複習之旅--ViewPager(自動輪播條)
算一算,已經很久都沒有更博文了,最近又是拍畢業照又是幫別人拍微電影,自己也搗鼓些專案,忙這忙那的,都沒什麼時間寫部落格記錄筆記了。 唉,說這麼多其實都是自己太懶了,為了讓自己不那麼內疚,只好找些藉口安慰安慰自己,哈哈~。 好久沒寫部落格了,嘮叨嘮叨。 最近的de
Android複習之旅--ViewPager(應用引導頁)
對於ViewPager,相信大家都不陌生,ViewPager在應用中的使用頻率和ListView是有得一拼的。而ViewPager最常用於三個場景,分別是應用的新手引導頁,廣告輪播控制元件,和Fragment結合做應用內的標籤頁。 由於網上已經有很多關於ViewPager的博文了,寫
Android基礎之資料儲存(SharedPreference)
Android資料持久化是說在斷電後資料不會丟失,而根據儲存位置和實現方式一般有3種方式,這裡說sharedpreferences: 一,sharedpreferences儲存 該種方式是在應用獨有目錄data/data/[packgename]/shared_prefs/下
Android學習之動畫總結(一)
寫在前面:本文是根據hencoder提供的教程寫的總結。HenCoder https://hencoder.com。 Android裡動畫可以分為兩類:Animation和Transition,其中Animation又可以分為View Animation和Pr
Android學習之動畫總結(二)
寫在前面:本文是根據hencoder提供的教程寫的總結。HenCoder https://hencoder.com。 關於ObjectAnimator可以用ofInt()來做整數的屬性動畫和ofFloat()來做小數的屬性動畫。當需要對其他型別的屬性來做動畫就需要
Android應用之電量測試(PowerTutor)
一、首先來看看,官網上的一些說明吧。 PowerTutor官網連結 大概是說了以下幾點: a、針對google手機的應用,可以顯示CPU、網路介面、顯示器、GPS接收機等主要系統元件所消耗的電量。 b、APP使用者可以
Android學習之RecyclerView學習(實現瀑布流式佈局)
RecyclerView,大家可以通過匯入support-v7對其進行使用。 如果使用AndroidStudio開發, 需要在build.gradle中新增: compile 'com.android.support:appcompat-v7:24.2.1' com
Android開發之裁切(拍照+相簿)影象並設定頭像小結
先看效果: 再貼程式碼: 自定義選擇照片底部彈出對話方塊佈局: <?xml version="1.0" encoding="utf-8"?> <Re
Android繪圖之Canvas變換(6)
1 Canvas 與螢幕 前面講解了Canvas的基本概念,Android繪圖之Canvas概念理解(5) , 對Canvas的概念進行了分析,但是沒有說明和螢幕的關係,Canvas不等於螢幕,螢幕不會動的,我們也無法對螢幕進行(平移,縮放等)操作,只能對Canvas進行操作,所以對
Android專案之JSON解析(3種解析技術詳解)
前言: 在我寫部落格前再宣告一下,我希望轉載我文章的某某某記得註明:(),要尊重我的勞動成果,這樣才能給我更多的支援和鼓勵!差不多有3天沒有寫部落格了,要想的、要做的事情太多了,額....原歸正傳,今天接著上一篇部落格:Android專案之JSON解析(扯淡),繼續分享我對