广告位联系
返回顶部
分享到

Android10客户端事务管理ClientLifecycleManager源码解析

Android 来源:互联网 作者:佚名 发布时间:2022-10-11 21:23:46 人浏览
摘要

在Android 10 App启动分析之Activity启动篇(二)一文中,简单地介绍了Activity的生命周期管理器是如何调度Activity进入onCreate生命周期的流程。这篇文章,我们将详细地分析framework中activity的

在Android 10 App启动分析之Activity启动篇(二)一文中,简单地介绍了Activity的生命周期管理器是如何调度Activity进入onCreate生命周期的流程。这篇文章,我们将详细地分析framework中activity的生命周期管理功能,从更宏观的角度来更全面地了解生命周期及相关事务的工作原理。

生命周期管理是google在Android 9才引入的设计,在Android 9之前,activity 存在生命周期的概念,但并无生命周期管理这一说法。为了方便生命周期的切换以及相关业务的管理,google采用了事务的思想,将生命周期抽象为客户端事务的一部分来统一管理。下图是客户端事务管理完整的UML图:

相关类说明:

  • ClientLifecycleManager: 客户端事务管理类,包括且不限于处理Activity生命周期转换事务,同时也包括 与客户端相关的其他事务处理。
  • ClientTransaction:事务集类,一个事务集可以存放一系列Callback事务及一个生命周期事务。
  • TransactionExecutor:事务执行器,让事务以正确的顺序执行。
  • BaseClientRequest :事务的抽象类,定义了preExecute、execute、postExecute三个接口,分别代表事务执行前、执行中、执行后三个阶段的回调方法。
  • ActivityLifecycleItem:abstract class,Activity生命周期事务类,其子类有DestroyActivityItem、PauseActivityItem、StopActivityItem、ResumeActivityItem,表示具体的activity生命周期转换事务。
  • ClientTransactionItem:abstract class,客户端事务类,ActivityLifecycleItem是它的子类之一。除此之外,还有如下内置的客户端事务:
Transaction Name Desc
ConfigurationChangeItem App configuration 改变的消息
WindowVisibilityItem Window可见性发生改变的消息
MoveToDisplayItem Activity 移动到不同的显示设备的消息
MultiWindowModeChangeItem 多窗口模式改变的消息
ActivityConfigurationChangeItem Activity configuration 改变的回调
PipModeChangeItem 画中画模式改变的消息
ActivityResultItem Activity result的回调
NewIntentItem New intent消息
TopResumedActivityChangeItem Top resumed activity 改变的回调
ActivityRelaunchItem 重启Activity的回调
LaunchActivityItem 请求启动一个Activity

ClientLifecycleManager

ClientLifecycleManager在ActivityTaskManagerService中初始化了唯一的实例,所有的事务操作,必须通过ATMS中的实例来发起。如: mService.getLifecycleManager().scheduleTransaction(clientTransaction);。

ClientLifecycleManager的源码如下:

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

class ClientLifecycleManager {

    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {

        final IApplicationThread client = transaction.getClient();

        transaction.schedule();

        if (!(client instanceof Binder)) {

            // If client is not an instance of Binder - it's a remote call and at this point it is

            // safe to recycle the object. All objects used for local calls will be recycled after

            // the transaction is executed on client in ActivityThread.

            transaction.recycle();

        }

    }

    void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,

