android-NDK开发3

知识补充

  1. POSIX表示可移植操作系统接口(Portable Operating System Interface of UNIX,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准,是IEEE为要在各种UNIX操作系统上运行的软件而定义的一系列API标准的总称
  2. Bionic是谷歌为android开发的POSIX标准的c库,不复用现有的gun c库
  3. 几乎每一种语言都有一个标准库,java为JCL,android扩展了它
  4. 后面的system,gunstl是c++的标准库,由于c++功能较多每个库所提供的能力不一
  5. C语言的标准库由ANSI C标准制定,POSIX附加了些
  6. 静态,全局变量在程序加载时分配内存(直接从文件存于内存中)
  7. 思考、查询:c与java等的库都是基于什么实现的?(猜测为系统api)
1
2
3
4
5
6
7
8
9
10
数组名与函数名都可以当指针,不过只有sizeof数组名比较特殊会显示数组大小。也就是说数组名和类型的指针其实是不同的。
char *pa[4]
char (*pa)[4]
//[]的优先级比较高所以第一个pa被优先识别为数组,后识别数组的类型-char*,
//第二个被识别为*pa,后识别指针的类型-为char [4]的数组
同理
int *func(int,int);//是一个全局的变量声明,可以直接用该函数
int (*func)(int,int);
//第一个是指针函数,优先识别为函数,其返回类型为int*,可以用来调用
//第二个为函数指针,优先识别为指针,其返回类型为int,参数为俩个int,可以赋值后调用

Bionic API

该库不与其他c库兼容,其他c库编译的程序不可以动态链接Bionic,但其他c库静态连接生成的程序不动态加载系统库的话可以在android上运行。
该库提供了:
内存管理,文件io,字符操作,数字,日期时间,进程控制,信号处理,套接字,多线程,用户与组,系统配置,命名服务切换

内存管理

stdlib.h
c:malloc分配 free释放 realloc改变
c++:new分配 delete delete[]释放

文件io

三种标准的:stdin,stdout,stderr
FILE* fopen(“文件名”,”模式”);
r只读,w只写、存在覆盖,a附加。r+读写,w+读写、存在覆盖,a+读写附加、读写位置分离

FILE*表示流

写入:fwrite二进制,fputs写字串,fputc写字符,fprintf格式写

fflush:写入的数据是异步写入而且会积累,读取是块读取。双向都有缓冲,刷新用于把缓冲区中的刷入目的中。

读取:fread二进制,fgets读一行,fgetc读一个,fscanf格式读

feof:检查流末尾,0表示还有,EOF=-1表示错误并末尾
ferror:错误检查

fseek(流,相对便宜,参照):改变流的位置

fclose:关闭流

与进程交互

stdilb.h
system(“命令”);执行shell命令开启子进程,不能交互,只能得知结果exit(),而且会阻塞

FILE popen(“命令”,”模式”);返回一个流,非阻塞,可以对结果读取
pclose(FILE
)等待子进程终止,返回exit状态。

系统配置

键值对保存的属性:环变、版本、用户、当前目录、分隔符等
sys/system_properties.h
int system_property_get()通过名字获取值
prop_info*
system_property_find()通过名字获取系统属性直接指针
java也可以通过System.Property读取

用户和组

android中每个应用被当成不同的用户对待,通过依赖用户的权限模型来禁止应用访问其他应用的数据,服务和硬件资源也是用用户的权限模型来保护的。


每个应用从10000开始获取用户id和组id,并拥有分配的用户名app_id尾
uid_t a=getuid();获取用户id
gid_t a=getgid();获取组id
char * a=getlogin();获取用户名

进程间通信

目前只能用android java的Binder
当系统决定要在一个新的进程中启动一个Activity或者Service时,它就会创建一个新的进程了,所以service和activity是属于不同进程的,想要通讯必须用binder(第一行代码中的下载服务)
https://www.cnblogs.com/Doing-what-I-love/p/5530291.html

线程

java调用native时进程不变,线程也不变。所以ui线程调用原生代码阻塞时ui也会阻塞。

用java创建线程

由于方法id可以被缓存,同时每次调用原生方法都得会传入java线程的env和object。所以可以用java创建线程然后在run中调用native方法执行具体操作。此时应该在java中留有回调方法。
好处:简单方便,缺点:原生不能创建java线程,原生代码线程需要安全,线程间的原生代码不能通信
示例
java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
/**
* Main activity.
*
* @author Onur Cinar
*/
public class MainActivity extends Activity {
/** Threads edit. */
private EditText threadsEdit;
/** Iterations edit. */
private EditText iterationsEdit;
/** Start button. */
private Button startButton;
/** Log view. */
private TextView logView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize the native code
nativeInit();
threadsEdit = (EditText) findViewById(R.id.threads_edit);
iterationsEdit = (EditText) findViewById(R.id.iterations_edit);
startButton = (Button) findViewById(R.id.start_button);
logView = (TextView) findViewById(R.id.log_view);
startButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
int threads = getNumber(threadsEdit, 0);
int iterations = getNumber(iterationsEdit, 0);
if (threads > 0 && iterations > 0) {
startThreads(threads, iterations);
}
}
});
}
@Override
protected void onDestroy() {
// Free the native resources
nativeFree();
super.onDestroy();
}
/**
* On native message callback.
*
* @param message
* native message.
*/
private void onNativeMessage(final String message) {
runOnUiThread(new Runnable() {
public void run() {
logView.append(message);
logView.append("\n");
}
});
}
/**
* Gets the value of edit text as integer. If the value
* is empty or count not be parsed, it returns the
* default value.
*
* @param editText edit text.
* @param defaultValue default value.
* @return numeric value.
*/
private static int getNumber(EditText editText, int defaultValue) {
int value;
try {
value = Integer.parseInt(editText.getText().toString());
} catch (NumberFormatException e) {
value = defaultValue;
}
return value;
}
/**
* Starts the given number of threads for iterations.
*
* @param threads thread count.
* @param iterations iteration count.
*/
private void startThreads(int threads, int iterations) {
javaThreads(threads, iterations);
}
/**
* Initializes the native code.
*/
private native void nativeInit();
/**
* Free the native resources.
*/
private native void nativeFree();
/**
* Native worker.
*
* @param id worker id.
* @param iterations iteration count.
*/
private native void nativeWorker(int id, int iterations);
/**
* Using the POSIX threads.
*
* @param threads thread count.
* @param iterations iteration count.
*/
private native void posixThreads(int threads, int iterations);
/**
* Using Java based threads.
*
* @param threads thread count.
* @param iterations iteration count.
*/
private void javaThreads(int threads, final int iterations) {
// Create a Java based thread for each worker
for (int i = 0; i < threads; i++) {
final int id = i;
Thread thread = new Thread() {
public void run() {
nativeWorker(id, iterations);
}
};
thread.start();
}
}
static {
System.loadLibrary("Threads");
}
}

