android中能不能new Activity()对象引发的思考

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/smileiam/article/details/88686637

前几天去某公司面试,被面试官问到能不能直接new一个Activity实例,从来没有这么用过,当时第一反应是不能吧,想着如果Android中直接new Activity()实例,那不是乱套了么,系统就管理不到Activity了啊。面试官又问Android中怎么创建Activity对象的呢,当时傻里吧唧的回答反射。真是too young too naive。

其实说白了Activity也只是一个普通类,它的父类的父类的父类...也是Object,当然可以通过new来创建一个Activity对象,只是Android系统中是通过类加载器的newInstance()方法来创建Activity实例的。Android系统为了便于管理Activity,回调其生命周期,把Activity实例化相关操作都封装好了,我们只要轻松调用startActivity操作,就能实现Activity中各种生命周期的回调了。

那问题又来了:

1、newInstance()和new创建对象有什么区别,android系统为什么要用类加载器来创建Activity的实例呢?

2、自己创建的Activity实例能直接调用Activity的生命周期方法吗?调用onCreate()方法能启动Activity吗?

3、在Activity中定义名为test方法,在里边弹出一个Toast,通过自己创建的Activity实例调用test()方法,能弹出toast吗?

回答上面问题前,我们先看看Android是怎么启动一个Activity的。虽然我上面已经给出了答案,是通过类加载器调用newInstance()方法来创建Activity实例的,但如果再问深点:

4、Android中是在哪个类中进行创建Activity的实例的?

5、启动新的Activity时,会经历哪些步骤?

所以我们还是得知道当我们调用startActivity()时,系统背后的调用链是怎样的,具体的源码我就不贴了,网上有一大堆,我们主要分析调用链,如下:

1、Android启动一个新Activity的调用链

1) Activity.startActivity(Intent)
2) -->Activity.startActivityForResult(Intent intent, int requestCode)
3) --> Instrumentation.execStartActivity(Context context, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode)
4) -->ActivityManagerNative.getDefault().startActivity()
//就是调用ActivityManagerProxy.startActivity,跨进程,ActivityManagerProxy是ActivityManagerService的代理类
5) -->ActivityManagerService.startActivity()-->startActivityMayWait()

//由ActivityStack来检查要启动的Activity,并且组装成一个ActivityRecord,将要启动它的组件先pause掉
6) -->ActivityStack.startActivityLocked()-->startActivityUncheckedLocked()-->resumeTopActivityLocked()-->startPausingLocked()
7) -->ApplicationThreadProxy.schedulePauseActivity()
//跨进程通信,AMS与应用进程通信,ApplicationThreadProxy是ApplicationThread的代理类
8) -->ApplicationThread.schedulePauseActivity()
9) -->ActivityThread.queueOrSendMessage()
10) -->H.handleMessage
11) -->ActivityThread.handlePauseActivity //将上一个Activity pause掉

12 -->ActivityManagerProxy.activityPaused() //告诉AMS上一个Activity已经paused掉了
13) -->ActivityManagerService.activityPaused()

//由ActivityStack来启动新的Activity, 这里的14-19步和上面的6-11步类似,都是由SystemServer的AMS控制应用进程启动(start)或是暂停(pause)组件(Activity)
14) -->ActivityStack.completePauseLocked()-->resumeTopActivityLocked()-->startSpecificActivityLocked()-->realStartActivityLocked()
15) -->ApplicationThreadProxy.scheduleLaunchActivity()
//跨进程通信,AMS与应用进程通信,ApplicationThreadProxy是ApplicationThread的代理类
16) -->ApplicationThread.scheduleLaunchActivity()
17) -->ActivityThread.queueOrSendMessage()
18) -->H.handleMessage
19) -->ActivityThread.handleLaunchActivity //启动要启动的Activity

20) -->ActivityThread.performLauchActivity(ActivityClientRecord r, Intent customIntent)
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();//获取类的加载器
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
21) -->Instrumentation.newActivity   //关键之处:Activity的实例是通过类加载器创建出来的
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
     return (Activity)cl.loadClass(className).newInstance();
}
22) -->newActivity.onCreate()//回调新启动Activity的onCreate()方法

从上面的调用链我们可以知道Android系统是在Instrumentation类中创建Activity的实例的,Instrumentation是系统与应用交互前的捕捉类,可以在这里进行监控应用与系统的交互操作。我们与系统交互一般是通过Activity来进行的。可以看到Instrumentation是通过类加载器的newInstance()方法来创建Activity实例的。

2、newInstance()与new创建对象的区别

newInstance()使用类加载器,无论类名怎么变,只要它们继承同一个接口,都可以创建出类的实例。但使用newInstance()方法必须保证这个类已经被加载,这个类已经连接了。上面的loadClass(className)就启动类加载器加载该类。使用class的静态加载方法loadClass能更灵活的加载类,不管类怎么变,都能实例化出类对象,更大程度解耦。不过它只能调用类的无参构造函数。

而new创建一个新类时,这个类可以没有被加载,只能调用public构造方法,但对类强耦合,如果类变化了,new相应的类名也要跟着变化。

这样也能回答Android系统中为什么要用newInstance()来实例化Activity了,为了解耦。

