0%

Android-小常识普及

Home键退出和返回键退出的区别

Home键退出,程序保留状态为后台进程;
而返回键退出,程序保留状态为空进程,空进程更容易被系统回收。Home键其实主要用于进程间切换,返回键则是真正的退出程序。

从理论上来讲,无论是哪种情况,在没有任何后台工作线程(即便应用处于后台,工作线程仍然可以执行)的前提下,被置于后台的进程都只是保留他们的运行状态,并不会占用CPU资源,所以也不耗电。只有音乐播放软件之类的应用需要在后台运行Service,而Service是需要占用CPU时间的,此时才会耗电。所以说没有带后台服务的应用是不耗电也不占用CPU时间的,没必要关闭,这种设计本身就是Android的优势之一,可以让应用下次启动时更快。然而现实是,很多应用多多少少都会有一些后台工作线程,这可能是开发人员经验不足导致(比如线程未关闭或者循环发送的Handler消息未停止),也可能是为了需求而有意为之,导致整个Android应用的生态环境并不是一片干净。

请问“强制进行GPU渲染”和“停用HW叠加层”有什么区别?

这两个还是有区别的,GPU渲染就是hwa(hard ware acceleration硬件加速)的一种,其存在的意义就是为了分担CPU的负担,其原理是通过GPU对软件图形图像的处理来减轻CPU的负担,从而使应用软件能够以更快的速度被处理,以达到提速的目的,简单来说就是一般情况下软件的2D图形图像处理是交给CPU的,3D图形图像处理才是交给GPU的,而开启这个则是强制用GPU进行2D图形图像处理从而降低CPU处理器的负担,这个主要是2D界面的图形也交给GPU处理从而更流畅点。

而HW叠加层,HW和叠加层应该分开理解,HW指的是硬件加速,而这个硬件指的是GPU处理器,而不是其它硬件或处理器硬件,至于为什么可以去参考一下硬解的相关资料,然后叠加层指的是使用CPU进行辅助运算,而不只是让GPU来进行全部的渲染工作,这个也是停用HW叠加层默认关闭的原因,汇总起来就是停用GPU上的CPU辅助计算,停用HW叠加层默认关闭就是说明HW叠加层默认是开启的,为什么会默认开启?这个就是因为就算开启了强制进行GPU渲染但并不是所有的图形处理交给了GPU,像游戏里面的一些画面、视频中的硬解(手机上不开启的话硬解是靠SQV的)就不是全部交给GPU的,而开启停用HW叠加层就是关闭HW叠加层,关闭HW叠加层就意味着图形处理任务就全部交给了GPU,这个主要是可以让游戏、视频之类一些原本不是GPU渲染的画面也强制使用GPU进行处理(像一些配置的视频显示不支持硬解,然后开启强制进行GPU渲染后就可以硬解了,因为强制使用GPU而不是手机上的另一个SQV硬解了,但因视频配置、屏幕刷新率之类的还是会有卡顿的情况,开启HW叠加层可以让部分高帧率的视频达到流畅的效果,但也受限于视频配置、屏幕刷新率等其它因素)。强制进行GPU渲染和停用HW叠加层不建议长时间开启,虽然开启这些可以让性能更好,但是也会损耗不少东西。

Android的同步屏障消息是指什么

Android 中的 Handler 用于在不同的线程之间传递消息和任务。一个常见的用例是在后台线程中执行耗时操作,然后将结果传递给主线程以更新用户界面。同步屏障消息是一种特殊的消息类型,可以用于控制消息处理的顺序。

原理

当你向 Handler 发送同步屏障消息时,它会将该消息插入到消息队列中,并等待消息队列中的所有先前的消息都被处理完毕,然后再处理同步屏障消息。这可以用于确保某些操作在其他消息处理之前执行,从而实现同步。

示例

可以在主线程上使用同步屏障消息来确保在更新用户界面之前执行某些初始化操作。这样,你可以防止在初始化操作尚未完成之前更新界面,从而提高了代码的可靠性和一致性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Handler handler = new Handler();

// 发送一个普通消息
handler.post(new Runnable() {
@Override
public void run() {
// 这是一个普通消息
// 可能会执行在其他消息之前
}
});

// 发送一个同步屏障消息
handler.postAtFrontOfQueue(new Runnable() {
@Override
public void run() {
// 这是一个同步屏障消息
// 会等待前面的消息都处理完才执行
}
});

同步屏障消息

系统一些高优先级的操作会使用到同步屏障消息

Android 是禁止App往MessageQueue插入同步屏障消息的,代码会报错

Looper.getMainLooper.postSyncBarrier

demo

例如View在绘制的时候

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
->ViewRootImpl

@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//插入同步屏障消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}

void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//移除同步屏障消息
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}

为了保证View的绘制过程不被主线程其它任务影响,View在绘制之前会先往MessageQueue插入同步屏障消息,然后再注册Vsync信号监听,Choreographer$FrameDisplayEventReceiver就是用来接收vsync信号回调的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
...
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
...
//
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
//1、发送异步消息
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}

@Override
public void run() {
// 2、doFrame优先执行
doFrame(mTimestampNanos, mFrame);
}
}

收到Vsync信号回调,注释1会往主线程MessageQueue post一个异步消息,保证注释2的doFrame优先执行。
doFrame才是View真正开始绘制的地方,会调用ViewRootImpl的doTraversal、performTraversals,
而performTraversals里面会调用我们熟悉的View的onMeasure、onLayout、onDraw。
这里还可以延伸到vsync信号原理,以及为什么要等vsync信号回调才开始View的绘制流程、掉帧的原理、屏幕的双缓冲、三缓冲,由于文章篇幅关系,不是本文的重点,就不一一分析了~

虽然app无法发送同步屏障消息,但是使用异步消息是允许的

异步消息

首先,SDK中限制了App不能post异步消息到MessageQueue里去的,相关字段被加了UnsupportedAppUsage注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-> Message

@UnsupportedAppUsage
/*package*/ int flags;

/**
* Returns true if the message is asynchronous, meaning that it is not
* subject to {@link Looper} synchronization barriers.
*
* @return True if the message is asynchronous.
*
* @see #setAsynchronous(boolean)
*/
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}

不过呢,高版本的Handler的构造方法可以通过传async=true,来使用异步消息

1
2
public Handler(@Nullable Callback callback, boolean async) {}

然后在Handler发送消息的时候,都会走到 enqueueMessage 方法,如下代码块所示,每个消息都带了异步属性,有优先处理权

1
2
3
4
5
6
7
8
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {
...
//如果mAsynchronous为true,就都设置为异步消息
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

对于低版本SDK,想要使用异步消息,可以通过反射调用Handler(@Nullable Callback callback, boolean async),参考androidx内部的一段代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
->androidx.arch.core.executor.DefaultTaskExecutor

private static Handler createAsync(@NonNull Looper looper) {
if (Build.VERSION.SDK_INT >= 28) {
return Handler.createAsync(looper);
}
if (Build.VERSION.SDK_INT >= 16) {
try {
return Handler.class.getDeclaredConstructor(Looper.class, Handler.Callback.class,
boolean.class)
.newInstance(looper, null, true);
} catch (IllegalAccessException ignored) {
} catch (InstantiationException ignored) {
} catch (NoSuchMethodException ignored) {
} catch (InvocationTargetException e) {
return new Handler(looper);
}
}
return new Handler(looper);
}