Intent implicitIntent = new Intent();
implicitIntent.setAction(“com.test.image”);
implicitIntent.addCategory(“android.intent.category.DEFAULT”);
MainActivity.this.startActivity(implicitIntent);

具体界面的配置如下:

注意如果是其他App的Activity,需要添加android:exported="true"才能被调用。

8.Activity的启动流程(中级问题)

对很多开发者来说,这可能都是个很沉重的问题,原因很简单,因为回答不好,毕竟里面涉及到的东西很多,需要你拥有很大知识存储量。下面来尝试回答这个问题(基于源码9.0)

首先先普及一些常见的概念

Instrumentation

Android Instrumentation是Android系统中的一套控制方法或者“钩子”,这些钩子可以在正常的生命周期(正常是由操作系统控制的)之外控制Android控件的运行,其实指的就是Instrumentation类提供的各种流程控制方法

app->instrumentation->ams->app,自动化测试可以通过Instrumentation来操作Activity等,这个Instrumentation相当于设计了一个统一的入口

ActivityThread

ActivityThread不是线程类(Thread),只不过它会跑在ActivityThread.main()方法中,安卓程序的入口就是该方法,同时在该方法中一个Looper不断循环的在消息队列中处理消息。管理应用程序进程中主线程的执行,根据Activity管理者的请求调度和执行activities、broadcasts及其相关的操作。

public static void main(String[] args) {
// 看源码很重要的一个能力就是‘眼中只有你’,认不到的都忽略,看认得到的
···
// 创建主线程的Looper对象,发现和工作线程创建Looper对象调用的方法不一样,这里先记下,以后在详解。
// 主线程原来也有Looper对象啊
Looper.prepareMainLooper();

//创建ActivityThread
ActivityThread thread = new ActivityThread();
thread.attach(false);

// 如果主线程的Handler为空(可以看出,一个好的命名可读性是多么高),那就为主线程创建一个Handler。
// 然后我们还可以在主线程创建Handler,说明一个线程对应多个Handler。多读源码,很多问题都得到了解决啊。
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();

// 这里抛了个异常,主线程loop异常退出。说明主线程loop不能退出,这里和前面建立Looper对象的调用方法有关
throw new RuntimeException(“Main thread loop unexpectedly exited”);
}

ActivityManagerService

Android中最核心的服务,主要负责系统中四大组件的启动、切换、调度及应用程序的管理和调度等工作。

ActivityManager

该类提供与Activity、Service和Process相关的信息以及交互方法, 可以被看作是ActivityManagerService的辅助类。

ActivityStackSupervisor

负责所有Activity栈的管理。内部管理了mHomeStack、mFocusedStack和mLastFocusedStack三个Activity栈。其中,mHomeStack管理的是Launcher相关的Activity栈;mFocusedStack管理的是当前显示在前台Activity的Activity栈;mLastFocusedStack管理的是上一次显示在前台Activity的Activity栈。下面是大致的关系图,对于没有分屏功能以及虚拟屏的情况下,ActivityStackSupervisor与ActivityDisplay都是系统唯一。

ActivityStack

ActivityStack负责“Activity栈”的状态和管理,ActivityStack内部包含了多个任务栈(TaskRecord),TaskRecord内部维护了一个ArrayList<ActivityRecord>用来保存和管理ActivityRecord,ActivityRecord包含了一个Activity的所有信息

如果我们从桌面点击启动app,桌面就是一个Activity,点击app(按钮)启动我们的启动页Activity,从这里分析Activity的启动流程更加全面,而不是在app中去启动一个普通的Activity。可以分为如下几个流程

  1. Launcher通知AMS启动App的启动页Activity,AMS记录要启动的Activity信息,并且通知Launcher进入pause状态。

Launcher进入pause状态后,通知AMS已经paused了,可以启动App了

  1. 如果App未开启过,AMS发送创建进程请求,Zogyte进程接受AMS请求并孵化应用进程,应用进程调用ActivityThread并调用mian()方法,并且main()方法中创建ActivityThread对象,activityThread.attach()方法中进行绑定(应用进程绑定到AMS),传入applicationThread以便通讯。

  2. AMS通知App绑定Application(bindApplication)并启动Activity,并且创建和关联Context,最后调用onCreate等方法。

灵魂拷问:AMS,Zogyte,App进程,Launcher如何通信?

这个问题一旦问出来,能干翻一大堆开发人员,下面来仔细讲讲:

App进程和AMS是如何通信的?

