乱码三千 – 分享实用IT技术

乱码三千 – 码出一个新世界


  • 首页

  • 归档

  • 搜索

Android中Aop和Apt有什么区别?

发表于 2019-12-16

什么是Aop?

AOP指的是:面向切面编程(Aspect-Oriented Programming)。如果说,OOP如果是把问题划分到单个模块的话,那么AOP就是把涉及到众多模块的某一类问题进行统一管理。

代表框架:

  • Hugo(Jake Wharton)
  • SSH
  • SpringMVC

Android 中应用

  • 日志
  • 持久化
  • 性能监控
  • 数据校验
  • 缓存
  • 按钮防抖
  • 其他更多

Android AOP就是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,提高开发效率

使用姿势

在Java中使用aop编程需要用到AspectJ切面框架,AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。

1.在build.gradle文件中引入AspectJ

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
pply plugin: 'com.android.application'
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
android {
...
}

final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->
if (!variant.buildType.isDebuggable()) {
log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
return;
}

JavaCompile javaCompile = variant.javaCompile
javaCompile.doLast {
String[] args = ["-showWeaveInfo",
"-1.8",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
log.debug "ajc args: " + Arrays.toString(args)

MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler);
for (IMessage message : handler.getMessages(null, true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
log.warn message.message, message.thrown
break;
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
}
}


dependencies {
...
implementation 'org.aspectj:aspectjrt:1.8.9'
}

2.创建注解类

1
2
3
4
5
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface SingleClick {
long clickIntervals() default 800;
}

3.创建切面类

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
/**
* 添加切面注解
*/
@Aspect
public class AopTest {
private static final String TAG = "linhaojian";
/**
* 定义切入点(定义那些类或者方法需要改变)
*/
@Pointcut("execution(* com.lhj.test_apt..*ck(..))")
public void pointcut1() {
}
/**
* 使用注解方式,定义注解
*/
@Pointcut("execution(@com.lhj.test_apt.DebugLog * *ck(..))")
public void pointcut() {
}
/**
* 前置通知,切点之前执行
* @param point
*/
@Before("pointcut()")
public void logBefore(JoinPoint point){
Log.e(TAG,"logBefore");
}
/**
* 环绕通知,切点前后执行
* @param joinPoint
* @throws Throwable
*/
@Around("pointcut()")
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
Log.e(TAG,"logAround");
// 1.执行切点函数(如果不调用该方法,切点函数不被执行)
joinPoint.proceed();
}
/**
* 后置通知,切点之后执行
* @throws Throwable
*/
@After("pointcut()")
public void logAfter(JoinPoint point){
Log.e(TAG,"logAfter");
}
/**
* 返回通知,切点方法返回结果之后执行
* @throws Throwable
*/
@AfterReturning("pointcut()")
public void logAfterReturning(JoinPoint point, Object returnValue){
Log.e(TAG,"logAfterReturning ");
}
/**
* 异常通知,切点抛出异常时执行
* @throws Throwable
*/
@AfterThrowing(value = "pointcut()",throwing = "ex")
public void logAfterThrowing(Throwable ex){
Log.e(TAG,"logAfterThrowing : "+ex.getMessage());
}
}

4.使用

1
2
3
4
5
@SingleClick(clickIntervals = 2000)
@Override
public void onClick(View v) {
Toast.makeText(this, "1", Toast.LENGTH_SHORT).show();
}

难点:
AspectJ语法比较多,但是掌握几个简单常用的,就能实现绝大多数切片,完全兼容Java(纯Java语言开发,然后使用AspectJ注解,简称@AspectJ。)

优点:
AspectJ除了hook之外,AspectJ还可以为目标类添加变量,接口。另外,AspectJ也有抽象,继承等各种更高级的玩法。它能够在编译期间直接修改源代码生成class,强大的团战切入功能,指哪打哪,鞭辟入里。有了此神器,编程亦如庖丁解牛,游刃而有余。

什么是Apt?

APT(Annotation Processing Tool 的简称),可以在代码编译期解析注解,并且生成新的 Java 文件,减少手动的代码输入

代表框架:

  • DataBinding
  • Dagger2
  • ButterKnife
  • EventBus3
  • DBFlow
  • AndroidAnnotation

