Android Thread解析--run()

在这里插入图片描述

1.Threads.cpp

system/core/libutils/Threads.cpp

Thread::Thread(bool canCallJava)
    : mCanCallJava(canCallJava),
      mThread(thread_id_t(-1)),
      mLock("Thread::mLock"),
      mStatus(OK),
      mExitPending(false),
      mRunning(false)
#if defined(__ANDROID__)
      ,
      mTid(-1)
#endif
{
}

这里有一个很关键的成员变量canCallJava,它很重要,因为在Thread 执行run启动时,代码如下:

status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
    Mutex::Autolock _l(mLock);
    //...
    if (mCanCallJava) {//这里开始有不同
        res = createThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    } else {
        res = androidCreateRawThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    }
    //...
    return NO_ERROR;
}

2 mCanCallJava=true时的流程

mCanCallJava将线程创建函数的逻辑分为两个分支,调用的函数不同。当mCanCallJava为true时,调用createThreadEtc的这个分支,代码如下:

inline bool createThreadEtc(...)
{
    return androidCreateThreadEtc(entryFunction, userData, threadName,threadPriority, threadStackSize, threadId) ? true : false;
}
int androidCreateThreadEtc(...)
{
    return gCreateThreadFn(entryFunction, userData, threadName,threadPriority, threadStackSize, threadId);
} 

继续分析gCreateThreadFn,代码如下:

static android_create_thread_fn gCreateThreadFn = androidCreateRawThreadEtc;

这里gCreateThreadFn是函数指针,初始化时和mCanCallJava为false时使用的是同一个方法。若gCreatThreadFn方法未被初始化,则mCanCallJava为false或者为true都是一样的,只有gCreatThreadFn被初始化成其他的方法,才会有不一样的流程

2 gCreateThreadFn的其他函数指针

代码中有的地方是会修改这个函数指针的指向的,比如在zygote中创建线程, AndroidRuntime调用startReg的地方,就有可能修改这个函数指针,其代码如下所示:

int AndroidRuntime::startReg(JNIEnv*env)
{
   //这里会修改函数指针为javaCreateThreadEtc,即不再是androidCreateRawThreadEtc
   androidSetCreateThreadFunc((android_create_thread_fn)javaCreateThreadEtc);
   return0;
}

这里继续分析代码androidSetCreateThreadFunc的实现,如下所示:

void androidSetCreateThreadFunc(android_create_thread_fn func)
{
    gCreateThreadFn = func;
}

如果mCanCallJava为true,则将调用javaCreateThreadEtc(否则会调用androidCreateRawThreadEtc),分析代码,如下所示:

int AndroidRuntime::javaCreateThreadEtc(
                               android_thread_func_tentryFunction,
                               void* userData,
                               const char*threadName,
                               int32_tthreadPriority,
                               size_t threadStackSize,
                               android_thread_id_t* threadId)
{
    void**args = (void**) malloc(3 * sizeof(void*));  
    intresult;
    args[0] = (void*) entryFunction;
    args[1] = userData;
    args[2] = (void*) strdup(threadName);
    //调用的还是androidCreateRawThreadEtc,但线程函数却换成了javaThreadShell。
    result= androidCreateRawThreadEtc(AndroidRuntime::javaThreadShell, args,threadName, threadPriority,threadStackSize, threadId);
    returnresult;
} 

继续分析javaThreadShell,代码如下:

int AndroidRuntime::javaThreadShell(void* args){
    ...
    int result;
    //把这个线程attach到JNI环境中,这样这个线程就可以调用JNI的函数了
    if(javaAttachThread(name, &env) != JNI_OK)
        return -1;
    //调用实际的线程函数干活
    result = (*(android_thread_func_t)start)(userData);
    //从JNI环境中detach出来。
    javaDetachThread();
    free(name);
    returnresult;
}

3 分析mCanCallJava为true的目的

它创建的新线程将:

在调用用户线程函数之前会attach到JNI环境中,这样线程函数就可以直接使用JNI函数了。
线程函数退出后,它会从JNI环境中detach,释放一些资源。
同时,进程退出前,dalvik虚拟机会检查是否有attach了,但是最后未detach的线程如果有,则会直接abort(这样并不好),如果关闭JNI check选项,就不会做这个检查,但这个检查和资源释放有关系。建议还是重视JNIcheck。如果直接使用POSIX的线程创建函数,那么凡是使用过attach的,最后就都需要detach!保证资源被正常释放。

4 线程函数_threadLoop

线程函数_threadLoop都会被调用,为什么不直接调用用户传入的线程函数呢?分析关键代码androidCreateRawThreadEtc的实现,如下所示:

//这里的entryFunction参数就是_threadLoop
int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
                               void *userData,
                               const char* threadName __android_unused,
                               int32_t threadPriority,
                               size_t threadStackSize,
                               android_thread_id_t *threadId)
{
    //线程初始化操作,start
    //线程属性设置
    pthread_attr_t attr; 
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
#ifdef HAVE_ANDROID_OS  /* valgrind is rejecting RT-priority create reqs */
    if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) {
        thread_data_t* t = new thread_data_t;
        t->priority = threadPriority;
        t->threadName = threadName ? strdup(threadName) : NULL;
        t->entryFunction = entryFunction;
        t->userData = userData;
        entryFunction = (android_thread_func_t)&thread_data_t::trampoline;
        userData = t;            
    }
#endif
    if (threadStackSize) { //线程栈的设置
        pthread_attr_setstacksize(&attr, threadStackSize);
    }
    //线程初始化操作,end
    errno = 0;
    pthread_t thread;
    //创建线程,entryFunction这个参数就是_threadLoop,开始执行_threadLoop方法
    int result = pthread_create(&thread, &attr,(android_pthread_entry)entryFunction, userData);
    pthread_attr_destroy(&attr);
    if (result != 0) {
        return 0;
    }
    if (threadId != NULL) {
        *threadId = (android_thread_id_t)thread; // XXX: this is not portable
    }
    return 1;
}

接下来,分析_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
    self->mTid = gettid();
#endif
    bool first = true;
    do {
        bool result;
        if (first) {//这种设计表示只能第一次进来。
            first = false;
            self->mStatus = self->readyToRun(); //self代表继承Thread类的对象,第一次进来将调用readyToRun,看看是否准备好
            result = (self->mStatus == NO_ERROR);
            if (result && !self->exitPending()) {
                result = self->threadLoop();
            }
        } else {
            result = self->threadLoop();//如果不是第一次进来,则直接调用派生类的threadLoop
            //注意:这段代码运行在一个do-while循环中。 这表示即使我们的threadLoop返回了,线程也不一定会退出。
        }
    //线程退出的条件:
    //1)result 为false。如果子类在threadLoop中返回false,线程就可以退出。这属于主动退出的情况,是threadLoop自己不想继续干活了,所以返回false。
    //因此,一定不要写错threadLoop的返回值。
    //2)mExitPending为true,这个变量可由Thread类的requestExit函数设置,这属于被动退出,因为由外界强制设置了退出条件。
        {
        Mutex::Autolock _l(self->mLock);
        if (result == false || self->mExitPending) {
            self->mExitPending = true;
            self->mRunning = false;
            self->mThread = thread_id_t(-1);
            self->mThreadExitedCondition.broadcast();
            break;
        }
        }
        
        strong.clear();
        strong = weak.promote();
    } while(strong != 0);
    return 0;
}

threadLoop运行在一个循环中,它的返回值可以决定是否退出线程。

5. 例子 和遇到的问题

int32_t Demo::startMonitor()
{
	run("Demo");
}

bool Demo::threadLoop()
{
for (;;) {
		...
		break;
	}
	return true;
}

run --> Thread.cpp 中run , 但调用run()函数, 创建线程失败.
原因: thread.cpp 中run 函数在android A13 检测传过来的数据是不是strong point . 如果不是,报错退出. 创建失败, thread_loop 内容无法执行.
A12 能成功, A13 fail .
对比A12 和 A13 的code
A13的接口 mHoldSelf = sp::fromExisting(this);. A12的接口 mHoldSelf =this; .

fromExisting的作用如果一个strong pointer可用,将检索它; 反之终止退出

