Mr.HeXiang Blog

记录学习,学习记录


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 公益404

Context和Application的理解

贺祥 | 发表于 2020-05-09 | 分类于 Android

####

Context的作用

context是用来提供上下文环境,可以通过它调用系统资源,调用系统服务,如context.startActivity

Context有几种

Application,Activity,Service

Activity继承自ContextThemeWrapper,

Application和Serivce继承自ContextWrapper类。

ContextWrapper内部有一个mBase属性,该类型为ContextImpl类型,它才是真正执行任务的对象,此处用了一个静态代理的方式

BroadcastReceiver是一个抽象类,不是Context类型,但是在onReceive方法中会传入一个context对象,当是动态广播的时候,这个传入的context是注册时的那个,当是静态广播的时候,传入的是以Application对象为mBase的ContextWrapper类型对象

ContentProvider也不是一个Context类型,但是它内部有一个context属性,该context属性是Application对象,需要提到的是,Application的attachBaseContext方法在contentProvider的onCreate方法之前执行 ,同时,Application的onCreate方法在contentProvider的onCreate方法之后执行

Context的区别

需要处理ui的继承ContextThemeWrapper 如Activity的context

不需要的继承ContextWrapper 如service和application的context

Context的创建过程

Application,Activity,Service的创建过程都是先调用构造方法,再调用attach方法,再调用onCreate方法,所以不能在构造方法中获取context对象

Activity Context 创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;

ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
 activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
r.assistToken, r.shareableActivityToken);
   if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
return activity;
}

Application Context 创建

1
2
3
4
5
   #ActivityThread.java
private void handleBindApplication(AppBindData data) {
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
mInstrumentation.callApplicationOnCreate(app);
}

Service Context 创建

1
2
3
4
5
6
7
8
9
10
    #ActivityThread.java
private void handleCreateService(CreateServiceData data) {
       Application app = packageInfo.makeApplicationInner(false, mInstrumentation);
ContextImpl context = ContextImpl.getImpl(service
.createServiceBaseContext(this, packageInfo));

service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
}

Android应用里面有多少个Context?

Activity个数+Application个数+service个数 ,多进程会有多个application

android系统启动过程

贺祥 | 发表于 2020-05-06 | 分类于 Android

####

Android系统启动过程
  1. Bootloader加载

  2. linux内核加载,文件系统挂载

  3. init进程启动,解析init.rc配置文件

  4. 启动serviceManager SurfaceFling MediaServer binder 驱动

    启动binder机制:

    5.1 打开binder驱动,

    5.2 映射虚拟内存,分配内存缓冲区

    5.3 注册binder线程

    5.4 进入binder loop

  5. Zygote进程启动,根据init.rc配置文件启动zygote

  6. 启动Android虚拟机,注册JNI函数

  7. 预加载系统资源,提高app启动速度

  8. 启动Systemserver

  9. 注册socket,开启无限循环,通过监听socket,等待AMS命令fork进程

  10. 启动系统服务,AMS,PKMS,WMS等分批启动,把这些系统服务注册到ServerManager,方便快速获得binder对象,

  11. AMS启动后SystemReady回调时候启动launcher

系统服务器启动的互相依赖怎么解决
  • 分批启动

  • 分阶段启动

    1
    2
    3
    4
    5
    6
          //启动引导服务
    startBootstrapServices();
    //启动核心服务
    startCoreServices();
    //启动其他服务
    startOtherServices();
ServcieManger启动和工作原理
  • 调用函数 binder_open 打开设备文件 /dev/binder 以及将它映射到本进程的地址空间.
  • 调用函数 binder_become_context_manager 将自己注册为所有服务的大管家.
  • 调用函数 binder_loop 来循环等待和处理 Client 进程的通信请求.

Activity线程消息传递机制

贺祥 | 发表于 2020-05-01 | 分类于 Android

####