使用姿势

1,在android工程中,创建一个java的Module,写一个类继承AbstractProcessor

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
@AutoService(Processor.class) // javax.annotation.processing.IProcessor 
@SupportedSourceVersion(SourceVersion.RELEASE_7) //java
@SupportedAnnotationTypes({ // 标注注解处理器支持的注解类型
"com.annotation.SingleDelegate",
"com.annotation.MultiDelegate"
})
public class AnnotationProcessor extends AbstractProcessor {

public static final String PACKAGE = "com.poet.delegate";
public static final String CLASS_DESC = "From poet compiler";

public Filer filer; //文件相关的辅助类
public Elements elements; //元素相关的辅助类
public Messager messager; //日志相关的辅助类
public Types types;

@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
filer = processingEnv.getFiler();
elements = processingEnv.getElementUtils();
messager = processingEnv.getMessager();
types = processingEnv.getTypeUtils();

new SingleDelegateProcessor().process(set, roundEnvironment, this);
new MultiDelegateProcessor().process(set, roundEnvironment, this);

return true;
}
}

2,重写AbstractProcessor类中的process方法,处理我们自定义的注解,生成代码:

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
public class SingleDelegateProcessor implements IProcessor {  
@Override
public void process(Set<? extends TypeElement> set, RoundEnvironment roundEnv,
AnnotationProcessor abstractProcessor) {
// 查询注解是否存在
Set<? extends Element> elementSet =
roundEnv.getElementsAnnotatedWith(SingleDelegate.class);
Set<TypeElement> typeElementSet = ElementFilter.typesIn(elementSet);
if (typeElementSet == null || typeElementSet.isEmpty()) {
return;
}
// 循环处理注解
for (TypeElement typeElement : typeElementSet) {
if (!(typeElement.getKind() == ElementKind.INTERFACE)) { // 只处理接口类型
continue;
}

// 处理 SingleDelegate,只处理 annotation.classNameImpl() 不为空的注解
SingleDelegate annotation = typeElement.getAnnotation(SingleDelegate.class);
if ("".equals(annotation.classNameImpl())) {
continue;
}
Delegate delegate = annotation.delegate();

// 添加构造器
MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC);

// 创建类名相关 class builder
TypeSpec.Builder builder =
ProcessUtils.createTypeSpecBuilder(typeElement, annotation.classNameImpl());

// 处理 delegate
builder = ProcessUtils.processDelegate(typeElement, builder,
constructorBuilder, delegate);

// 检查是否继承其它接口
builder = processSuperSingleDelegate(abstractProcessor, builder, constructorBuilder, typeElement);

// 完成构造器
builder.addMethod(constructorBuilder.build());

// 创建 JavaFile
JavaFile javaFile = JavaFile.builder(AnnotationProcessor.PACKAGE, builder.build()).build();
try {
javaFile.writeTo(abstractProcessor.filer);
} catch (IOException e) {
e.printStackTrace();
}
}
}

3,在项目Gradle中添加 annotationProcessor project 引用

1
2
compile project(':apt-delegate-annotation')  
annotationProcessor project(':apt-delegate-compiler')

4,如果有自定义注解的话,创建一个java的Module,专门放入自定义注解。项目与apt Module都需引用自定义注解Module

4-1,主工程:

1
2
compile project(':apt-delegate-annotation')  
annotationProcessor project(':apt-delegate-compiler')

4-2,apt Module:

1
2
3
compile project(':apt-delegate-annotation')  
compile 'com.google.auto.service:auto-service:1.0-rc2'
compile 'com.squareup:javapoet:1.4.0'

5,生成的源代码在build/generated/source/apt下可以看到

难点

就apt本身来说没有任何难点可言,难点一在于设计模式和解耦思想的灵活应用,二在与代码生成的繁琐,你可以手动字符串拼接,当然有更高级的玩法用squareup的javapoet,用建造者的模式构建出任何你想要的源代码

优点

它的强大之处无需多言,看代表框架的源码,你可以学到很多新姿势。总的一句话:它可以做任何你不想做的繁杂的工作,它可以帮你写任何你不想重复代码。懒人福利,老司机必备神技,可以提高车速,让你以任何姿势漂移。它可以生成任何源代码供你在任何地方使用,就像剑客的剑,快疾如风,无所不及