a12走的通过拷贝构造函数实现的,调用的是incStrong,不要求调用的对象本身必须是sp
a13要求调用者必须是sp,不然就会报错,导致crash

报错log 如下

10-10 02:59:30.656  1987  1987 F DEBUG   : Abort message: 'incStrongRequireStrong() called on 0xee0804d0 which isn't already owned'
10-10 02:59:30.656  1987  1987 F DEBUG   :     r0  00000000  r1  0000071f  r2  00000006  r3  fff40a10
10-10 02:59:30.656  1987  1987 F DEBUG   :     r4  fff40a20  r5  fff40a08  r6  0000071f  r7  0000016b
10-10 02:59:30.657  1987  1987 F DEBUG   :     r8  00000000  r9  ffffffff  r10 fff40a10  r11 edf40ddc
10-10 02:59:30.657  1987  1987 F DEBUG   :     ip  0000071f  sp  fff409f0  lr  ee43de83  pc  ee43de96
10-10 02:59:30.657  1987  1987 F DEBUG   : backtrace:
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
    Mutex::Autolock _l(mLock);
    //...
     ALOGE(" mHoldSelf start");
     // hold a strong reference on ourself
     mHoldSelf = sp<Thread>::fromExisting(this);
     // mHoldSelf = this;
	 ALOGE(" mHoldSelf end");

    //...
    return NO_ERROR;
}

查看log , 对应code mHoldSelf = sp::fromExisting(this); 导致的.

fromExisting 的函数作用 检测是否有可用strong point, 没有报错退出.

template <typename T>
sp<T> sp<T>::fromExisting(T* other) {
    if (other) {
        check_not_on_stack(other);
        other->incStrongRequireStrong(other);
        sp<T> result;
        result.m_ptr = other;
        return result;
    }
    return nullptr;
}

incStrongRequireStrong 和 Abort message: ‘incStrongRequireStrong() called on 0xee0804d0 which isn’t already owned’ 可以对应上了.

这里面有原子操作, 判单是否自增.

const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
LOG_ALWAYS_FATAL_IF(c <= 0 || c == INITIAL_STRONG_VALUE, "incStrongRequireStrong() called on %p which isn't already     owned #### c=%d", refs,c);

我失败的原因, 传过来的指针不是strong point 的. 所有 c = INITIAL_STRONG_VALUE (1<<28). 导致报错.

解决办法: 传过来的类的, 设置未sp<> 类型.

参考: https://blog.51cto.com/u_14344871/3370021

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
回答: 在Android Studio中使用Netty需要进行一些配置。首先,在底层的gradle文件中,需要添加对Netty的依赖。可以通过解析implementation的地址来获得正确的jar地址。具体的配置如下所示:\[1\] buildscript { repositories { google() jcenter() maven{ url 'http://central.maven.org/maven2/'} } dependencies { classpath 'com.android.tools.build:gradle:3.2.1' // 注意:不要在这里放置应用程序的依赖项,它们应该放在各个模块的build.gradle文件中 } } 接下来,在app模块的gradle文件中,也需要添加对Netty的依赖。具体的配置如下所示:\[2\] buildscript { repositories { google() jcenter() maven{ url 'http://central.maven.org/maven2/'} } dependencies { classpath 'com.android.tools.build:gradle:3.2.1' // 注意:不要在这里放置应用程序的依赖项,它们应该放在各个模块的build.gradle文件中 } } 最后,在启动项目时,可以在一个新的线程中启动Netty服务器。具体的代码如下所示:\[3\] new Thread(new Runnable() { @Override public void run() { try { new NettyServer().bind(8080); } catch (Exception e) { e.printStackTrace(); } } }).start(); 这样,你就可以在Android Studio中使用Netty来进行网络编程了。 #### 引用[.reference_title] - *1* [Android studio导入Netty(修改maven仓库Url)](https://blog.csdn.net/doublemark/article/details/84316619)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Android studio导入Netty](https://blog.csdn.net/qq_29443203/article/details/89140560)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [25.Android网络通讯-Netty(TCP服务端)](https://blog.csdn.net/zxc472504515/article/details/123392317)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值