            @NonNull ActivityLifecycleItem stateRequest) throws RemoteException {

        final ClientTransaction clientTransaction = transactionWithState(client, activityToken,

                stateRequest);

        scheduleTransaction(clientTransaction);

    }

    void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,

            @NonNull ClientTransactionItem callback) throws RemoteException {

        final ClientTransaction clientTransaction = transactionWithCallback(client, activityToken,

                callback);

        scheduleTransaction(clientTransaction);

    }

    void scheduleTransaction(@NonNull IApplicationThread client,

            @NonNull ClientTransactionItem callback) throws RemoteException {

        final ClientTransaction clientTransaction = transactionWithCallback(client,

                null /* activityToken */, callback);

        scheduleTransaction(clientTransaction);

    }

    private static ClientTransaction transactionWithState(@NonNull IApplicationThread client,

            @NonNull IBinder activityToken, @NonNull ActivityLifecycleItem stateRequest) {

        final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken);

        clientTransaction.setLifecycleStateRequest(stateRequest);

        return clientTransaction;

    }

    private static ClientTransaction transactionWithCallback(@NonNull IApplicationThread client,

            IBinder activityToken, @NonNull ClientTransactionItem callback) {

        final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken);

        clientTransaction.addCallback(callback);

        return clientTransaction;

    }

}

可以看到,ClientLifecycleManager对外暴露了三种事务调度方法:一是 直接调度一个事务集(ClientTransaction);二是调度一个 Lifecycle事务; 三是调用一个Callback事务(ps:除LifeCycle以外的事务,都属于Callback事务)。实际上,无论是Lifecycle事务还是Callback事务,它们都被封装成了事务集的形式,并通过ClientTransaction中的schedule方法去进一步处理。

ClientTransaction

ClientTransaction里的schedule方法非常简单,代码如下所示:

1

2

3

public void schedule() throws RemoteException {

    mClient.scheduleTransaction(this);

}

上述代码片段中的mClient到底指的是什么?

从源码的角度来看,mClient 是 ApplicationThread的一个实例。而 ApplicationThread 是ActivityThread的一个内部类,作为ActivityThread 与外部沟通的桥梁。所有的事务集最终都会被派发到ActivityThread中统一处理。

1

2

3

4

5

ActivityThread.java

   void scheduleTransaction(ClientTransaction transaction) {

        transaction.preExecute(this);

        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);

    }

ActivityThread里首先调用了ClientTransaction中的preExecute方法,代码片段如下:

1

2

3

4

5

6

7

8

9

10

11

public void preExecute(android.app.ClientTransactionHandler clientTransactionHandler) {

    if (mActivityCallbacks != null) {

        final int size = mActivityCallbacks.size();

        for (int i = 0; i < size; ++i) {

            mActivityCallbacks.get(i).preExecute(clientTransactionHandler, mActivityToken);

        }

    }

    if (mLifecycleStateRequest != null) {

        mLifecycleStateRequest.preExecute(clientTransactionHandler, mActivityToken);

    }

}

可以看到,ClientTransaction先调用了 所有注册的Callback事务的preExecute方法,然后调用了唯一的LifeCycle事务的preExecute方法。

在完成所有事务的preExecute逻辑后,ActivityThread发送了一条ActivityThread.H.EXECUTE_TRANSACTION的message,内容如下:

1

2

3

4

mTransactionExecutor.execute(transaction);

if (isSystem()) {

   transaction.recycle();

}

接下来由TransactionExecutor负责后续的逻辑处理。

TransactionExecutor

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

public void execute(ClientTransaction transaction) {

       final IBinder token = transaction.getActivityToken();

       if (token != null) {

           final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed =

                   mTransactionHandler.getActivitiesToBeDestroyed();

           final ClientTransactionItem destroyItem = activitiesToBeDestroyed.get(token);

           if (destroyItem != null) {

               if (transaction.getLifecycleStateRequest() == destroyItem) {

                   // It is going to execute the transaction that will destroy activity with the

                   // token, so the corresponding to-be-destroyed record can be removed.

                   activitiesToBeDestroyed.remove(token);

               }

               if (mTransactionHandler.getActivityClient(token) == null) {

                   // The activity has not been created but has been requested to destroy, so all

                   // transactions for the token are just like being cancelled.

                   Slog.w(TAG, tId(transaction) + "Skip pre-destroyed transaction:\n"

                           + transactionToString(transaction, mTransactionHandler));

                   return;

               }

           }

       }

       executeCallbacks(transaction);

       executeLifecycleState(transaction);

       mPendingActions.clear();

   }