Zogyte去fork一个App进程,后面就是应用进程和AMS两者的事情了,我们知道Android的跨进程通信是通过Binder服务的,AMS所在的进程和应用进程在通过Binder互相通信时,实际上都是通过两者的代理类进行通信的。

ActivityManagerService(AMS)在手机开机后时就已经启动了,应用进程去调用AMS的方法,比如startActivity,很容易调用,因为AMS是一个有名称的Binder服务,在任意地方都可以通过在ServiceManger(SM)里面查询拿到代理类,调用代理类的对应方法,然后再去调用AMS的真正方法。

因为Binder通信是通过代理类来通信的,如果拿不到代理类,其他进程就不知道如何和我们的App通信,系统服务中的AMS也就不知道如何和我们App通信了,所以当App进程创建完成后,会进行设置代理,代理的设置过程如图

就是在ActivityThread.attach(false)方法中,AMS绑定ApplicationThread对象,即应用进程绑定到AMS,通过调用AMS的attachApplication来将ActivityThread的内部类ApplicationThread对象绑定至AMS,这样AMS就可以通过这个代理对象来控制应用进程

AMS和Launcher是怎么通信的?

其实Launcher也是一个App,调用startActivity方法,然后调用的是Instrumentation的execStartActivity方法

public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {

try {

//获取AMS的代理对象
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException(“Failure from system”, e);
}
return null;
}

在这个方法会调用ActivityManager的getService方法来得到AMS的代理对象,然后调用这个代理对象的startActivity方法

@UnsupportedAppUsage
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}

@UnsupportedAppUsage
private static final Singleton IActivityManagerSingleton =
new Singleton() {
@Override
protected IActivityManager create() {
//得到activity的service引用,即IBinder类型的AMS引用
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
//转换成IActivityManager对象
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};

可以发现在Singleton中的create方法中由于b是AMS引用作为服务端处于SystemServer进程中,与当前Launcher进程作为客户端与服务端不在同一个进程,所以am返回的是IActivityManager.Stub的代理对象,此时如果要实现客户端与服务端进程间的通信,只需要在AMS继承了IActivityManager.Stub类并实现了相应的方法,而通过下面的代码可以发现AMS刚好是继承了IActivityManager.Stub类的,这样Launcher进程作为客户端就拥有了服务端AMS的代理对象,然后就可以调用AMS的方法来实现具体功能了,就这样Launcher的工作就交给AMS实现了。

public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
}

Zygote和AMS是如何通信的?

AMS和Zygote建立Socket连接,然后发送创建应用进程的请求。具体可以参考这里。

最后我们再来看看流程图,看下方的App进程启动过程和Activity.startActivity这两个流程

这里还要提到一点,Hook Activity的启动流程是一个很重要的运用场景,我们需要欺骗AMS,然后启动真正的TargetActivity,Hook有起始点和终点。这里需要寻找两个地方的hook点,一个是对Intent中Activity的替换(hookIActivityTaskManager方法),一个是对Intent中Activity的还原(hookHandler)。

在回答Activity的启动流程时,具体的方法如何调用并不重要,所以我才会在最后放出整个流程,各个进程之间如何建立通信,如何通信很重要,同时一些Activity相关概念也很重要,熟悉这些,你就很容易把整个流程串起来了。

Activity深层次问题

1.Activity生命周期的变化对进程的优先级有什么影响?

这里先看一下官网上Activity生命周期上对onStart的一段描述,onStart时候Activity就对用户可见了

同时你也可以在《Android开发艺术探索》上看到类似的描述

![](https://p3-juejin.b
yteimg.com/tos-cn-i-k3u1fbpfcp/bc9894f0ea024cc88192b9291f076181~tplv-k3u1fbpfcp-watermark.image)

但是了解Activity启动流程源码的朋友都知道,ActivityThread的handleResumeActivity方法中,首先调用Activity的onResume方法,接着会调用Activity.makeVisible()在该方法中,DecorView真正完成了添加和显示这两个过程,到这里Activity的视图才能被看到。DecoreView和Window进行关联。有兴趣可以看看我这篇文章的分析。

void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
//DecoreView和WindowManager进行关联。
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
//设置DecorView可见
mDecor.setVisibility(View.VISIBLE);
}

也就是说在onResume方法执行之后再调用Activity.makeVisible()方法,我们才能真正用肉眼看到我们的DecoreView,看到这里你这里不禁会产生一个疑问,那上面官网上的说法(onStart() 调用使 Activity 对用户可见)难道是错误的吗?