Activity线程消息传递机制
  1. 消息循环

    Looper.loop,主线程默认开启了loop,ActivityThread.main 入口开启了消息循环

    nativePollOnce 阻塞在那监听是否有别的线程往当前消息队列发送消息,如果有消息或者超时会唤醒阻塞

  2. 消息发送

    handle.sendMessage发送消息

  3. 消息处理

    消息分发:msg.target 就是一个handle,handle.dispatchMessage,handlerMessage

    如果消息没有自带callback就会判断是否有全局mCallback,最后才会调用handleMessage

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /**
    95 * Handle system messages here.
    96 */
    97 public void dispatchMessage(Message msg) {
    98 if (msg.callback != null) {
    99 handleCallback(msg);
    100 } else {
    101 if (mCallback != null) {
    102 if (mCallback.handleMessage(msg)) {
    103 return;
    104 }
    105 }
    106 handleMessage(msg);
    107 }
    108 }

延时消息:消息插入会按执行时间排序(计算触发时间),postDelay就是依赖这个,当时间大于delay时间就会取出执行,延时精度不准(1,计算的触发超时时间,2.卡顿)

屏障消息,ui绘制绿色通道,收到屏障消息阻塞普通消息,执行异步消息

idlehandle 空闲消息处理

图片加载消耗的内存

贺祥 | 发表于 2020-03-07 | 分类于 Android

图片加载消耗的内存

测试实验的手机及配置

一张图片700*700px 88.2k 测试手机小米k20 分辨率 1080*2340px 测试手机密度440dp

一、通过image src方式加载图片
1
2
3
4
<ImageView
android:src="@mipmap/test_memory"
android:layout_width="300px"
android:layout_height="300px"

1. 放入mipmap-xxxhdpi
无论设置ImageView高宽多少内存分配都是481*481*4 =925476b
这个481 是怎么来的呢
440/640 * 700 = 481.25 向上取整就是缩放转化后的高宽
Alt text

2. 放入mipmap-xxhdpi
无论设置ImageView高宽多少内存分配都是642*642*4 =1648656b
手机精确密度440/ xxhdpi 对应的480dpi*原图高宽700 px = 641.6666 向上取整
Alt text

二、当通过Glide裁剪图片大小的时候

无论图片放在哪个目录mdpi,hdpi,xhdpi,xxhdpi, xxxhdpi 还是网络加载
他的内存都是一样
300*300* 4 = 360032

1
2
3
4
Glide.with(this)
.load(R.mipmap.test_memory)
.override(300, 300)
.into(image)

1
2
3
4
Glide.with(this)
.load("https://mgamevoice2.bs2dl.yy.com/344892b5daac49a9870826beee6b1da5.jpg")
.override(300, 300)
.into(image)

Alt text

总结:

1. 当同imageView src 加载图片的情况
无论设置ImageView高宽多少,对内存分配无效。
bitmap分配的内存 =: 原图缩放的高*原图缩放的宽*每个像素大小
高宽缩放比例 :手机密度/图片所在目录对应dpi

ImageView 加载res图片过程
ResourcesImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
@Nullable
private Drawable decodeImageDrawable(@NonNull AssetInputStream ais,
...
try {
return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
});
} catch (IOException ioe) {
// This is okay. This may be something that ImageDecoder does not
// support, like SVG.
return null;
}
}

设置高宽大小密度用于创建bitmap
ImageDecoder.java

1
2
3
4
5
6
7
8
9
// This method may modify the decoder so it must be called prior to performing the decode
private int computeDensity(@NonNull Source src) {
...
float scale = (float) dstDensity / srcDensity;
int scaledWidth = Math.max((int) (mWidth * scale + 0.5f), 1);
int scaledHeight = Math.max((int) (mHeight * scale + 0.5f), 1);
this.setTargetSize(scaledWidth, scaledHeight);
return dstDensity;
}

2. 当通过Glide裁剪本地图片大小的情况
无论图片放在哪个目录mdpi,hdpi,xhdpi,xxhdpi, xxxhdpi 还是网络加载
他的内存都是一样 : 裁剪高* 裁剪宽 * 每个像素大小

以上的所说内存分配,bitmap.getAllocationByteCount()可以获得 ,跟getByteCount 不一样

测试的图片
1
2
测试图片地址
https://mgamevoice2.bs2dl.yy.com/344892b5daac49a9870826beee6b1da5.jpg
不同目录对应的dpi

ldpi(低)~120dpi density=0.75
mdpi(中)~160dpi density=1
hdpi(高)~240dpi density=1.5
xhdpi(超高)~320dpi density=2
xxhdpi(超超高)~480dpi density=3
xxxhdpi(超超超高)~640dpi density=4