3、自己创建Activity实例,能调用其生命周期方法么?

为了验证,我新建了两个Activity,一个是MainActivity,一个是OneActivity,在MainActivity中创建一个Button,它的android:onClick="startOne",即点击Button执行startOne方法。

//MainActivity类代码
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void startOne(View view) {
        OneActivity activity = new OneActivity();//直接用new方法创建OneActivity的实例
        activity.onCreate(null);//调用Activity的onCreate()方法
    }
}

//OneActivity类代码
public class OneActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_one);//注释1
        Log.e("OneActivity", "onCreate");
    }

程序能正常运行不会报错,这也验证了能直接new Activity()来创建Activity实例的,那这样能启动Activity么,发现执行startOne()方法后,程序异常,报错如下:

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.Window$Callback android.view.Window.getCallback()' on a null object reference
        at android.support.v7.app.AppCompatDelegateImpl.<init>(AppCompatDelegateImpl.java:249)
        at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:182)
        at android.support.v7.app.AppCompatActivity.getDelegate(AppCompatActivity.java:520)
        at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:71)
        at com.example.testapplication.OneActivity.onCreate(OneActivity.java:12)
        at com.example.testapplication.MainActivity.startOne(MainActivity.java:21)

很明显是Window为空,即使我们把上面的注释1代码去掉,不给OneActivity类设置布局也会报错,其实了解Activity布局的人都知道,setContentView也只是给Window的子组件设置布局,

è¿éåå¾çæè¿°

只要调用了super.onCreate(savedInstanceState);就会给Activity添加窗口,而我们没有靠系统给Activity创建窗口,调用了super.onCreate(savedInstanceState)方法,必然会用到窗口,那肯定会报错的。同理也能回答上面的第3个问题,弹出Toast也是在Window上addView的,由于Window不存在,所以也会报错。

如果只调用Acitivity跟View不相关的方法,则能正常运行,这其实和普通类的普通方法没什么区别。

上面的第4问和第5问其实通过调用链就能回答了,第5问,主要涉及到前一个Activity pause,后一个Activity onCreate方法,这其中又迁扯到ActivityManagerProxy->ActivityManagerService之间跨通信,为什么同一个应用中启动Activity也要用到跨进程通信呢?

4、为什么同一个应用中启动Activity也要用到跨进程通信

因为Android需要统一管理所有Activity及其生命周期回调,Android支持跨进程启动其他应用的Activity,如果发现要启动的Activity组件的进程不存在是,会由AMS所在的SystemServer进程触发Zgote进程,fork出一个子进程作为新的Activity组件的进程。AMS为了统一管理,不管是不是同一个进程中的Activity组件启动,都要到AMS中,告诉它要进行什么操作。

5、启动一个新的Activity至少要触发多少次跨进程通信

其实从引用链上也可以看出来

第一次:上面的第4)步ActivityManagerProxy.startActivity()->ActivityManagerService.startActivity() 应用进程告诉AMS我要启动新的Acitvity组件;

第二次:上面的第7)步ApplicationThreadProxy.schedulePauseActivity()->ApplicationThread.schedulePauseActivity() AMS通知应用进程将前一个Activity组件pause掉;

第三次:上面的第12)步 ActivityManagerProxy.activityPaused() -->ActivityManagerService.activityPaused() 应用进程通知AMS上一个Activity已经paused掉了

第四次:上面的第15)步-->ApplicationThreadProxy.scheduleLaunchActivity()-->ApplicationThread.scheduleLaunchActivity()
AMS通知应用进程通信启动新的Activity组件

这里要注意如果启动的其他进程的组件,还要多一次跨进程通信的,AMS通知Zgote进程fork出一个子进程出来。

所以启动一个新的Activity组件至少要四次跨进程通信

6、为什么在ActivityThread回调应用生命周期方法需要来回发送Message
 

从上面的调用链上9) -->ActivityThread.queueOrSendMessage()
10) -->H.handleMessage
11) -->ActivityThread.handlePauseActivity //将上一个Activity pause掉

和17) -->ActivityThread.queueOrSendMessage()
18) -->H.handleMessage
19) -->ActivityThread.handleLaunchActivity //启动要启动的Activity

为什么同一个进程中pause或是create Activity组件要来回发消息呢,为什么不直接调用组件的生命周期方法呢?因为跨进程通信有单独的线程Binder线程池,Binder线程池主要用来处理组件间的跨进程通信请求的,如果在Binder线程中去启动Activity的生命周期方法,可能会阻塞Binder线程,导致Binder线程无法立马响应其他进程间通信请求。另外Activity生命周期方法可能涉及到用户界面相关操作,而UI线程不是线程安全的,所以需要放到主线程中执行了。这样就涉及到Binder线程与主线程之间通信,发送Message到主线程。让主线程进行Activity的生命周期回调。

有时面试官问的问题是有点奇葩,但这也能考察一个人对某个知识点掌握的深不深入了。

给大家一个思考题,也是当天面试官问我的:

关于GC的,如果一个对象A持有另外一个对象B的强引用,对象A会不会被垃圾回收器回收到(B GCRoots可达)?

猜你喜欢

转载自blog.csdn.net/smileiam/article/details/88686637
今日推荐