带着疑问我们继续在官网上找答案,在进程和生命周期这一章节上可以看到:

为了确定在内存不足时应该终止哪些进程,Android 会根据每个进程中运行的组件以及这些组件的状态,将它们放入“重要性层次结构”。这些进程类型包括(按重要性排序):

  1. 前台进程是用户目前执行操作所需的进程。在不同的情况下,进程可能会因为其所包含的各种应用组件而被视为前台进程。如果以下任一条件成立,则进程会被认为位于前台:
  • 它正在用户的互动屏幕上运行一个 Activity(其 onResume() 方法已被调用)。
  • 它有一个 BroadcastReceiver 目前正在运行(其 BroadcastReceiver.onReceive() 方法正在执行)。
  • 它有一个 Service 目前正在执行其某个回调(Service.onCreate()Service.onStart()Service.onDestroy())中的代码。
  1. 系统中只有少数此类进程,而且除非内存过低,导致连这些进程都无法继续运行,才会在最后一步终止这些进程。通常,此时设备已达到内存分页状态,因此必须执行此操作才能使用户界面保持响应。

  2. 可见进程正在进行用户当前知晓的任务,因此终止该进程会对用户体验造成明显的负面影响。在以下条件下,进程将被视为可见:

  • 它正在运行的 Activity 在屏幕上对用户可见,但不在前台(其 onPause() 方法已被调用)。举例来说,如果前台 Activity 显示为一个对话框,而这个对话框允许在其后面看到上一个 Activity,则可能会出现这种情况。
  • 它有一个 Service 正在通过 Service.startForeground()(要求系统将该服务视为用户知晓或基本上对用户可见的服务)作为前台服务运行。
  • 系统正在使用其托管的服务实现用户知晓的特定功能,例如动态壁纸、输入法服务等。

相比前台进程,系统中运行的这些进程数量较不受限制,但仍相对受控。这些进程被认为非常重要,除非系统为了使所有前台进程保持运行而需要终止它们,否则不会这么做。

  1. 服务进程包含一个已使用 startService() 方法启动的 Service 。虽然用户无法直接看到这些进程,但它们通常正在执行用户关心的任务(例如后台网络数据上传或下载),因此系统会始终使此类进程保持运行,除非没有足够的内存来保留所有前台和可见进程。

已经运行了很长时间(例如 30 分钟或更长时间)的服务的重要性可能会降位,以使其进程降至下文所述的缓存 LRU 列表。这有助于避免超长时间运行的服务因内存泄露或其他问题占用大量内存,进而妨碍系统有效利用缓存进程。

  1. 缓存进程是目前不需要的进程,因此,如果其他地方需要内存,系统可以根据需要自由地终止该进程。在正常运行的系统中,这些是内存管理中涉及的唯一进程:运行良好的系统将始终有多个缓存进程可用(为了更高效地切换应用),并根据需要定期终止最早的进程。只有在非常危急(且具有不良影响)的情况下,系统中的所有缓存进程才会被终止,此时系统必须开始终止服务进程。

这些进程通常包含用户当前不可见的一个或多个 Activity 实例(onStop() 方法已被调用并返回)。只要它们正确实现其 Activity 生命周期(详情请见 Activity),那么当系统终止此类流程时,就不会影响用户返回该应用时的体验,因为当关联的 Activity 在新的进程中重新创建时,它可以恢复之前保存的状态。

这些进程保存在伪 LRU 列表中,列表中的最后一个进程是为了回收内存而终止的第一个进程。此列表的确切排序政策是平台的实现细节,但它通常会先尝试保留更多有用的进程(比如托管用户的主屏幕应用、用户最后看到的 Activity 的进程等),再保留其他类型的进程。还可以针对终止进程应用其他政策:比如对允许的进程数量的硬限制,对进程可持续保持缓存状态的时间长短的限制等。

可以看到在屏幕上运行时一个Activity的onResume的方法已被调用,此时处于前台进程;可见进程的一个符合条件:它正在运行的 Activity 在屏幕上对用户可见,但不在前台,然后再对比上面对onStart的描述(onStart() 调用使 Activity 对用户可见,因为应用会为 Activity 进入前台并支持互动做准备),这下子你就豁然开朗了,这里的onStart的可见指的是可见进程的可见,而不是真正意义上的肉眼可见