InstantRun 及Tinker原理

贺祥 | 发表于 2019-03-09 | 分类于 Android
Instant Run

Instant Run 热部署,提升app安装部署的速度
首先要在android studio 设置中开启 Instant Run
Preferences -> Build,Execution,Deployment -> Instant Run

  1. tinker 会和instant Run 起冲突
  2. AndFix的实现与Instant Run的热插拔有点类似
    Alt text
加载补丁dex

Tinker采用的是下发差分包,然后在手机端合成全量的dex文件进行加载

工作流程
hook Classloader 在DexPathList 中dexElements数组中
插入dex,并且插入到最前面

加载补丁资源

Tinker的资源更新采用的InstantRun的资源补丁方式,全量替换资源
Tinker的这套方案虽然也采用全量的替换,但是在下发patch中依然采用差量资源的方式获取差分包,下发到手机后再合成全量的资源文件,有效的控制了补丁文件的大小。

  1. 修改变量属性,修改资源属性不需要重启Activity,修改方法内不需要重启Activity
  2. 新增类或者,新增资源属性需要重启Activity
  3. 新增属性需要重启Activity

简单来说,在编译时通过新旧两个Dex生成差异path.dex。在运行时,将差异patch.dex重新跟原始安装包的旧Dex还原为新的Dex。这个过程可能比较耗费时间与内存,所以我们是单独放在一个后台进程:patch中。为了补丁包尽量的小,微信自研了DexDiff算法,它深度利用Dex的格式来减少差异的大小。它的粒度是Dex格式的每一项,可以充分利用原本Dex的信息,而BsDiff的粒度是文件,AndFix/QZone的粒度为class。

热拔插

热拔插:代码改变被应用、投射到APP上,不需要重启应用,不需要重建当前activity。
场景:适用于多数的简单改变(包括一些方法实现的修改,或者变量值修改)

温拔插

温拔插:activity需要被重启才能看到所需更改。
场景:典型的情况是代码修改涉及到了资源文件,即resources。

冷拔插

冷拔插:app需要被重启(但是仍然不需要重新安装)
场景:任何涉及结构性变化的,比如:修改了继承规则、修改了方法签名等。

参考文章