Aop和Apt对比

如图所示:

和

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Kotlin中@JvmOverloads 注解

发表于 2019-12-16

在Kotlin中@JvmOverloads注解的作用就是:在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。
可能还是云里雾里,直接上代码,代码解释一切:
如果我们再kotlin中写如下代码:

1
2
3
fun f(a: String, b: Int = 0, c: String="abc"){
...
}

相当于在Java中声明

1
2
void f(String a, int b, String c){
}

默认参数没有起到任何作用。

但是如果使用的了@JvmOverloads注解:

1
2
@JvmOverloads fun f(a: String, b: Int=0, c:String="abc"){
}

相当于在Java中声明了3个方法:

1
2
3
void f(String a)
void f(String a, int b)
void f(String a, int b, String c)

是不是很方便,再也不用写那么多重载方法了。

注:该注解也可用在构造方法和静态方法。

1
2
3
4
5
class MyLayout: RelativeLayout {

@JvmOverloads
constructor(context:Context, attributeSet: AttributeSet? = null, defStyleAttr: Int = 0): super(context, attributeSet, defStyleAttr)
}

相当Java中的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyLayout extends RelativeLayout {

public MyLayout(Context context) {
this(context, null);
}

public MyLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public MyLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

kotlin中 data class 到底是个什么鬼

发表于 2019-12-16

data class就是一个类中只包含一些数据字段,类似于vo,pojo,java bean。一般而言,我们在Java中定义了这个数据类之后要重写一下toString,equals等方法。要生成get,set方法。

然而在Kotlin中这些都不在需要自己手动去敲了,编译器在背后默默给我们生成了如下的东西:

  • equals()/hashCode()
  • toString()方法
  • componentN()方法
  • copy()方法

如何申明一个简单的数据类? 有一下几点要求:

  • 主构造函数必须要至少有一个参数
  • 主构造函数中的所有参数必须被标记为val或者var
  • 数据类不能有以下修饰符:abstract,inner,open,sealed
  • data class只能实现接口(Kotlin1.1以前的规则),现在也可以继承其它类
1
data class User(var id: Int, var name: String)

就这么一行代码,你已然拥有了一个数据类

主构造函数中的所有参数必须被标记为var或者val,var就表示可读写,val就表示只读,这就相当于表明了数据字段的访问权限

componentN()方法是干嘛用的?

在主构造函数中有多少个参数,就会依次生成对应的component1,component2,component3……这些函数返回的就是对应字段的值

componentN函数是用来实现解构申明的

1
2
3
4
5
6
data class User(var id: Int,var name:String) 
fun main(args: Array<String>) {
var user: User = User(123, "liuliqianxiao")
var (id,name) = user//多重赋值
print("$id,$name")
}

拿上面的例子来说,给id赋值,其实调用的是user.component1(),给name赋值其实调用的是component2()函数。

有了这个解构申明,想在一个函数中返回多个结果,就可以申明一个简单的数据类来返回了,然后取值也很方便。

copy函数

默认生成的copy函数就是用现在的数据字段生成了一个新的对象。

1
fun copy(id: Int = this.id,name: String = this.name) = User(id,name)

如果只想改变其中的某些字段,就可以在调用copy的时候采用命名参数的方法进行调用

1
2
3
4
fun main(args: Array<String>) {
var user: User = User(123, "liuliqianxiao")
var other: User = user.copy(name = "mdzz")//只想改变名字
}

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Github Token的获取+PicGo图床配置

发表于 2019-12-14

Github获取token

在GitHub上获取个人访问令牌(Personal Access Token, PAT)的步骤如下:

  1. 登录到你的GitHub账户。
  2. 点击右上角的用户头像,选择Settings(设置
  3. 在侧边栏中点击Developer settings(开发者设置)
  4. 点击Personal access tokens(个人访问令牌)
  5. 点击Generate new token(生成新令牌)
  6. 填写必要的信息,例如令牌的描述和选择所需的权限
  7. 完成后,点击Generate token(生成令牌)
  8. 复制生成的令牌并保存,因为生成后不会再显示

PicGo图床配置

GitHub图床

  • 仓库名 :用户名/仓库名
  • 分支名 :master
  • 该仓库token : xxxxxxxxxx
  • 指定存储路径 :img/
  • 自定义域名访问(CDN) : https://cdn.jsdelivr.net/gh/用户名/仓库名

截图如下:

示例图如下:

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

java五大排序算法之选择排序

发表于 2019-12-09

一.选择排序介绍

选出最小的一个数与第一个位置的数交换

二.选择排序原理分析

第1趟比较:拿第1个元素依次和它后面的每个元素进行比较,如果第1个元素大于后面某个元素,交换它们,经过第1趟比较,数组中最小的元素被选出,它被排在第一位

第2趟比较:拿第2个元素依次和它后面的每个元素进行比较,如果第2个元素大于后面某个元素,交换它们,经过第2趟比较,数组中第2小的元素被选出,它被排在第二位

……

第n-1趟比较:第n-1个元素和第n个元素作比较,如果第n-1个元素大于第n个元素,交换它们

三.选择排序代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void selectionSort(int[] nums) {

if (nums == null || nums.length < 2) {
return;
}

for(int i = 0; i < nums.length - 1; i++) {
for(int j = i + 1; j < nums.length; j++) {
if(nums[i] > nums[j]) {
swap(nums, i, j);
}
}
}

}

public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}

四.选择排序的优化

使用临时变量存储最小值的角标值,减少交换的次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void selectSort(int[] numbers) {
int size = numbers.length; // 数组长度
int temp = 0; // 中间变量
for (int i = 0; i < size-1; i++) {
int k = i; // 待确定的位置
// 选择出应该在第i个位置的数
for (int j = size - 1; j > i; j--) {
if (numbers[j] < numbers[k]) {
k = j;
}
}
// 交换两个数
temp = numbers[i];
numbers[i] = numbers[k];
numbers[k] = temp;
}
}

五.选择排序的时间复杂度

时间复杂度:O(n²)

空间复杂度:O(1),只需要一个附加程序单元用于交换

稳定性:选择排序是不稳定的排序算法,因为无法保证值相等的元素的相对位置不变,例如 [3, 4, 3, 1, 5]这个数组,第一次交换,第一个3和1交换位置,此时原来两个3的相对位置发生了变化。

本帖附件

点击下载

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Java面试题之TCP和UDP的区别

发表于 2019-12-09

网络层的划分

一 网络层的划分

  • 物理层 :负责在物理线路上传输原始的二进制数据(0和1),该层数据以比特流的形式传输

  • 链路层: 负责在通信的实体间建立数据链路连接,该层数据以帧的形式传输

  • 网络层: 负责创建逻辑链路,以及实现数据包的分片和重组,实现拥塞控制、网络互连等功能,该层数据以IP数据报(IP分组)的形式传输

  • 传输层: 负责向用户提供端到端的通信服务,实现流量控制以及差错控制,这一层主要重点是两个协议 : UDP 和 TCP

  • 应用层: 为应用程序提供了网络服务,应用层协议最著名的就是HTTP, FTP了, 还有一个重要的DNS

    二、TCP和UDP的区别

三 TCP的三次握手

  • 第一次握手:建立连接
  • 第二次握手:响应连接
  • 第三次握手:测试数据

本帖附件

点击下载

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Source Insight快捷键大全

发表于 2019-12-06

Source Insight快捷键大全

退出程序 : Alt+F4

重画屏幕 : Ctrl+Alt+Space

完成语法 : Ctrl+E

复制一行 : Ctrl+K

恰好复制该位置右边的该行的字符 : Ctrl+Shift+K

复制到剪贴板 : Ctrl+Del

剪切一行 : Ctrl+U

剪切该位置右边的该行的字符 : Ctrl+;

剪切到剪贴板 : Ctrl+Shift+X

剪切一个字 : Ctrl+,

左边缩进 : F9

右边缩进 : F10

插入一行 : Ctrl+I

插入新行 : Ctrl+Enter

加入一行 : Ctrl+J

从剪切板粘贴 : Ctrl+Ins

粘贴一行 : Ctrl+P

重复上一个动作 : Ctrl+Y

重新编号 : Ctrl+R

重复输入 : Ctrl+

替换 : Ctrl+H

智能重命名 : Ctrl+’

关闭文件 : Ctrl+W

关闭所有文件 : Ctrl+Shift+W

新建 : Ctrl+N

转到下一个文件 : Ctrl+Shift+N

打开 : Ctrl+O

重新装载文件 : Ctrl+Shift+O

另存为 : Ctrl+Shift+S

显示文件状态 : Shift+F10

激活语法窗口 : Alt+L

回到该行的开始 : Home

回到选择的开始 : Ctrl+Alt+[

到块的下面 : Ctrl+Shift+]

到块的上面 : Ctrl+Shift+[

书签 : Ctrl+M

到文件底部 : Ctrl+End, Ctrl+(KeyPad) End

到窗口底部 : (KeyPad) End (小键盘的END)

到一行的尾部 : End

到选择部分的尾部 : Ctrl+Alt+]

到下一个函数 : 小键盘 +

上一个函数 : 小键盘 -

后退 : Alt+,, Thumb 1 Click

后退到索引 : Alt+M

向前 : Alt+., Thumb 2 Click

转到行 : F5, Ctrl+G

转到下一个修改 : Alt+(KeyPad) +

转到下一个链接 : Shift+F9, Ctrl+Shift+L

回到前一个修改 : Alt+(KeyPad) -

跳到连接(就是语法串口列表的地方) : Ctrl+L

跳到匹配 : Alt+]