“onPause此方法表示 Activity 不再位于前台(尽管在用户处于多窗口模式时 Activity 仍然可见)”,“如果您的 Activity 不再对用户可见,说明其已进入“已停止”状态,因此系统将调用 onStop() 回调”,以上都是官方的描述,我们可以打印一下手机中的这些进程,使用adb shell dumpsys meminfo命令,设备是android 10华为手机。

可以看到分别对应我们的前台进程,可见进程,服务进程和缓存进程,其中服务进程还分为A Services和B Services。其实远远不止这么多的进程级别区分,我自己的App打开后,然后点击home键退到后台,此时属于Previous进程(后台进程)级别(com.jackie.testdialog),如果我打开App后,点击返回键退出,这个时候我的App进程就变成了Cached进程级别了。

讲了这么多,你可能觉得一直没有一个量化的数字,进程的级别(oom_adj)的取值范围是多少,在Android7.0之后,ADJ采用100,200,300等数字。下面是基于android9的区分:

ADJ级别 取值 含义
NATIVE_ADJ -1000 native进程
SYSTEM_ADJ -900 仅指system_server进程
PERSISTENT_PROC_ADJ -800 系统persistent进程
PERSISTENT_SERVICE_ADJ -700 关联着系统或persistent进程
FOREGROUND_APP_ADJ 0 前台进程
VISIBLE_APP_ADJ 100 可见进程
PERCEPTIBLE_APP_ADJ 200 可感知进程,比如后台音乐播放
BACKUP_APP_ADJ 300 备份进程
HEAVY_WEIGHT_APP_ADJ 400 重量级进程
SERVICE_ADJ 500 服务进程
HOME_APP_ADJ 600 Home进程
PREVIOUS_APP_ADJ 700 上一个进程
SERVICE_B_ADJ 800 B List中的Service
CACHED_APP_MIN_ADJ 900 不可见进程的adj最小值
CACHED_APP_MAX_ADJ 906 不可见进程的adj最大值

开发者应该减少在保活上花心思,更应该在优化内存上下功夫,因为在相同ADJ级别的情况下,系统会选择优先杀内存占用的进程。当然你也可以手动去测试App的进程级别,不过过程可能有点麻烦,可以参考这篇文章。

小结

当界面只有一个Activity时,它进入onStart和onPause时是可见进程,进入onResume时是前台进程,打开后点击Home键退到后台这个时候是Previous进程(后台进程),如果直接点击返回键退出Activity,这个时候是缓存进程;如果有多个Activity(注意这个时候只有app从后台任务进入前台,或者点击Home键退到后台这两种场景;因为app在前台运行时都是前台进程),栈顶的的Activity进入onStart和onPause时是可见进程,进入onResume后是前台进程,点击Home键退到后台时是Previous进程(大家常说的后台进程)。

2.如果App还存在缓存进程,这个时候启动App,应用Application的onCreate方法会执行吗?

如果你点击主界面MainActivity,点击返回键后系统执行MainActivity的onDestory方法,这个时候App进程为缓存进程,下次启动App你会发现Application的onCreate方法并不会执行,当然MainActivity的生命周期都会正常执行,这是因为从缓存进程启动App,系统已经缓存了很多信息,很多数据并不会被销毁,onCreate中初始化的那些内容还在,方便用户下次快速启动。利用这一特性,我们的App首次启动速度一般为500600ms,退出App后存在缓存进程的情况下,每次启动的速度一般为200300ms,算是某种程度上提升了App的启动时间。

需要注意的是,很多App在退出主界面的时候,会手动调用如下代码去退出App

System.exit(0);

一旦调用了如下代码,就会彻底的退出并不会利用缓存进程的优势,也失去了系统提供给我们的优化了。

3.一个Activity A启动另一个Activity B,为何会先走A的onPause方法,等到B执行完onResume方法后,才会走A的onStop方法呢?

如果你看过前面两个问题,这个问题你可能已经有答案了。手机之所以进行进程的管理,用不同的优先级对进程进行区分,首先肯定是为了保证用户的流畅体验,对于优先级低且占用内存高的进程及时清理,保证前台进程有足够的运行空间。前面我们讲到处于前台的(获取焦点)界面只有一个,onPause时当前进程离开了前台,当然可能也要进行一些数据的保存,所以肯定需要先执行当前界面的某个方法,然后再执行B界面的onCreate,onStart,onResume是为了新的界面能够被快速呈现(获取焦点),然后再走旧界面A的onStop方法。