execute中有一段对DestroyActivityItem特殊处理的逻辑,不太重要,我们忽略它。

我们分别来看一下executeCallbacks和executeLifecycleState两个方法。

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

public void executeCallbacks(ClientTransaction transaction) {

       final List<ClientTransactionItem> callbacks = transaction.getCallbacks();

       if (callbacks == null || callbacks.isEmpty()) {

           return;

       }

       final IBinder token = transaction.getActivityToken();

       ActivityClientRecord r = mTransactionHandler.getActivityClient(token);

       // In case when post-execution state of the last callback matches the final state requested

       // for the activity in this transaction, we won't do the last transition here and do it when

       // moving to final state instead (because it may contain additional parameters from server).

       final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();

       final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState()

               : UNDEFINED;

       // Index of the last callback that requests some post-execution state.

       final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);

       final int size = callbacks.size();

       for (int i = 0; i < size; ++i) {

           final ClientTransactionItem item = callbacks.get(i);

           if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);

           final int postExecutionState = item.getPostExecutionState();

           final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,

                   item.getPostExecutionState());

           if (closestPreExecutionState != UNDEFINED) {

               cycleToPath(r, closestPreExecutionState, transaction);

           }

           item.execute(mTransactionHandler, token, mPendingActions);

           item.postExecute(mTransactionHandler, token, mPendingActions);

           if (r == null) {

               // Launch activity request will create an activity record.

               r = mTransactionHandler.getActivityClient(token);

           }

           if (postExecutionState != UNDEFINED && r != null) {

               // Skip the very last transition and perform it by explicit state request instead.

               final boolean shouldExcludeLastTransition =

                       i == lastCallbackRequestingState && finalState == postExecutionState;

               cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction);

           }

       }

   }

上述代码主要做了以下几件事:

  • 获取事务集的target lifecycle。
  • 获取事务集中最后一次Activity生命周期转换的Callback索引。
  • 遍历所有的Callback事务。
  • 获取Callback事务的结束状态值,如结束状态值为onResume,检查Activity当前状态,判断当前的就近状态(onStart/onPause),并将activity转换到就近状态。
  • 执行Callback事务的execute和postExecute逻辑。
  • 跳过最后一个状态转换,改为通过显式状态变换去执行。

google这段逻辑写的极为繁杂啰嗦,让人吐槽的点实在太多了,但还是要在此讲一下它是怎么设计的,源码中又哪里糟点。

首先是事务集的target lifecycle,它指的是事务集中唯一的Lifecycle事务(如果存在的话)的状态,表示事务集执行完毕后,Activity最终的生命周期状态。

第二点,事务集中最后一次Activity生命周期转换的Callback索引,这句话是什么含义呢?

Callback事务中有这么一个方法,getPostExecutionState,它表示在当前Callback事务执行完毕后Activity所需处于的生命周期状态,为方便叙述,下文称其为结束状态。将Callback事务列表从后向前遍历,如果当前事务存在结束状态(即getPostExecutionState的值不为UNDEFINED),且与上一个结束状态相同,记录下此时事务在列表中的索引值,直到当前结束状态与上一个状态不同为止。此时,上一个事务的索引值即为事务集中最后一次Activity生命周期转换的Callback索引。

假如某事务集有这样一组Callback事务——ConfigurationChangeItem、NewIntentItem、ConfigurationChangeItem、NewIntentItem。其中ConfigurationChangeItem的结束状态为UNDEFINED,NewIntentItem的结束状态为ON_RESUME。

我们从后向前开始遍历:

  • 最后一个事务为 NewIntentItem,结束状态为ON_RESUME,记录下此时的索引值 3。
  • 倒数第二个事务为 ConfigurationChangeItem,不存在结束状态,跳过。
  • 倒数第三个事务为 NewIntentItem,结束状态为ON_RESUME,上一个结束状态同样也是 ON_RESUME,更新索引值 为 1。
  • 倒数第四个事务为 ConfigurationChangeItem,不存在结束状态,跳过。

