frameworks 之线程类使用

接触android 底层代码后,会有很多关于线程的启动和使用,不掌握线程类相关写法,对阅读代码理解比较困难。该文讲解有关线程类的使用。

Linux下的线程库 pthread_t

pthread_t 是Linux下的线程库。使用该线程步骤主要经历3个步骤

  1. 创建 pthread_create
  2. 执行 pthread_join
  3. 退出 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)

执行结果如下,至此我们就完成了使用该类的实操
在这里插入图片描述
在这里插入图片描述

  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值