下一页 : PgDn, (KeyPad) PgDn

上一页 : PgUp, (KeyPad) PgUp

向上滚动半屏 : Ctrl+PgDn, Ctrl+(KeyPad) PgDn, (KeyPad) *

向下滚动半屏 : Ctrl+PgUp, Ctrl+(KeyPad) PgUp, (KeyPad) /

左滚 : Alt+Left

向上滚动一行 : Alt+Down

向下滚动一行 : Alt+Up

右滚 : Alt+Right

选择一块 : Ctrl+-

选择当前位置的左边一个字符 : Shift+Left

选择当前位置右边一个字符 : Shift+Right

选择一行 : Shift+F6

从当前行其开始向下选择 : Shift+Down

从当前行其开始向上选择 : Shift+Up

选择上页 : Shift+PgDn, Shift+(KeyPad) PgDn

选择下页 : Shift+PgUp, Shift+(KeyPad) PgUp

选择句子(直到遇到一个 . 为止) : Shift+F7, Ctrl+.

从当前位置选择到文件结束 : Ctrl+Shift+End

从当前位置选择到行结束 : Shift+End

从当前位置选择到行的开始 : Shift+Home

从当前位置选择到文件顶部 : Ctrl+Shift+Home

选择一个单词 : Shift+F5

选择左边单词 : Ctrl+Shift+Left