因此,此事务集中最后一次Activity生命周期转换的Callback索引 为1。至于这个索引值有什么意义呢,待会再解释。

然而,这段设计中还是有几点需要吐槽一下:

  • google官方在注释中举例的 事务是 Configuration - ActivityResult - Configuration - ActivityResult , 并且说明ActivityResult 的结束状态为RESUMED。让我们看看ActivityResultItem的源码:

1

2

3

4

5

6

7

8

9

public class ActivityResultItem extends ClientTransactionItem {

    @UnsupportedAppUsage

    private List<ResultInfo> mResultInfoList;

    /* TODO(b/78294732)

    @Override

    public int getPostExecutionState() {

        return ON_RESUME;

    }*/

}

Excuse me? TODO state!! 实际上,这个TODO 一直拖到android 13 才被加上,en~~~~。

  • 设计归设计,到android13 为止,framework里从来没有过一个事务集里绑定多个Callback事务的用法,所以 这段逻辑设计的并没有什么用。

第四点,如果Callback事务的结束状态为ON_RESUME,则判断当前Activity状态是更靠近ON_START状态还是ON_PAUSE状态,并将activity状态转换到就近状态(ON_START or ON_PAUSE)。

这里判断当前Activity状态更靠近ON_START还是ON_PAUSE,采用的是路径长度比较法。framework中为Activity定义了九种状态,具体如下:

1

2

3

4

5

6

7

8

public static final int UNDEFINED = -1;

public static final int PRE_ON_CREATE = 0;

public static final int ON_CREATE = 1;

public static final int ON_START = 2;

public static final int ON_RESUME = 3;

public static final int ON_PAUSE = 4;

public static final int ON_STOP = 5;

public static final int ON_DESTROY = 6;

状态之间的转换路径如下表所示:

上表中置灰的单元格,表示不存在或禁止的状态转换,而单元格中A ~ B 这种写法 表示 从 A到 B状态之间的中间所有状态,如: start 状态为 PRE_CREATE , finish 状态为 DESTROY ,在上表中状态转换路径为 create ~ destroy,表示 activity从 PRE_CREATE 状态转换到 DESTROY 状态,需要经历 create、start 、 resume 、pause 、stop 、 detroy ,即create到destroy之间所有的生命周期变换的过程。同时,我们也称 create~destroy 是 PRE_CREATE到DESTROY状态路径,它的路径长度为6。

如何判断当前生命周期是更靠近ON_START还是ON_PAUSE?我们举个例子来看一下,假如当前的生命周期为ON_STOP,由上述状态路径表可知,从ON_STOP状态转换到ON_START状态的路径为restart、start,长度为2;而由ON_STOP状态转换到ON_PAUSE状态的路径为restart、start~pause,长度为4。因此,当前activity的状态更靠近ON_START。

在路径长度算法的代码里,google给凡是路径中含有destroy状态的长度,赋予了一段惩罚长度,让它的长度增加了10,具体代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

public int getClosestOfStates(ActivityClientRecord r, int[] finalStates) {

      if (finalStates == null || finalStates.length == 0) {

          return UNDEFINED;

      }

      final int currentState = r.getLifecycleState();

      int closestState = UNDEFINED;

      for (int i = 0, shortestPath = Integer.MAX_VALUE, pathLength; i < finalStates.length; i++) {

          getLifecyclePath(currentState, finalStates[i], false /* excludeLastState */);

          pathLength = mLifecycleSequence.size();

          if (pathInvolvesDestruction(mLifecycleSequence)) {

      //路径中含有destroy状态,增加惩罚长度

              pathLength += DESTRUCTION_PENALTY;

          }

          if (shortestPath > pathLength) {

              shortestPath = pathLength;

              closestState = finalStates[i];

          }

      }

      return closestState;

  }

然而我们可以看一下表格中finish state为 START 和 PAUSE的两列,所有的路径中都不包含 destroy 状态,所以这个惩罚长度的意义何在?