c++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <stdio.h>
#include <unistd.h>
static jmethodID gOnNativeMessage = NULL;
void Java_com_apress_threads_MainActivity_nativeInit (
JNIEnv* env,
jobject obj)
{
// If method ID is not cached
if (NULL == gOnNativeMessage)
{
// Get the class from the object
jclass clazz = env->GetObjectClass(obj);
// Get the method id for the callback
gOnNativeMessage = env->GetMethodID(clazz,
"onNativeMessage",
"(Ljava/lang/String;)V");
// If method could not be found
if (NULL == gOnNativeMessage)
{
// Get the exception class
jclass exceptionClazz = env->FindClass(
"java/lang/RuntimeException");
// Throw exception
env->ThrowNew(exceptionClazz, "Unable to find method");
}
}
}
void Java_com_apress_threads_MainActivity_nativeFree (
JNIEnv* env,
jobject obj)
{}
void Java_com_apress_threads_MainActivity_nativeWorker (
JNIEnv* env,
jobject obj,
jint id,
jint iterations)
{
// Loop for given number of iterations
for (jint i = 0; i < iterations; i++)
{
// Prepare message
char message[26];
sprintf(message, "Worker %d: Iteration %d", id, i);
// Message from the C string
jstring messageString = env->NewStringUTF(message);
// Call the on native message method
env->CallVoidMethod(obj, gOnNativeMessage, messageString);
// Check if an exception occurred
if (NULL != env->ExceptionOccurred())
break;
// Sleep for a second
sleep(1);
}
}

原生线程

使用库在c++代码中创建线程,与其他平台不同,编译时不用链接其它任何库

1
2
3
4
5
//创建线程
int pthread_create(pthread_t *tidp,
const pthread_attr_t *attr,
(void*)(*start_rtn)(void*),
void *arg);

若线程创建成功,则返回0。若线程创建失败,则返回出错编号,并且*thread中的内容是未定义的。注意这个result只是是否创建成功,不是线程函数返回的void指针。

第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。函数指针
最后一个参数是运行函数的参数。

示例:
java:

1
2
3
4
把上修改为:
private void startThreads(int threads, int iterations) {
posixThreads(threads, iterations);
}

