Mr.HeXiang Blog

记录学习,学习记录


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 公益404

SystemServer进程的创建及Launcher启动

贺祥 | 发表于 2019-02-09 | 分类于 android

SystemServer进程的创建及Launcher启动

1.SystemServer进程的创建

ZygoteInit.main方法中里fork出个SystemServer进程

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
/* Hardcoded command line to start the system server */
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;

int pid;

try {
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

boolean profileSystemServer = SystemProperties.getBoolean(
"dalvik.vm.profilesystemserver", false);
if (profileSystemServer) {
parsedArgs.runtimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
}

/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.runtimeFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}

2.SystemServer.main方法

1
2
3
public static void main(String[] args) {
new SystemServer().run();
}

run方法

2.1. 创建loop对象
1
2
// Prepare the main looper thread (this thread).
Looper.prepareMainLooper();
2.2 创建了系统上下文
1
2
// Initialize the system context.
createSystemContext();

创建AcitivityThread
这里创建的ActivityThread 和ActivityThread.main()中创建的不是同一个

1
2
3
4
5
6
7
8
9
10
private void createSystemContext() {
// systemMain()中创建activityThread 调用activityThread .attach(true, 0); 会创建Application,Instrumentation ,并调用applicition.onCreate()
ActivityThread activityThread = ActivityThread.systemMain();
//创建context,创建LoadedApk
mSystemContext = activityThread.getSystemContext();
mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);

final Context systemUiContext = activityThread.getSystemUiContext();
systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
}

2.3启动SystemServiceManager
1
2
3
4
5
6
7
// Create the system service manager.
mSystemServiceManager = new SystemServiceManager(mSystemContext);
mSystemServiceManager.setStartInfo(mRuntimeRestart,
mRuntimeStartElapsedTime, mRuntimeStartUptime);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
// Prepare the thread pool for init tasks that can be parallelized
SystemServerInitThreadPool.get();
2.4 启动相关服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Start services.
try {
traceBeginAndSlog("StartServices");
startBootstrapServices();
startCoreServices();
startOtherServices();
SystemServerInitThreadPool.shutdown();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
traceEnd();
}

2.4.1startBootstrapServices()启动了ActivityManagerService ,PowerManagerService,PackageManagerService …等

2.4.2
startCoreServices()启动了BatteryService,UsageStatsService,WebViewUpdateService

2.4.3 startOtherServices()启动了AlarmManagerService,BluetoothService,NotificationManagerService …等

mActivityManagerService.systemReady 启动launcher

1
2
3
4
5
6
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
// where third party code can really run (but before it has actually
// started launching the initial applications), for us to complete our
// initialization.
mActivityManagerService.systemReady(...)

systemReady 方法中调用 mActivityManagerService.startHomeActivityLocked启动launcher

1
2

startHomeActivityLocked(currentUserId, "systemReady");

下面代码1处调用ActivityStarter中的 startActivityMayWait 或者startActivity ,又开始走Activity启动流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
mSupervisor.moveHomeStackTaskToTop(reason);
//代码1
mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
.setOutActivity(tmpOutRecord)
.setCallingUid(0)
.setActivityInfo(aInfo)
.execute();
mLastHomeActivityStartRecord = tmpOutRecord[0];
if (mSupervisor.inResumeTopActivity) {
// If we are in resume section already, home activity will be initialized, but not
// resumed (to avoid recursive resume) and will stay that way until something pokes it
// again. We need to schedule another resume.
mSupervisor.scheduleResumeTopActivities();
}
}

2.5进入消息循环
1
2
// Loop forever.
Looper.loop();

ThreadPoolExecutor的使用和理解

贺祥 | 发表于 2019-02-09 | 分类于 Android
ThreadPoolExecutor的使用和理解

corePoolSize,//核心线程数,当线程数超过workQueue阻塞队列数,并且核心数线程已满,会创建新的线程直到达到最大线程
maximumPoolSize,//最大线程数,
keepAliveTime,//空闲线程等待时间,时间到结束释放
unit,//等待时间单位
workQueue, //线程阻塞队列
threadFactory,//线程生产工厂
handler);//当线程数大于maximumPoolSize+workQueue的时候会执行RejectedExecutionHandler

ThreadFactory是一个线程工厂,负责生产线程的

