Android安全机制简介

一、代码混淆proguard

由于java字节码可以被反编译,代码混淆可以混淆关键代码,增加阅读代码难度。同时proguard可以压缩代码、优化编译后的java字节码。

二、应用权限检查

应用声明的权限需要得到系统和用户的认可

三、签名证书

Android系统识别App的凭证,App的身份证。

四、Linux内核安全机制

比如文件访问机制等,权限系统。

五、Android虚拟机沙箱机制

每个App有与之对应的Uid,分别运行在单独的虚拟机中,与其他应用完全隔离。即使一个应用崩溃,也不会导致其他应用异常。

CoordinatorLayout的使用

一、前言

1.什么是嵌套滚动机制(NestedScrolling)?

所谓嵌套滚动其实就是界面布局中包含 一个可滚动的列表 和 一个不可滚动的View,这样在滚动列表时,首先将不可滚动View移出屏幕或移进屏幕,待不可滚动View固定时,才会继续滚动滚动列表的内容。

2.为什么要有滑动嵌套机制?

回想一下android系统事件分发机制,一个down事件到up事件为一个事件流,在这事件流之中一旦某个View拦截(处理)该Touch事件,那么后续这个事件流将统一交由它处理。

有时候我们需要将一个事件流分开成几个事件片给不同的View处理,那么我们需要调配事件分发。滑动嵌套机制应运而生。

二、Android嵌套滚动机制

1.重要的类

1
2
3
4
NestedScrollingChild接口
NestedScrollingParent接口
NestedScrollingChildHelper辅助类
NestedScrollingParentHelper辅助类

2.结构图

结构图结构图

三、注意事项

1.子类宽高

由于CoordinatorLayout是FrameLayout的增强版(官方文档),所以子View使用match_parent这个属性就需要注意了。

如下图,View B使用了match_parent,然后CoordinatorLayout在layout的时候把它的位置下移了,并且超出CoordinatorLayout的区域了。使用clipChildren属性是无效的,clipChildren默认是true的。
layoutlayout

四、参考文章

深入理解Android卷一笔记

一、JNI调用流程分析

1.Java层

示例代码:

1
2
3
4
5
6
7
8
public class MediaScanner {
static {
System.loadLibrary("media_jni");
native_init();
}
private static native void native_init();
}
  • 加载对应的JNI库。
  • 声明native函数。

2.JNI层

示例代码:

1
2
3
4
//cpp
static void android_media_MediaScanner_native_init(JNIEnv *env) {
...
}

那么Java层调用native函数,系统是如何找到JNI层的对应函数的呢?

一、静态注册

流程:

  • 编写java层native函数声明,并javac编译为class文件。
  • 使用javah -o output packagename.classname生成.h头文件。
  • JNI层cpp或者c文件实现该头文件。

当Java层第一次调用该native方法时,jvm会从对应库找对应的头文件。如果找到,保存这个函数指针供下次调用;如果没找到就抛异常。

弊端:

  • 修改Java层的native方式后需要重新生成头文件,因为函数签名可能改变了。
  • 初次调用native函数才会去建立关联,有懒加载特性,有利有弊。

二、动态注册

JNI中有一个数据结构专门来记录这种一一对应关系的。

1
2
3
4
typedef struct {
const char* signature;
void* fnPtr;
} JNINativeMethod;

AndroidRunTime.cpp提供了registerNativeMethods方法来注册;其中又通过JNIHelp.c的jniRegisterNativeMethods。

1
2
jclass clazz = (*env) -> FindClass(env, className);
(*env) -> RegisterNatives(env, clazz, gMethods, numMethods);
  • 1.当Java层通过System.loadLibrary加载so库时,会先找是否有JNI_OnLoad函数,如果有就调用它;
  • 2.动态注册调用一般写在这个函数里。

提示:
Android提供了JNIHelp.h这个文件,它内部包含了jni.h,所以我们编写jni时可以直接引入JNIHelp.h。

4.什么是JNIEnv?

JNIEnv是一个与线程相关的代表JNI环境的结构体。我们经常通过它来从native层回调java层函数,但由于它是线程相关的,所以我们不能在B线程调用A线程的JNIEnv。

5.什么是JavaVM?

JavaVM顾名思义,就是一个java虚拟机变量,可以比作进程。不管有多少个线程,单进程就单独一个JavaVM。在JNI_OnLoad函数可以获得JavaVM。

  • 调用JavaVM->AttachCurrentThread可以获得当前线程的JNIEnv,这样可以在子线程回调java层函数。
  • 在线程结束时,调用JavaVM->DetachCurrentThread来释放相关资源。

6.什么是jfieldID和jmethodID?

1.获取:

1
2
3
//JNIEnv方法
jfieldID GetFieldID (jclass clazz, const char* name, const char* sig);
jmethodID GetMethodID (jclass clazz, const char* name, const char* sig);

建议在初始化的时候获取并保存下来,提高下一次调用的运行效率。

2.使用:

1
2
3
4
5
6
//JNIEnv方法
NativeType Call<type>Method (jobject jobj, jmethodID methodID, ...);
NativeType CallStatic<type>Method (jmethodID methodID, ...);
NativeType Get<type>Field (jobject jobj, jfieldID fieldID);
void Set<type>Field (jobject jobj, jfieldID fieldID, NativeType value);

7.jstring

1.java层String与native层string的转换关系:
jstringjstring

2.释放资源:

1
2
3
//JNIEnv方法
void ReleaseStringChars(jstring str, char* chs);
void ReleaseStringUTFChars(jstring str, char* chs);

转换字符串和释放资源方法应该是成对出现的。

8.JNI类型签名

命令:javap -s -p xxx.class

意义:

  • -s 表示输出内部数据类型的签名信息。
  • -p 表示打印所有函数和成员的签名信息。默认只打印public级别。

9.引用

类型:

1
2
3
1.Local Reference 本地变量
2.Global Reference 全局变量(强印用)
3.Weak Global Reference 弱全局变量

使用:

1
2
3
4
5
6
7
//JNIEnv方法
NewGlobalRef(NativeType ref);
NewWeakGlobalRef(NativeType ref);
DeleteLoaclRef(NativeType ref);
DeleteGlobalRef(NativeType ref);
DeleteWeakGlobalRef(NativeType ref);

有时候我们可能创建太多LocalReference,这样内存占用可观,可以在方法结束调用前,提前手动回收LocalReference。

10.异常

JNI层可以截获和修改异常。

1
2
3
4
//JNIEnv方法
ExceptionOccured 判断是否发生异常
ExceptionClear 清空当前JNI发生的异常
ThrowNew 向java层抛异常