frameworks 之线程类
接触android 底层代码后,会有很多关于线程的启动和使用,不掌握线程类相关写法,对阅读代码理解比较困难。该文讲解有关线程类的使用。
Linux下的线程库 pthread_t
pthread_t 是Linux下的线程库。使用该线程步骤主要经历3个步骤
- 创建 pthread_create
- 执行 pthread_join
- 退出 pthread_exit
pthread_create
该方法需要4个参数,返回值 若线程创建成功,则返回0,失败则返回错误码,并且 thread 内容是未定义的
参数 | 意义 |
---|---|
thread | 是一个指向线程标识符的指针,线程调用后,改值被设置为线程ID |
attr | 用来设置线程属性 |
start_routine | 是线程函数的其实地址,即线程回调函数方法体,线程创建成功后,thread 指向的内存单元从该地址开始运行 |
arg | 是传递给线程函数体的参数 |
typedef unsigned long int pthread_t;
int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void*(*start_routine)(void*), void* arg);
pthread_join
是一个线程阻塞函数,调用该函数则等到线程结束才继续运行,调用该方法
参数 | 意义 |
---|---|
thread | 线程表示符 |
retval | 来获取线程的返回值,一般是 pthread_join 方法传递出来的值 |
int pthread_join(pthread_t thread, void **retval);
pthread_exit
一个线程的结束有两种途径:
线程函数体执行结束;
调用 pthread_exit 方法退出线程;
参数 | 意义 |
---|---|
retval | 是线程的退出码,传递给创建线程的地方 |
void pthread_exit(void *retval);
编写demo
新建 thread_linux.c 文件,代码如下,包含声明 pthread_t 创建 和运行。导入相关的头文件,如果运行报错缺少对于的头文件,可以输入 man 缺少方法查看对于的头文件 导入即可
如 man abort
// test/thread_linux/thread_linux.c
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <utils/Log.h>
void *thread_posix_function(void *arg) {
// 无关代码,避免无使用报错
(void)arg;
int i;
for ( i=0; i<30; i++) {
// 控制台 打印
printf("hello thread i = %d\n",i);
// logcat 打印
ALOGD("hello thread i = %d\n",i);
sleep(1);
}
return NULL;
}
int main(void) {
pthread_t mythread;
if ( pthread_create( &mythread, NULL, thread_posix_function, NULL) ) {
ALOGD("error creating thread.");
abort();
}
if ( pthread_join ( mythread, NULL ) ) {
ALOGD("error joining thread.");
abort();
}
ALOGD("hello thread has run end exit\n");
exit(0);
}
新建 Android.mk 文件,编译为 二进制文件
# test/thread_linux/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# 文件名称
LOCAL_SRC_FILES := thread_linux.c
# 模块名称
LOCAL_MODULE := thread_linux
# 打印日志依赖
LOCAL_SHARED_LIBRARIES :=liblog
LOCAL_PRELINK_MODULE := false
# 编译为二进制文件
include $(BUILD_EXECUTABLE)
执行 make thread_linux 编译该模块,编译成功会输出到该路径下(out/target/product/emulator_x86_64/system/bin/thread_linux
)。
启动模拟器 将该脚本 推送到 data/local目录下。并执行脚本后,打开控制台或者 logcat 可以看到该日志输出,这样就可以执行并打印该进程。
adb root
adb remount
# 推送到目录下
adb push out/target/product/emulator_x86_64/system/bin/thread_linux data/local
adb shell
# 切换到该目录
cd data/locat
# 如果权限不够可以执行给权限
chmod +x thread_linux
# 执行脚本
,/thread_linux
Android 下的线程库 thread
Android native的Thread类是Android提供的一个基础类,源码路径在源码目录下,
system/core/libutils/Threads.cpp
system/core/libutils/include/utils/threads.h
system/core/libutils/include/utils/Thread.h
其中 threads.h 是头文件,里面空实现,导入了 Thread等头文件,查看 Thread.h 文件 可以看到该类继承了 RefBase ,所以在构造时候 会触发 onFirstRef 。 此外该头文件还定义了 一些方法
class Thread : virtual public RefBase
...
线程运行第一次会执行
// Good place to do one-time initializations
virtual status_t readyToRun();
// 执行线程
// Start the thread in threadLoop() which needs to be implemented.
// NOLINTNEXTLINE(google-default-arguments)
virtual status_t run( const char* name,
int32_t priority = PRIORITY_DEFAULT,
size_t stack = 0);
// 线程开始
virtual bool threadLoop() = 0;
其中 run 方法传入线程名称和优先级
Thread.cpp 实现
查看 Thread.cpp 具体run 方法,可以看到 run 方法会执行 androidCreateRawThreadEtc 方法。
bool res;
if (mCanCallJava) {
res = createThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
} else {
res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
}
而该 方法里面又是执行 初始化 Linux 的 pthread_t ,方法里面初始线程属性 并执行 所以可以得到也是基于 pthread 的封装
int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
void *userData,
const char* threadName __android_unused,
...
int result = pthread_create(&thread, &attr,
(android_pthread_entry)entryFunction, userData);
pthread_attr_destroy(&attr);
查看运行线程的回调函数 entryFunction 可以看到该方法是通过方法第一个参数传进来,查看调用方法 可以看到 调用的是 _threadLoop 该方法。_threadLoop 方法里面 是 wihile 循环 ,有变量 first 判断,如果是true 即会执行 readyToRun 方法 在执行 threadLoop
if (first) {
first = false;
self->mStatus = self->readyToRun();
result = (self->mStatus == OK);
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();
}
这就是我们经常实现线程会重写的方法
编写demo
新建 ThreadAndroid.h 文件,代码如下,声明该类有的方法
/*
* 通过ifndef ifnotdefind 避免有文件多次引入 导致重复定义
*/
#ifndef _THREAD_ANDROID_H
#define _THREAD_ANDROID_H
#include <utils/threads.h>
namespace android {
class ThreadAndroid: public Thread {
public:
ThreadAndroid();
virtual void onFirstRef();
virtual status_t readyToRun();
//如果返回true,循环调用此函数,返回false下一次不会再调用此函数
virtual bool threadLoop();
virtual void requestExit();
private:
int hasRunCount = 0;
};
}
#endif
新建 ThreadAndroid.cpp 文件, 实现引入定义的头文件,代码如下
#define LOG_TAG "threadAndoird"
#include <utils/Log.h>
#include "ThreadAndroid.h"
namespace android {
// 调用基类 Thread 的构造函数,并传参数 false
ThreadAndroid::ThreadAndroid(): Thread(false) {
ALOGD("MyThread");
}
bool ThreadAndroid::threadLoop() {
ALOGD("threadLoop hasRunCount = %d",hasRunCount);
hasRunCount++;
if (hasRunCount == 10) {
return false;
}
return true;
}
void ThreadAndroid::onFirstRef() {
ALOGD("onFirstRef");
}
status_t ThreadAndroid::readyToRun() {
ALOGD("readyToRun");
return 0;
}
void ThreadAndroid::requestExit() {
ALOGD("requestExit");
}
}
新建 MainThreadAndroid.cpp 的 main 函数执行文件,初始化 并调用该 线程,代码如下
#define LOG_TAG "MainThreadAndroid"
#include <utils/Log.h>
#include <utils/threads.h>
#include "ThreadAndroid.h"
using namespace android;
int main()
{
sp<ThreadAndroid> thread = new ThreadAndroid;
thread -> run("MainThreadAndroid", PRIORITY_URGENT_DISPLAY);
// 需要用循环判断该线程是否执行完成,不然会 执行下面的return 0 导致程序终止
while(1) {
if (!thread->isRunning()) {
break;
}
}
ALOGD("main end");
return 0;
}
最后新建 Android.mk 文件,执行 make thread_android 编译该模块为二进制文件,重复上面命令 推包到 模拟器 并执行
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := MainThreadAndroid.cpp \
ThreadAndroid.cpp \
LOCAL_SHARED_LIBRARIES :=libandroid_runtime \
libcutils \
libutils \
liblog
LOCAL_MODULE := thread_android
LOCAL_PRELINK_MODULE := false
include $(BUILD_EXECUTABLE)
执行结果如下,至此我们就完成了使用该类的实操