Dexposed github (https://github.com/alibaba/dexposed)
AndFix github (https://github.com/alibaba/AndFix)
Nuwa github (https://github.com/jasonross/Nuwa)
QZone实现原理解析 (https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=400118620&idx=1&sn=b4fdd5055731290eef12ad0d17f39d4a)
Instant Run英文原文 (https://medium.com/google-developers/instant-run-how-does-it-work-294a1633367f#.c088qhdxu)
Instant Run工作原理及用法中文翻译稿 (http://www.jianshu.com/p/2e23ba9ff14b)
Buck exopackage 介绍 (https://buckbuild.com/article/exopackage.html)
各大热补丁方案分析和比较 (http://blog.zhaiyifan.cn/2015/11/20/HotPatchCompare/)

微信Android热补丁实践演进之路
(https://github.com/WeMobileDev/article/blob/master/%E5%BE%AE%E4%BF%A1Android%E7%83%AD%E8%A1%A5%E4%B8%81%E5%AE%9E%E8%B7%B5%E6%BC%94%E8%BF%9B%E4%B9%8B%E8%B7%AF.md)

快速获取native方法的签名

贺祥 | 发表于 2019-03-09 | 分类于 Android
快速获取native方法的签名

找到build下面对应的.class文件
在当前文件夹下打开终端,
输入javap -s class文件名,就会打印出签名信息

1
javap -s NativeNCk

Alt text

Aop切面编程APT方式

贺祥 | 发表于 2019-03-08 | 分类于 Android
Aop切面编程APT方式

APT (Annotation Processing Tool )即注解处理器
简单来说就是在编译期,通过注解生成 .java 文件。使用的 Annotation 类型是 SOURCE。

代表框架:DataBinding、Dagger2、ButterKnife、EventBus3、DBFlow、AndroidAnnotation

利用javapoet开源库编译的时候生成代码文件

是用于生成Java源文件的Java API。
https://github.com/square/javapoet

Gradle引用库

1
compile 'com.squareup:javapoet:1.11.1'

使用例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//方法
MethodSpec main = MethodSpec.methodBuilder("main")//定义方面名
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)//定义修饰符
.returns(void.class)//定义返回类型
.addParameter(String[].class, "args")//添加方法参数
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")//添加方法内容
.build();
//类
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")//构造一个类,类名
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)//定义类的修饰符
.addMethod(main)//添加类的方法,也就是上面生成的MethodSpec对象
.build();
//生成源码文件
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();

javaFile.writeTo(System.out);

android系统启动过程

贺祥 | 发表于 2019-03-07 | 分类于 Android
android系统启动过程

1.bootloader加载
2.linux内核加载,文件系统挂载
3.init进程启动,解析init.rc配置文件
4.根据init.rc配置文件启动zygote进程,启动SystemManager binder驱动
5.启动Systemserver 进程,启动loop消息循环及
6.启动相关服务
7.启动launcher

Activity启动过程

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

Activity启动过程

Activity

1.startActivity
2.startActivityForResult

Instrumentation

3.execStartActivity

ActivityManagerService

4.startActivity
5.startActivityAsUser

ActivityStarter

6.startActivityMayWait

解析出Intent相匹配的Activityinfo
得到启动Activity的Task

7.startActivityLocked

禁止App切换恢复
校验参数

8.startActivityUnchecked
ActivityStackSupervisor
9.resumeFocusedStackTopActivityLocked

ActivityStack

10.resumeTopActivityUncheckedLocked
11.resumeTopActivityInnerLocked

判断是否又resume的Activity有则暂停
唤醒目标ActivityActivity或重启Acitivity

ActivityStackSupervisor

12.startSpecificActivityLocked

没有启动app的,通过zygote创建应用进程startProcessLocked
启动了了进入14步启动realStartActivityLocked
ActivityManagerService

13.startProcessLocked

ActivityStackSupervisor

14.realStartActivityLocked

通过发送事务执行事务 最后执行LaunchActivityItem 和ResumeActivityItem 的execute
最终调用activityThread.handleLaunchActivity, activityThread.handleResumeActivity
handleLaunchActivity 会调用performLaunchActivity
mInstrumentation.newActivity 创建acitivty
activity.attach 创建phonewindow
mInstrumentation.callActivityOnCreate
调用created 调用onCreated 中window.setContentView生成dectoView

handleResumeActivity
WindowManagerGlobal把dectoView添加到window addView 会生ViewRootImpl
scheduleTraversals performTraversals performMeasure performLayout performDraw view绘画工作机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Create activity launch transaction.
final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
r.appToken);
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
...
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);

// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);

ActivityThread.this.scheduleTransaction

ActivityThread 的父类

1
2
3
4
5
6
7
8
public abstract class ClientTransactionHandler {
// Schedule phase related logic and handlers.

/** Prepare and schedule transaction for execution. */
void scheduleTransaction(ClientTransaction transaction) {
transaction.preExecute(this);
sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}

ActivityThread H 中

1
2
3
4
5
6
7
8
9
10
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);//最后执行LaunchActivityItem 和PauseActivityItem 等里面的execute方法
if (isSystem()) {
// Client transactions inside system process are recycled on the client side
// instead of ClientLifecycleManager to avoid being cleared before this
// message is handled.
transaction.recycle();
}
// TODO(lifecycler): Recycle locally scheduled transactions.

LaunchActivityItem 中

1
2
3
4
5
6
7
8
9
10
11
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mIsForward,
mProfilerInfo, client);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}

Alt text

Alt text

Apk瘦身优化

贺祥 | 发表于 2019-02-09 | 分类于 Android
Apk瘦身优化

资源混淆 minifyEnabled true
开启资源压缩 shrinkResources true

移除无用代码、功能;
移除无用的库、避免功能雷同的库;
缩减方法数;

重复资源优化
移除无用的资源文件;
Drawable目录只保留一份资源;
对图片进行压缩;
PNG转换JPG;
使用矢量图;
使用WebP;
资源在线化;

3 So瘦身

保留特定架构的so库,比如armeabi几户支持所有手机,像微信一样

123…13
贺祥

贺祥

记学学记

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