选择右边单词 : Ctrl+Shift+Right

到文件顶部 : Ctrl+Home, Ctrl+(KeyPad) Home

到窗口顶部 : (KeyPad) Home

到单词左边(也就是到一个单词的开始) : Ctrl+Left

到单词右边(到该单词的结束) : Ctrl+Right

排列语法窗口(有三种排列方式分别按1,2,3次) : Alt+F7

移除文件 : Alt+Shift+R

同步文件 : Alt+Shift+S

增量搜索(当用Ctrl + F 搜索,然后按F12就会转到下一个匹配) : F12

替换文件 : Ctrl+Shift+H

向后搜索 : F3

在多个文件中搜索 : Ctrl+Shift+F

向前搜索 : F4

搜索选择的(比如选择了一个单词,shift+F4将搜索下一个) : Shift+F4

搜索 : Ctrl+F

浏览本地语法(弹出该文件语法列表窗口,如果你光标放到一个变量/函数等,那么列出本文件该变量/函数等的信息) : F8

浏览工程语法 : F7, Alt+G

跳到基本类型(即跳到原型) : Alt+0

跳到定义出(也就是声明) : Ctrl+=, Ctrl+L Click (select), Ctrl+Double L Click

检查引用 : Ctrl+/

语法信息(弹出该语法的信息) : Alt+/, Ctrl+R Click (select)