这里也需要注意,onPause方法中尽量不要去做耗时的操作,如果过于耗时,新界面会很久才能显示出来,尽量放在onStop方法中去做。当然onStop中也不能做过于耗时的操作中,前面我们也试过,点击Home键会执行onStop方法,此时App进程处于后台进程,此时进程的优先级的很低的,当内存不足时,onStop中保存数据的操作可能就未完成,然后App进程就被系统回收了。

关于状态保存和恢复,在API28之前,onSaveInstanceState执行在onStop之前,但不限于在onPause之前或之后;在API28之后,onSaveInstanceState 执行时机已确定为在 onStop 之后。而onRestoreInstanceState确定执行在onStart之后。

4.为什么要这么设计Activity生命周期

假如你自己设计界面的生命周期:

  • 界面启动时候用需要设计一个方法

  • 界面完全渲染完毕显示需要一个方法

  • 界面被部分遮盖时/跳到其他界面/退到后台需要一个方法

  • 界面完全退出销毁时需要一个方法

这么看来,我们好像只需要onCreate,onResume,onPause,onDestroy这四个方法,但是这只是一个很粗糙的界面创建~退出流程的回调,但是你看看IOS的UIViewController的生命周期,看起来就是个精致的猪猪女孩

这样一对比,连Android的生命周期显得有点粗糙了,其实不全是,Activity还有一系列的onPostXXX方法以及onContentChanged等,但还是没有IOS细腻。其实我觉得,这些生命周期的回调是基于一些场景设计的,从视图的显示到销毁,考虑到不同的需求,我们需要不同程度级别的设计,如果Android是一个非常简单的系统,也不会实现那么多的特殊需求,可能只需要前面我说的那四个方法就够了,我感觉在生命周期的设计方面,IOS做的更好一些,对开发者更加友好

也有一些人在回答生命周期为什么要这么设计时,可能会这么回答,因为界面需要有个创建/销毁过程,onCreate/onDestroy肯定需要,onStart时进程为可见进程,提升进程的优先级,或者做一些特殊场景的操作,onResume在界面启动完成或者恢复时需要,界面在被透明Activity的覆盖时会执行onPause(),需要有个方法在这个时候做状态保存或特殊操作等,onStop时可以进行状态保存。这样想问题完全是一种结果倒推的想法,经不起仔细的推敲,一定不要从具体的方法去推场景,而是应该从需求场景开始推导,切记,这一切都是需求或可能的需求引起的

第三方App中一些Activity的设置

今日头条极速版-新闻界面打开的一些限制

NewDetailActivity就是我们看到的普通新闻界面,最多只能打开四个,超过四个就会将之前最早的NewDetailActivity关闭,原因很简单,如果无限制的话Activity会越建越多,整个应用越来越卡,影响用户体验。

TaskRecord{8636d7b #6564 A=com.ss.android.article.lite U=0 StackId=282 sz=5}
Run #4: ActivityRecord{8794744 u0 com.ss.android.article.lite/com.ss.android.article.base.feature.detail2.view.NewDetailActivity t6564}
Run #3: ActivityRecord{8be5248 u0 com.ss.android.article.lite/com.ss.android.article.base.feature.detail2.view.NewDetailActivity t6564}
Run #2: ActivityRecord{8bd6a09 u0 com.ss.android.article.lite/com.ss.android.article.base.feature.detail2.view.NewDetailActivity t6564}
Run #1: ActivityRecord{87cc383 u0 com.ss.android.article.lite/com.ss.android.article.base.feature.detail2.view.NewDetailActivity t6564}
Run #0: ActivityRecord{8bd6b44 u0 com.ss.android.article.lite/.activity.SplashActivity t6564}

而且还可以发现这个今日头条极速版的主页叫SplashActivity,真他么牛逼~,估计是原来有个SplashActivity界面和MainActivity界面,为了优化快速启动,给用户一个秒开的感觉,移除原来的SplashActivity,直接把MainActivity改名为SplashActivity,然后做主题的替换。

然后我们看看它的启动模式,启动模式是standard。

~ » adb shell dumpsys activity | grep SplashActivity jackie@JackieLindeMacBook-Pro
baseIntent=Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.ss.android.article.lite/.activity.SplashActivity }