例子:

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
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadPollTest {
static ThreadPoolExecutor executor;
public static void main(String[] args) throws InterruptedException, IOException {
int corePoolSize = 2;
int maximumPoolSize = 4;
long keepAliveTime = 60;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(10);
ThreadFactory threadFactory = new NameTreadFactory();
RejectedExecutionHandler handler = new MyIgnorePolicy();
executor = new ThreadPoolExecutor(
corePoolSize,//核心线程数,当线程数超过workQueue阻塞队列数,并且核心数线程已满,会创建新的线程直到达到最大线程
maximumPoolSize,//最大线程数,
keepAliveTime,//空闲线程等待时间,时间到结束释放
unit,//等待时间单位
workQueue, //线程阻塞队列
threadFactory,//线程生产工厂
handler);//当线程数大于maximumPoolSize+workQueue的时候会执行RejectedExecutionHandler
executor.prestartAllCoreThreads(); // 预启动所有核心线程

for (int i = 1; i <= 16; i++) {
MyTask task = new MyTask(String.valueOf(i));
executor.execute(task);
}

}
//线程工厂
static class NameTreadFactory implements ThreadFactory {

private final AtomicInteger mThreadNum = new AtomicInteger(1);

@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());
System.out.println(t.getName() + " has been created");
return t;
}
}
//线程数大于maximumPoolSize+workQueue的时候会执行RejectedExecutionHandler
public static class MyIgnorePolicy implements RejectedExecutionHandler {

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
doLog(r, e);
}

