作者:韩亚飞_yue31313_韩梦飞沙 QQ:313134555
Android构架:
Activity:
一个应用要与用户交户,必须至少有一个Activity用于显示view
通过Activity可以实现启动另一个Activity,---startActivity
通过Activity可以获得所有的view对象---findViewByid
通过Activity可以启动Service.startService
通过Activity可以获得广播接收者 getContentResolver();
contentProvider:
一个应用中的数据可能要提供给外部访问,但是又想外部通过自已定义的规则都访问,则就要用到内容提供者contentProvider
内容提供者可以通过Uri(content://内容提供者的Authority)去定位一个,应用的的contentProvider,调用CURD方法,去操作该应用的数据(这个数据不仅限于数据库,具体是什么,看该应用的内容提供者是怎么定的)
第二:如果可以通过getContentResolver().registerContentObserver方法通过uri去给一个特定应用注册一个观察者,就可以监测应用中,定义了getContext().getContentResolver().notifyChange(uri,null);的方法的变化,没有定义这个方法,是无法观察的.
BroadcastReceiver::
Android在进行一些动作的时候,如开机,接电话等,都会发送一个广播,通过广播的形式都告诉对应的应用去响应和处理.
一个应用,也可以在做某一个动作的时候发送广播给其它应用,当其它应用收广播,进行相应的响应和处理.
广播分有序与无序两种发送方法也不一样,无序全部传有序就是应用根据priority="5"优先权去传:越大越优先
而应用,则通过manifest中配置receiver 的Intent-filter定义接收广播的类型
Service:
一个应用可能需要调用另一个应用的功能,这个时候就就可以通过意图去startService来启动一个应用的服务,或bindService去绑定一个服务,从而去调用Service的方法.
或应用需要一直存在后台,不被kill,则可以开一个服务,因为只在服务在后台运行就不会被清理.
Context:
Intent:
Listener:
CallBack:
Adapter
hander
JNI
定义用到的技术资源
JNI是什么?
JNI(Java NativeInterface,JAVA原生接口)
使用JNI可以使Java代码和其他语言写的代码(如C/C++代码)进行交互。
JNI的作用
|- 首先,Java语言提供的类库无法满足要求,且在数学运算,实时渲染的游戏上,音视频处理等方面上与C/C++相比效率稍低。
|- 然后,Java语言无法直接操作硬件,C/C++代码不仅能操作硬件而且还能发挥硬件最佳性能。
|- 接着,使用Java调用本地的C/C++代码所写的库,省去了重复开发的麻烦,并且可以利用很多开源的库提高程序效率。
C语言常见术语:
库函数:
|- 为了代码重用,在C语言中提供了一些常用的、用于执行一些标准任务(如输入/出)的函数,这些函数事先被编译,并生成目标代码,然后将生成的目标代码打包成一个库文件,以供再次使用。 库文件中的函数被称为库函数,库文件被称为函数库。
|- 在Windows中C语言库函数中的中间代码都是以.obj为后缀的,Linux中是以 .o为后缀。
提示:单个目标代码是无法直接执行的,目标代码在运行之前需要使用连接程序将目标代码和其他库函数连接在一起后生成可执行的文件。 Windows下.dll的文件 , linux下 .so .a的文件.
头文件:
|- 头文件中存放的是对某个库中所定义的函数、宏(define)、类型、全局变量等进行声明,它类似于一份仓库清单。若用户程序中需要使用某个库中的函数,则只需要将该库所对应的头文件include到程序中即可。
|- 头文件中定义的是库中所有函数的函数原型。而函数的具体实现则是在库文件中。
|- 简单的说:头文件是给编译器用的,库文件是给连接器用的。
|- 在连接器连接程序时,会依据用户程序中导入的头文件,将对应的库函数导入到程序中。头文件以.h为后缀名。
函数库:
|- 动态库:在编译用户程序时不会将用户程序内使用的库函数连接到用户程序的目标代码中,只有在运行时,且用户程序执行到相关函数时才会调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。
|- 静态库:在编译用户程序时会将其内使用的库函数连接到目标代码中,程序运行时不再需要静态库。使用静态库生成可执行文件比较大。
在Linux中:
|- 静态库命名一般为:lib+库名+.a 。
|- 如:libcxy.a 其中lib说明此文件是一个库文件,cxy是库的名称,.a说明是静态的。
|- 动态库命名一般为:lib+库名+.so 。.so说明是动态的。
交叉编译:
|- 将中间代码连接成当前计算机可执行的二进制程序时,连接程序会根据当前计算机的CPU、操作系统的类型来转换。
根据运行的设备的不同,可以将cpu分为:
|- arm结构 :主要在移动手持、嵌入式设备上。
|- x86结构 : 主要在台式机、笔记本上使用。如Intel和AMD的CPU 。
若想在使用了基于x86结构CPU的操作系统中编译出可以在基于arm结构CPU的操作系统上运行的代码,就必须使用交叉编译。
交叉编译:在一个平台下编译出在另一个平台中可以执行的二进制代码。Google提供的NDK就可以完成交叉编译的工作。
NDK全称:Native Development Kit 。
|- NDK是一系列工具的集合,它有很多作用。
|- 首先,NDK可以帮助开发者快速开发C(或C++)的动态库。
|- 其次,NDK集成了交叉编译器。使用NDK,我们可以将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
NDK工具必须在Linux下运行,它可以在linux环境下编译出可以在arm平台下运行的二进制库文件。
使用JNI技术,其实就是在Java程序中,调用C语言的函数库中提供的函数,来完成一些Java语言无法完成的任务。由于Java语言和C语言结构完全不相同,因此若想让它们二者交互,则需要制定一系列的规范。JNI就是这组规范,此时 Java只和JNI交互,而由JNI去和C语言交互。
JNI技术的实现原理:
JNI技术分为两部分:Java端和C语言端。且以Java端为主导。
|- 首先,Java程序员在Java端定义一些native方法,并将这些方法以C语言头文件的方式提供给C程序员。
|- 然后,C程序员使用C语言,来实现Java程序员提供的头文件中定义的函数。
|- 接着,C程序员将函数打包成一个库文件,并将库文件交给Java程序员。
|- 最后,Java程序员在Java程序中导入库文件,然后调用native方法。
在Java程序执行的时候,若在某个类中调用了native方法,则虚拟机会通过JNI来转调用库文件中的C语言代码。提示:C代码最终是在Linux进程中执行的,而不是在虚拟机中。
问题:在Android中可执行的文件,在Linux中一定可以执行吗?
答: 不一定,必须是arm处理器的linux系统下编译好的才可以.
JNI与C/C++交互
JNI实现java调用C
创建一个android工程
1. 首先,在java中定义public anitve 方法名();
public native int add(int a,int b);
public native Stringget(String s);
public native int[] set(int[] arr );
2. 用java自带工具javah生成头文件
注:如果是jdk1.6版本进入应用bin下的classes目录编译:
如果是jdk1.7则进入应用下src下目录编译:
E:\exlipse_workspace\jnidemo\bin\classes>javahday.demo.DataProvider
生成day_data_DataProvider.h头文件,拷贝入jni目录下
3. 创建jni目录,编写c代码,
把头文件方法拷贝过来,加上参数名:名字要对应
记得要导包:
#include<stdio.h>;
#include<stdlib.h>;
#include<malloc.h>;
#include<jni.h>;
#include "day_data_DataProvider.h"
JNIEXPORT jstring JNICALL Java_day_data_DataProvider_get(JNIEnv* env,jobject obj, jstring s) {
具本实现....
JNIEnv 类型代表了java环境 通过JNIEnv* 指针,就可以对java端的代码进行操作.
创建java类的对象,调用java对象的方法
获取java对象的属性 等等.
jobject是个什么
jobject obj 就是当前方法所在的类.
}
4. 编写Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Hello--表示jni加载时的名字
LOCAL_SRC_FILES := Hello.c--C源文件.(如果是C++,则就是Hello.cpp)
注:如有多个c文件,直接在后面空格接上就行
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
5. Ndk编译生成动态库
fada@fada-PC /cygdrive/e/exlipse_workspace/jnidemo/jni
$ ndk-build
Compile thumb : Hello <=Hello.c
E:/exlipse_workspace/jnidemo/jni/Hello.c
E:/exlipse_workspace/jnidemo/jni/Hello.c
E:/exlipse_workspace/jnidemo/jni/Hello.c
E:/exlipse_workspace/jnidemo/jni/Hello.c
SharedLibrary : libHello.so
Install : libHello.so=> libs/armeabi/libHello.so
6. Java代码load 动态库.调用native代码
static{
System.loadLibrary("Hello");--这个名要和Android.mk中的文件名一样
}
public class JnidemoActivity extends Activity {
private DataProviderdp;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
dp = new DataProvider();
}
调用方法:
public void clickTo(View view){
int[] arr={1,2,3,4};
Toast.makeText(this, Arrays.toString(dp.add(3,4))+"",0).show();
}
JNI实现java调用C++
跟上面基本一样,只不过把Hello.c改成Hello.cpp
另外jni库中的c++调用结构体里面的方法用的是:
env->方法名
而C用的是(*env)->方法名
另外如果一个方法里没有用到的参数,C++可以不用参数名.
JNI实现C回调java
有点类似于java的反射原理
在C的方法中:
JNIEXPORT void JNICALL Java_com_itheima14_ndk4_DataProvider_callback1
(JNIEnv * env, jobject obj){
//C调用java空方法
指定 类名
char* classname = "com/itheima14/ndk4/DataProvider";
LOGI("1");
根据类名, 查找对应的类
//jclass (*FindClass)(JNIEnv*, const char*);
jclass clazz = (*env)->FindClass(env, classname);
LOGI("2");
在指定的类中,查找指定的方法ID
//jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
//jclass 指定类
//const char* 指定方法名
//const char* 方法的签名 返回类型、参数信息 javap -s 包名.类名 得到方法的签名
public void helloFromJava();
Signature: ()V--无返回值无参
public int Add(int, int);
Signature: (II)I---有两个int参数,无返回值
public void printString(java.lang.String);
Signature: (Ljava/lang/String;)V--有一个String参数,返回值
public native int[] set(int[]);
Signature: ([I)[I----int型数组参数,无返回值
jmethodID method = (*env)->GetMethodID(env, clazz, "helloFromJava", "()V");
LOGI("3");
执行该方法
无返回值,无参数
// void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
(*env)->CallVoidMethod(env, obj, method);
LOGI("4");--打logcat的
有返回值,有参数,
有参数则:直接在方法的最后加上参数就可以,有多个加多个少
jmethodID method = (*env)->GetMethodID(env, clazz, "Add", "(II)I");
//4执行该方法
// jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
jint result = (*env)->CallIntMethod(env, obj,method, 10, 30);
}
有参数,无返回值
jmethodID method = (*env)->GetMethodID(env, clazz, "printString", "(Ljava/lang/String;)V");
LOGI("3");
//4执行该方法
// void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
(*env)->CallVoidMethod(env,obj, method, (*env)->NewStringUTF(env, "hello") );
LOGI("4");
在C中实现使用logcat
类似于printf,可以通过二分法查找对应的错误位置
通过判断哪段代码没有执行去定义错误的位置.
C代码中增加
#include <android/log.h>
#define LOG_TAG "System.out"
#define LOGD(...)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
方法里面用
LOGI("info\n");
LOGD("debug\n");
C语言读写操作
C语言操作模式--用于fopen的第二个参数.
“rt” 只读打开一个文本文件,只允许读数据
“wt” 只写打开或建立一个文本文件,只允许写数据
“at” 追加打开一个文本文件,并在文件末尾写数据
“rb” 只读打开一个二进制文件,只允许读数据
“wb” 只写打开或建立一个二进制文件,只允许写数据
“ab” 追加打开一个二进制文件,并在文件末尾写数据
“rt+” 读写打开一个文本文件,允许读和写
“wt+” 读写打开或建立一个文本文件,允许读写
“at+” 读写打开一个文本文件,允许读,或在文件末追加数据
“rb+” 读写打开一个二进制文件,允许读和写
“wb+” 读写打开或建立一个二进制文件,允许读和写
“ab+” 读写打开一个二进制文件,允许读,或在文件末追加数据
对于文件使用方式有以下几点说明:
文件使用方式由r,w,a,t,b,+六个字符拼成,各字符的含义是:
r(read): 读
w(write): 写
a(append): 追加
t(text): 文本文件,可省略不写
b(banary): 二进制文件
读取数据
#include <stdio.h>
#include <stdlib.h>
main(){
//模式 读取数据
FILE* fp = fopen("1.txt", "rt");
/*
函数名: fread
功 能: 从一个流中读数据
用 法: int fread(void *ptr, int size, int nitems, FILE *stream);
void *ptr 数据存放的数组
int size 一个读取多少个字节
int nitems 读取的次数
FILE *stream 指定读取的文件
返回值:读取了多少次数据
*/
char arr[20] = "\0";
int count = fread(arr, sizeof(char), 5, fp);
printf("读取了 %d 次数据 \n",count);
printf("读取到的数据为: %s \n", arr);
fclose(fp);
system("pause");
}
写数据
#include <stdio.h>
#include <stdlib.h>
main(){
// 1 打开文件
/*
函数名: fopen
功 能: 打开一个流
用 法: FILE *fopen(char *filename, char *type);
//char *filename 文件名称
//char *type 操作模式
返回值: FILE* 文件的首地址
*/
FILE* fp = fopen("1.txt", "wt");
// 2 写数据
/*
函数名: fwrite
功 能: 写内容到流中
用 法: int fwrite(void *ptr, int size, int nitems, FILE *stream);
void *ptr 需要写入的数据
int size 一个写入多少个字节
int nitems 写入的次数
FILE *stream 指定写入的文件
返回值:写入了多少次数据
*/
char* arr = "12345";
int count = fwrite(arr, sizeof(char), 5, fp);
printf("写入了 %d 次数据 \n",count);
// 3 关闭文件
/*
函数名: fclose
功 能: 关闭一个流
用 法: int fclose(FILE *stream);
*/
fclose(fp);
system("pause");
}