第五步开始,正式执行 事务的execute和postExecute逻辑。这里要谈一下,为什么执行结束状态为 ON_RESUME的事务时,先要在第四步将 状态切换到 ON_START 或 ON_RESUME后,然后才开始去执行事务的逻辑呢?

在Android 10中,结束状态为 ON_RESUME 的事务只有 NewIntentItem,其 excute 方法代码片段如下:

1

2

3

4

5

6

7

NewIntentItem.java

    public void execute(ClientTransactionHandler client, ActivityClientRecord r,

            PendingTransactionActions pendingActions) {

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityNewIntent");

        client.handleNewIntent(r, mIntents);

        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

    }

它最终会调用到 activity的performNewIntent方法。

可以看到在这个过程中并没有涉及到生命周期状态的转换。因此,google这段逻辑的意图是:在执行最终状态为ON_RESUME的事务时,先将activity生命周期状态转换到ON_RESUME的临近状态,即ON_START或ON_PAUSE状态,然后再去执行事务,最后在事务执行完毕后,将activity的状态真正地切换到ON_RESUME。

第六步,跳过最后一个状态转换,改为通过显式状态变换去执行。

这里做了一个判断,如果事务组最后一次最终状态与事务集的生命周期状态相同,跳过此事务的最终状态的转换,改由 LifeCycle事务去执行状态转换。

然而,我们来看这样一组事务,ConfigurationChangeItem、NewIntentItem、ConfigurationChangeItem、NewIntentItem,虽然实际编码中并不会写出这样一组事务,但仍可以用来吐槽一下google的这段代码逻辑:

由第二步可知,上述的的事务组最后一次activity状态转换的Callback索引为 1。

1

2

3

final boolean shouldExcludeLastTransition =

                        i == lastCallbackRequestingState && finalState == postExecutionState;

cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction);

可以看到,在第二个事务,activity并不会切换到ON_RESUME状态。

然而这段代码最大的问题是,这个判断并不能达成显式状态变换的目标,因为在第四个事务时 activity会被切换到ON_REUSME的目标状态。

有读者可能会提出异议了,作者你举的这个例子是属于特例,代码中不可能这么写。 然而,如果不需要考虑这种特殊情况的话,第二步的索引值计算又有什么作用呢?

executeLifecycleState

这个方法是对事务集中的LifeCycle事务的处理,其代码具体如下:

1

2

3

4

5

6

7

8

private void executeLifecycleState(ClientTransaction transaction) {

    ...

      // Cycle to the state right before the final requested state.

      cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);

      // Execute the final transition with proper parameters.

      lifecycleItem.execute(mTransactionHandler, token, mPendingActions);

      lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);

  }

可以看到,cycleToPath是将activity切换到目标生命周期状态的关键方法:

1

2

3

4

5

6

private void cycleToPath(ActivityClientRecord r, int finish, boolean excludeLastState,

          ClientTransaction transaction) {

      final int start = r.getLifecycleState();

      final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);

      performLifecycleSequence(r, path, transaction);

  }

getLifecyclePath是获取状态路径的方法,关于状态路径在上文中已经有所介绍。

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

  private void performLifecycleSequence(ActivityClientRecord r, IntArray path,

            ClientTransaction transaction) {

        final int size = path.size();

        for (int i = 0, state; i < size; i++) {

            state = path.get(i);

            switch (state) {

                case ON_CREATE:

                    mTransactionHandler.handleLaunchActivity(r, mPendingActions,

                            null /* customIntent */);

                    break;

                case ON_START:

                    mTransactionHandler.handleStartActivity(r, mPendingActions,

                            null /* activityOptions */);

                    break;

                case ON_RESUME:

                    mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */,

                            r.isForward, "LIFECYCLER_RESUME_ACTIVITY");

                    break;

                case ON_PAUSE:

                    mTransactionHandler.handlePauseActivity(r, false /* finished */,

                            false /* userLeaving */, 0 /* configChanges */, mPendingActions,

                            "LIFECYCLER_PAUSE_ACTIVITY");

                    break;

                case ON_STOP:

                    mTransactionHandler.handleStopActivity(r, 0 /* configChanges */,

                            mPendingActions, false /* finalStateRequest */,

                            "LIFECYCLER_STOP_ACTIVITY");

                    break;

                case ON_DESTROY:

                    mTransactionHandler.handleDestroyActivity(r, false /* finishing */,

                            0 /* configChanges */, false /* getNonConfigInstance */,

                            "performLifecycleSequence. cycling to:" + path.get(size - 1));

                    break;

                case ON_RESTART:

                    mTransactionHandler.performRestartActivity(r, false /* start */);

                    break;

                default:

                    throw new IllegalArgumentException("Unexpected lifecycle state: " + state);

            }

        }

    }

}