c++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
// Native worker thread arguments
struct NativeWorkerArgs
{
jint id;
jint iterations;
};
// Method ID can be cached
static jmethodID gOnNativeMessage = NULL;
// Java VM interface pointer
static JavaVM* gVm = NULL;
// Global reference to object
static jobject gObj = NULL;
jint JNI_OnLoad (JavaVM* vm, void* reserved)
{
// Cache the JavaVM interface pointer
gVm = vm;
return JNI_VERSION_1_4;
}
void Java_com_apress_threads_MainActivity_nativeInit (
JNIEnv* env,
jobject obj)
{
// If object global reference is not set
if (NULL == gObj)
{
// Create a new global reference for the object
gObj = env->NewGlobalRef(obj);
if (NULL == gObj)
{
goto exit;
}
}
// If method ID is not cached
if (NULL == gOnNativeMessage)
{
// Get the class from the object
jclass clazz = env->GetObjectClass(obj);
// Get the method id for the callback
gOnNativeMessage = env->GetMethodID(clazz,
"onNativeMessage",
"(Ljava/lang/String;)V");
// If method could not be found
if (NULL == gOnNativeMessage)
{
// Get the exception class
jclass exceptionClazz = env->FindClass(
"java/lang/RuntimeException");
// Throw exception
env->ThrowNew(exceptionClazz, "Unable to find method");
}
}
exit:
return;
}
void Java_com_apress_threads_MainActivity_nativeFree (
JNIEnv* env,
jobject obj)
{
// If object global reference is set
if (NULL != gObj)
{
// Delete the global reference
env->DeleteGlobalRef(gObj);
gObj = NULL;
}
}
void Java_com_apress_threads_MainActivity_nativeWorker (
JNIEnv* env,
jobject obj,
jint id,
jint iterations)
{
// Loop for given number of iterations
for (jint i = 0; i < iterations; i++)
{
// Prepare message
char message[26];
sprintf(message, "Worker %d: Iteration %d", id, i);
// Message from the C string
jstring messageString = env->NewStringUTF(message);
// Call the on native message method
env->CallVoidMethod(obj, gOnNativeMessage, messageString);
// Check if an exception occurred
if (NULL != env->ExceptionOccurred())
break;
// Sleep for a second
sleep(1);
}
exit:
return;
}
static void* nativeWorkerThread (void* args)
{
JNIEnv* env = NULL;
// Attach current thread to Java virtual machine
// and obrain JNIEnv interface pointer
if (0 == gVm->AttachCurrentThread(&env, NULL))
{
// Get the native worker thread arguments
NativeWorkerArgs* nativeWorkerArgs = (NativeWorkerArgs*) args;
// Run the native worker in thread context
Java_com_apress_threads_MainActivity_nativeWorker(env,
gObj,
nativeWorkerArgs->id,
nativeWorkerArgs->iterations);
// Free the native worker thread arguments
delete nativeWorkerArgs;
// Detach current thread from Java virtual machine
gVm->DetachCurrentThread();
}
return (void*) 1;
}
void Java_com_apress_threads_MainActivity_posixThreads (
JNIEnv* env,
jobject obj,
jint threads,
jint iterations)
{
// Create a POSIX thread for each worker
for (jint i = 0; i < threads; i++)
{
// Thread handle
pthread_t thread;
// Native worker thread arguments
NativeWorkerArgs* nativeWorkerArgs = new NativeWorkerArgs();
nativeWorkerArgs->id = i;
nativeWorkerArgs->iterations = iterations;
// Create a new thread
int result = pthread_create(
&thread,
NULL,
nativeWorkerThread,
(void*) nativeWorkerArgs);
if (0 != result)
{
// Get the exception class
jclass exceptionClazz = env->FindClass(
"java/lang/RuntimeException");
// Throw exception
env->ThrowNew(exceptionClazz, "Unable to create thread");
}
}
}

获取线程的返回结果

1
int pthread_join(pthread_t thread,void** ret);

该函数让当前线程等待目标线程结束,第一个参数是线程句柄,第二个参数接受返回结果(为了改变void指针的指向必须传指针的指针)。返回值表示是否成功0。

线程同步

有多种同步机制,这里有互斥锁与信号量

互斥锁

返回值都是0成功
初始化锁

1
2
int pthread_mutex_init(pthread_mutex_t * mutex,
const pthread_mutex_t *attr);

mutex 互斥量
attr 互斥锁属性
加锁

1
int pthread_mutex_lock(pthread_mutex_t *mutex);

若以锁将会线程阻塞等待
解锁

1
int pthread_mutex_unlock(pthread_mutex_t *mutex);

销毁锁

1
int pthread_mutex_destroy(pthread_mutex_t *mutex);

已锁的锁销毁结果不可确定

信号量

信号量有可同步使用数量限制。

初始化

1
int sem_init(sem_t *sem, int pshared,unsigned int value);

sem :信号量变量;
pshared:表明是否在多个进程中使用信号量,如果是则应该传递非0值;
value:该参数指定信号量的初始值。如果想要在两个进程之间使用信号量,需要确保sem参数指向两个进程之间共享的内存范围。
锁定

1
int sem_wait(sem_t *sem);

若信号量不等于0就锁并减一
解锁

1
int sem_post(sem_t *sem);

信号量加一
销毁

1
int sem_destroy(sem_t *sem);

C++的库

https://blog.csdn.net/suningning/article/details/74510591