高亮当前单词 : Shift+F8

语法窗口(隐藏/显示语法窗口) : Alt+F8

关闭窗口 : Alt+F6, Ctrl+F4

最后一个窗口 : Ctrl+Tab, Ctrl+Shift+Tab

本帖附件

点击下载

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

linux下的线程与进程

发表于 2019-12-06

两种线程设计模型

  • 核心级线程设计模型:

    由操作系统内核实现, 特点是: 速度快 windows系统采用的是这种设计模型

    可以比喻为用自己的大脑控制自己十根手指头

  • 用户级线程设计模型:

    操作系统核外实现的线程模式, 特点是: 线程调度在核外 速度不如核内 Linux系统采用的是这种

​ 可以比喻为自己的十根手指头需要借助外力才能动

Linux系统下有真正意义的多线程么?

由上面Linux采用的线程设计模型可知,Linux系统并没有真正意义上的多线程

因此, Linux系统里处理多线程不如Windows强悍

Linux系统的两个线程库

  • LinuxThreads线程库
  • RedHat的NPTL

这两个线程库实际上并没有完全按照线程模式进行实现

进程的生命周期

进程的创建及回收

在Android中, ActivityThead的创建预示着进程的创建

进程的级别(由高到低)

  • 前台进程: 优先级最高, 正处于Activity Resume()状态, 杀死前台进程需要用户响应
  • 可见进程
  • 服务进程
  • 后台进程
  • 空进程: 无组件启动,做进程缓存使用, 恢复速度快

当一个应用启动的时候, 它的进程级别不是保持固定的, Android内部通过Handler进行轮询检测当前进程的状态,ActivityThread掌控的Activity 的生命周期, 如果栈中无Activity存在, 但是有Service存在的情况下, 此时的进程级别就会从前台进程降为服务进程

如果想要查询当前进程的级别, 可以通过ActivityManager .RuningAppProcessInfo进行查询,内部有对应的变量和方法

进程级别的记忆方法

前见服后空

谐音: 权健服后空 (懂?)

本帖附件

点击下载

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

从源码角度分析Activity与Window及View之间的关系

发表于 2019-12-06

我们都知道布局文件的加载是在Activity的onCreate()方法中,使用setContentView进行加载

这个方法是个重载方法

它们无一例外都是使用的getWindow()进行加载

那么window是在什么时候创建的呢?

我们知道Acitivity的生命周期是从onCreate开始的, 其实在它之前还有一个方法已经被执行了, 那就是attach方法

PolicyManager创建了一个新的Window对象

接下来

我们进入到PolicyManager类中

IPolicy是个接口

我们需要找到它的实现类,通过寻找发现Pollicy.java实现了IPolicy接口

实现代码如下:

直接创建了一个PhoneWindow对象,

那么意味着 每创建一个Activity都会创建一个PhoneWindow对象

那么PhoneWindow与Window到底是什么关系呢?

PhoneWindow是Window的子类

那么到此为止, 我们知道了其实Activity中的setContentView实际上是PhoneWindow在处理

我们找到PhoneWindow.java能发现其对应的方法

这里面的是三个重载方法咱们一个一个来分析

首先第一个

咱们平常开发时在xml中写的布局并不是根结点, 而是contentParent的子view

总结

它们之间的关系可以大致理解为:

Activity: 相当于一栋房子

Window: 相当于房子里的一扇窗户

View: 相当于窗户上的一朵窗花

它们三个的创建顺序为:

Activity—>Window—>View

具体时序图如下:

本帖附件

点击下载

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Android应用流程简介

发表于 2019-12-06

我们知道Android系统的启动是从Init.c开始

那么Android应用的启动过程是从哪里开始呢?

下面是具体的时序图参考:

从Launcher.java开始 由于虚拟机的特性每个应用都会独占一个进程,ActivityThead是应用启动的标志

本帖附件

点击下载

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

1…444546…48

乱码三千

android程序员一枚,擅长java,kotlin,python,金融投资,欢迎交流~

479 日志
139 标签
RSS
© 2024 乱码三千
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%