获取到状态路径后,开始遍历路径,按顺序依次切换路径中的activity生命周期状态,直到到达目标状态为止。

在达到目标路径后,会调用Lifecycle事务的excute方法。这里会再一次调用切换到目标状态的逻辑,不过实际状态切换时,源码里做了状态判重的操作,并不会造成任何不良的影响


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。

您可能感兴趣的文章 :

原文链接 : https://juejin.cn/post/7152108470630613023
相关文章
  • Kotlin的Collection与Sequence操作异同点介绍

    Kotlin的Collection与Sequence操作异同点介绍
    在Android开发中,集合是我们必备的容器,Kotlin的标准库中提供了很多处理集合的方法,而且还提供了两种基于容器的工作方式:Collection 和
  • 实现一个Kotlin函数类型方法

    实现一个Kotlin函数类型方法
    接口与函数类型 业务开发中,经常会有实现一个函数式接口(即接口只有一个方法需要实现)的场景,大家应该都会不假思索的写出如下代
  • Android10 App启动Activity源码分析
    ActivityThread的main方法 让我们把目光聚焦到ActivityThread的main方法上。 ActivityThread的源码路径为/frameworks/base/core/java/android/app/ActivityThread。 1 2
  • Android10客户端事务管理ClientLifecycleManager源码解析

    Android10客户端事务管理ClientLifecycleManager源码解析
    在Android 10 App启动分析之Activity启动篇(二)一文中,简单地介绍了Activity的生命周期管理器是如何调度Activity进入onCreate生命周期的流程。这
  • Kotlin对象的懒加载方式by lazy与lateinit异同介绍

    Kotlin对象的懒加载方式by lazy与lateinit异同介绍
    属性或对象的延时加载是我们相当常用的,一般我们都是使用 lateinit 和 by lazy 来实现。 他们两者都是延时初始化,那么在使用时那么他们两
  • Android类加载流程分析

    Android类加载流程分析
    本文分析的代码基于Android8.1.0源码。 流程分析 从loadClass开始,我们来看下Android中类加载的流程 /libcore/ojluni/src/main/java/java/lang/ClassLoader.ja
  • Android实现读写USB串口数据的代码

    Android实现读写USB串口数据的代码
    最近在研究USB方面的内容;先后做了关于Android读写HID、串口设备的DEMO。本文比较简单,主要介绍的是Android实现读取串口数据的功能 废话不
  • Epoxy - 在RecyclerView中构建复杂界面
    Diffing 对于复杂数据结构支持的多个视图类型展示在屏幕上, Epoxy此时是尤其有用的. 在这些场景中, 数据可能会被网络请求, 异步 Observable, 用
  • Android性能优化的详细介绍

    Android性能优化的详细介绍
    性能优化是一个app很重要的一部分,一个性能优良的app从被下载到启动到使用都能给用户到来很好的体验。自然我们做性能优化也是从被下
  • Android进阶宝典-插件化2(Hook启动插件中四大组件

    Android进阶宝典-插件化2(Hook启动插件中四大组件
    在上一节,我们主要介绍了如果通过反射来加载插件中的类,调用类中的方法;既然插件是一个apk,其实最重要的是启动插件中的Activity、
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计