Activity的初级,中级,高级问法,android开发艺术探索电子相关推荐

  1. 不经一番寒彻骨,怎得梅花扑鼻香,android开发艺术探索电子

    因为年纪都比我们略大,再加上穿得比较正式,王大哥在人群里显得比较成熟.要不是后来有一次闲聊,还真以为他就是这个项目的负责人. 也就是从那个时候开始,我和王大哥渐渐熟悉了起来. 一个正值打拼事业的年纪, ...

  2. Android开发艺术探索完结篇——天道酬勤

    这片文章发布,代表着我已经把本书和看完并且笔记也发布完成了,回忆了一下我看Android群英传,只用了两个月,但是看本书却花了2016年05月04日 - 2018年07月16日,整整两年多,真是惭愧 ...

  3. 《Android开发艺术探索》笔记目录

    该笔记以<Android开发艺术探索>为基础,结合Android 9.0代码和官方文档,修正了原书中表述不明确和过时的部分,同时加入了大量的个人理解. 13章,14章,15章是总结性的章节 ...

  4. Android开发艺术探索笔记(一) Activity的生命周期和启动模式(1)

    Activity作为Android开发中最常用的一个组件,是Android开发人员必须熟悉且掌握的重要内容.同时Activity也是在面试中经常被问到的一个方向.因此,掌握Activity的重要性也不 ...

  5. Android开发艺术探索之Activity篇总结

    本文内容来自<Android开发艺术探索>第一章,个人学习提炼总结,欢迎指正. 1.1典型情况下的生命周期 onCreate():表示Activity正在被创建,初始化布局资源+Activ ...

  6. 《Android开发艺术探索》图书勘误

    第一章 在13页提到"系统只在Activity异常终止的时候才会调用onSaveInstanceState与onRestoreInstanceState来储存和恢复数据,其他情况不会触发这个 ...

  7. Android开发艺术探索——第七章:Android动画深入分析

    Android开发艺术探索--第七章:Android动画深入分析 Android的动画可以分成三种,view动画,帧动画,还有属性动画,其实帧动画也是属于view动画的一种,,只不过他和传统的平移之类 ...

  8. Android开发艺术探索读书笔记(一)

    首先向各位严重推荐主席这本书<Android开发艺术探索>. 再感谢主席邀请写这篇读书笔记 + 书评.书已经完整的翻完一遍了,但是还没有细致的品读并run代码,最近有时间正好系统的把整本书 ...

  9. 《Android开发艺术探索》完结篇

    笔记链接: <Android开发艺术探索>之Activity的生命周期和启动模式(一) <Android开发艺术探索>之IPC机制上(二) <Android开发艺术探索& ...

最新文章

  1. VMware14安装CentOS7的详细教程
  2. 从自来水公司到社会工作者再做运维,我深耕 IT 一线的这 8 年
  3. SQLite中的SELECT子句使用通配符
  4. apache2.4.6支不支持jsp_Spring Boot中文参考指南(2.1.6)50、Kotlin 支持
  5. html中怎样引入外部字体文件路径,CSS引入外部字体
  6. Windows搭建FTP服务
  7. 人工智能 信道估计 深度学习_DEMO演示|基于IVP02D 人工智能工作站的深度学习引擎,实现人群热力估计...
  8. 数据可视化【十一】树状图
  9. Linux的守护进程
  10. tableView 三级展开 嵌入collocationView
  11. 记一次成功修复U盘的痛苦经历
  12. 分享下我 Github 被封的经历
  13. 硬件问题解决方法(大全)
  14. MOS管开关速度相关参数
  15. 疫情当前,产品求职者更需苦练内功
  16. java实现捕鱼达人
  17. 使用高匿代理访问西刺代理(假如ip被封可用高匿ip访问网站)
  18. 云原生IDE:iVX免费的首个通用无代码开发平台
  19. 介绍一款功能强大的步进电机控制驱动芯片TMC5240
  20. 学生网课网页设计成品 在线视频学习类网页制作 三层结构网页模板 静态HTML注册登录网页模板 学生毕业设计网页制作作品 网校类网页代制做

热门文章

  1. 如何申请试用密信免费时间戳产品与服务
  2. php提取表格,php 实现的图像读取到表格中
  3. OSChina 元旦乱弹 ——能笑一辈子的笑话
  4. Unity3D游戏学习笔记1
  5. C. 【UNR #2】黎明前的巧克力
  6. Day10-1.多态 、抽象类
  7. Java堆外内存泄露分析
  8. bios里IDE改AHCI-win7启用ahci蓝屏解决办法
  9. 直播带货直播陪玩直播约会的基础功能模块
  10. 笔记本win7系统屏幕亮度无法调节解决措施