private void doLog(Runnable r, ThreadPoolExecutor e) {
System.err.println( r.toString() + " rejected");
}
}
//例子执行任务
static class MyTask implements Runnable {
private String name;

public MyTask(String name) {
this.name = name;
}

@Override
public void run() {
try {
System.out.println(this.toString() + " is running!activityCount:"+executor.getActiveCount()
+",getCompletedTaskCount:"+executor.getCompletedTaskCount()
+",getTaskCount:"+executor.getTaskCount()
+",getQueue().size:"+executor.getQueue().size()
);
Thread.sleep(3000); //让任务执行慢点
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public String getName() {
return name;
}

@Override
public String toString() {
return "MyTask [name=" + name + "]";
}
}
}

invalidate和postInvalidate及requestLayout方法

贺祥 | 发表于 2019-02-09 | 分类于 Android
invalidate和postInvalidate及requestLayout方法

invalidate() postInvalidate()
共同点:都是调用onDraw()方法,然后去达到重绘view的目的

区别:invalidate()用于主线程,postInvalidate()用于子线程

requestLayout()

也可以达到重绘view的目的,但是与前两者不同,它会先调用onMearsure() onLayout() ,再调用ondraw()方法。重新进行一次测量,布局,绘制的过程

二分查找

贺祥 | 发表于 2019-02-09 | 分类于 Android
二分查找

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列

算法要求
1.必须采用顺序存储结构。
2.必须按关键字大小有序排列。

二分查找只适用顺序存储结构。为保持表的有序性,在顺序结构里插入和删除都必须移动大量的结点。因此,二分查找特别适用于那种一很少改动、而又经常需要查找的线性表。

循环实现

java实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static int binarySearch(Integer[] srcArray, int des) {
//定义初始最小、最大索引
int low = 0;
int high = srcArray.length - 1;
//确保不会出现重复查找,越界
while (low <= high) {
//计算出中间索引值
int middle = (high + low)>>>1 ;//防止溢出
if (des == srcArray[middle]) {
return middle;
//判断下限
} else if (des < srcArray[middle]) {
high = middle - 1;
//判断上限
} else {
low = middle + 1;
}
}
//若没有,则返回-1
return -1;
}

为什么子线程弹Toast报错及为什么Handle需要Loop对象

贺祥 | 发表于 2019-02-09 | 分类于 Android
为什么子线程弹Toast报错及为什么Handle需要Loop对象

弹出错误:java.lang.RuntimeException: Can’t toast on a thread that has not called Looper.prepare()

1
Toast.makeText(et_input.getContext(),"测试",Toast.LENGTH_LONG).show();;

1.Toast.makeTest调用构造函数会创建个TN对象,这个对象需要Loop,Loop为空,并且当前线程的loop也是空的就会抛错

1
2
3
public Toast(@NonNull Context context, @Nullable Looper looper) {
mContext = context;
mTN = new TN(context.getPackageName(), looper);
1
2
3
4
5
6
7
8
9
10
TN(String packageName, @Nullable Looper looper) {
...
if (looper == null) {
// Use Looper.myLooper() if looper is not specified.
looper = Looper.myLooper();
if (looper == null) {
throw new RuntimeException(
"Can't toast on a thread that has not called Looper.prepare()");
}
}

2.提示也很明细,从源码看也可以看到是少了loop对象
所以只需要在生成Toast对象之前,创建loop对象Looper.prepare(),并且最后开启循环 Looper.loop()

注意必须开启循环Loop.loop(),否则消息无法弹出

1
2
3
Looper.prepare();
Toast.makeText(et_input.getContext(),"测试",Toast.LENGTH_LONG).show();
Looper.loop();

3.为什么一定要Loop ,是因为源码中有用到Handler ,handler 必须要有个loop对象的

而且handler会关联Loop当中的mQueue对象,用来发消息,比如sendMessageDelayed 最终会通过mQueue对象发送消息,loop对象收到消息,会回调mQueue对象关联的loop对象的handleMessage(Message msg)的接口,完成消息通信.

1
2
3
4
5
6
7
8
9
10
11
12
13
public Handler(Callback callback, boolean async) {
...

mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

内存优化

贺祥 | 发表于 2019-02-09 | 分类于 Android

####内存优化

一. 避免内存泄漏及优化方法

单例导致内存泄露
单例的静态特性使得它的生命周期同应用的生命周期一样长,如果一个对象已经没有用处了,但是单例还持有它的引用,那么在整个应用程序的生命周期它都不能正常被回收,从而导致内存泄露。

内存泄漏场景
单例模式引用了activity,activity退出后,有于单例引用而无法释放

解决方法
context参数改为全局的上下文

静态变量导致内存泄露

静态变量存储在方法区,它的生命周期从类加载开始,到整个进程结束。一旦静态变量初始化后,它所持有的引用只有等到进程结束才会释放。

内存泄漏场景
activity中定静态变量,activity退出后,有于静态变量引用而不能释放

非静态内部类导致内存泄露
非静态内部类(包括匿名内部类)默认就会持有外部类的引用,当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄露。

内存泄漏场景
1.典型的场景就是使用Handler

1.使用Thread或者AsyncTask。

解决方法
静态内部类+弱引用的方式
hadler 要清除回调消息

未取消注册或回调导致内存泄露
比如我们在Activity中注册广播,如果在Activity销毁后不取消注册,那么这个刚播会一直存在系统中,同上面所说的非静态内部类一样持有Activity引用,导致内存泄露。因此注册广播后在Activity销毁后一定要取消注册。

Timer和TimerTask导致内存泄露
Timer和TimerTask在Android中通常会被用来做一些计时或循环任务,比如实现无限轮播的ViewPager:

当我们Activity销毁的时,有可能Timer还在继续等待执行TimerTask,它持有Activity的引用不能被回收,因此当我们Activity销毁的时候要立即cancel掉Timer和TimerTask,以避免发生内存泄漏。

集合中的对象未清理造成内存泄露
这个比较好理解,如果一个对象放入到ArrayList、HashMap等集合中,这个集合就会持有该对象的引用。当我们不再需要这个对象时,也并没有将它从集合中移除,这样只要集合还在使用(而此对象已经无用了),这个对象就造成了内存泄露。并且如果集合被静态引用的话,集合里面那些没有用的对象更会造成内存泄露了。所以在使用集合时要及时将不用的对象从集合remove,或者clear集合,以避免内存泄漏。

资源未关闭或释放导致内存泄露
在使用IO、File流或者Sqlite、Cursor等资源时要及时关闭。这些资源在进行读写操作时通常都使用了缓冲,如果及时不关闭,这些缓冲对象就会一直被占用而得不到释放,以致发生内存泄露。因此我们在不需要使用它们的时候就及时关闭,以便缓冲能及时得到释放,从而避免内存泄露。

属性动画造成内存泄露
动画同样是一个耗时任务,比如在Activity中启动了属性动画(ObjectAnimator),但是在销毁的时候,没有调用cancle方法,虽然我们看不到动画了,但是这个动画依然会不断地播放下去,动画引用所在的控件,所在的控件引用Activity,这就造成Activity无法正常释放。因此同样要在Activity销毁的时候cancel掉属性动画,避免发生内存泄漏。

WebView造成内存泄露
关于WebView的内存泄露,因为WebView在加载网页后会长期占用内存而不能被释放,因此我们在Activity销毁后要调用它的destory()方法来销毁它以释放内存。

总结
内存泄露在Android内存优化是一个比较重要的一个方面,很多时候程序中发生了内存泄露我们不一定就能注意到,所有在编码的过程要养成良好的习惯。总结下来只要做到以下这几点就能避免大多数情况的内存泄漏:

构造单例的时候尽量别用Activity的引用;
静态引用时注意应用对象的置空或者少用静态引用;
使用静态内部类+软引用代替非静态内部类;
及时取消广播或者观察者注册;
耗时任务、属性动画在Activity销毁时记得cancel;
文件流、Cursor等资源及时关闭;
Activity销毁时WebView的移除和销毁。

自己写一遍加深印象方便记忆
感谢原文作者
原文链接:https://www.jianshu.com/p/ab4a7e353076

内存分析工具

Android Profiler
leakcanary

二.对象复用

复用系统自带的资源
例如adapter中的ContentView;避免在onDraw里创建对象;对象池设计的应用。

三.数据结构优化

频繁字符串拼接使用stringbuilder,它比+拼接高效;大数据量存储使用ArrayMap,SparseArray替代HaspMap;避
免内存抖动,避免大量分配了一些局部变量。

四.缓存技术

1.使用缓存技术,比如LruCache、DiskLruCache、对象重复并且频繁调用可以考虑对象池
2.对于引用生命周期不一样的对象,可以用软引用或弱引用SoftReferner WeakReferner

SoftReferner 和WeakReferner弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

ReferenceQueue

引用队列,如果保存的是Reference对象本身,如果:Reference引用指向的对象被GC回收,其实Reference已经无效了

leakcanary,每当弱引用所指向的对象被内存回收的时候,我们就可以在queue中找到这个引用。如果在接下来的预期时间之后,我们发现它依然没有出现在ReferenceQueue中,那就可以判定它的内存泄露了。LeakCanary检测内存泄露的核心原理就在这里

GC回收机制算法 - 可达性算法
可达性算法是目前Java虚拟机比较常用的GC回收机制算法。

主要原理是是通过GCRoots对象作为根节点,从根节点开始往下遍历,遍历的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的时候,则说明此对象是不可用的。

四.避免oom

避免方法 上面的内存优化

JVM内存模型

贺祥 | 发表于 2019-02-09 | 分类于 Android
JVM内存结构

Heap内存分配
JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;
JVM最大分配的内存由-Xmx指 定,默认是物理内存的1/4。

1、程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看

做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,

各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变

这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、

线程恢复等基础功能都需要依赖这个计数器来完成。

2、Java 虚拟机栈

与程序计数器一样,Java 虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,

它的生命周期与线程相同。虚拟机栈描述的是Java 方法执行的内存模型:每个方法被执

行的时候都会同时创建一个栈帧(Stack Frame ①)用于存储局部变量表、操作栈、动态

链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在

虚拟机栈中从入栈到出栈的过程。

3、本地方法栈

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其

区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则

是为虚拟机使用到的Native 方法服务。虚拟机规范中对本地方法栈中的方法使用的语

言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至

有的虚拟机(譬如Sun HotSpot 虚拟机)直接就把本地方法栈和虚拟机栈合二为一。

与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError 和OutOfMemoryError

异常。
局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、

float、long、double)

4、Java 堆

对于大多数应用来说,Java 堆(Java Heap)是Java 虚拟机所管理的内存中最大的

一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的

唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存

5、方法区

方法区(Method Area)与Java 堆一样,是各个线程共享的内存区域,它用于存

储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

6、运行时常量池

运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中除了有

类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool

Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放

到方法区的运行时常量池中。

哪些对象可以作为GC Roots(指的是堆内存回收)

虚拟机栈中的引用对象

方法区中类静态属性引用的对象

方法区中常量引用的对象

本地方法栈中JNI引用的对象

各大热补丁方案分析和比较

贺祥 | 发表于 2019-02-09 | 分类于 Android
各大热补丁方案分析和比较

http://blog.zhaiyifan.cn/2015/11/20/HotPatchCompare/

反转链表

贺祥 | 发表于 2019-02-09 | 分类于 Android
反转链表

递归方法

1
2
3
4
5
6
7
8
   public ListNode reverseList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode p = reverseList(head.next);
head.next.next = head;
head.next = null;
return p;
}
}

循环方法

1
2
3
4
5
6
7
8
9
10
public ListNode reverseList(ListNode head) {
ListNode cur = head, pre = null, next = null;
while(cur != null){
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}

原子性可见性有序性

贺祥 | 发表于 2019-02-09 | 分类于 Android
原子性,可见性,有序性
原子性

原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰.volatile并不能保证原子性,synchronized满足原子性。

可见性

可见性是指当一个线程修改了共享变量后,其他线程能够立即得知这个修改。通过之前对synchronzed内存语义进行了分析,当线程获取锁时会从主内存中获取共享变量的最新值,释放锁的时候会将共享变量同步到主内存中。从而,synchronized具有可见性。同样的在volatile分析中,会通过在指令中添加lock指令,以实现内存可见性。因此, volatile具有可见性

有序性

synchronized语义表示锁在同一时刻只能由一个线程进行获取,当锁被占用后,其他线程只能等待。因此,synchronized语义就要求线程在访问读写共享变量时只能“串行”执行,因此synchronized具有有序性。

1
volatile包含禁止指令重排序的语义,其具有有序性

1…345…13
贺祥

贺祥

记学学记

123 日志
10 分类
65 标签
© 2023 贺祥
由 Hexo 强力驱动「Hosted by Coding Pages」
|
主题 — NexT.Pisces v5.1.3