Android 反射 - 通过 java 反射获取应用进程 Application 的实例化对象

本文详细探讨了Android中反射机制的应用,特别是在创建Application对象的过程。同时,深入分析了反射与new关键字的区别,以及Java泛型在集合中的作用与限制。通过实例,解释了动态加载与静态加载的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        反射 - 平时没使用过,但是最近在复习(划掉,学习) framework 东西的时候,发现应用进程 Application 在被实例化的时候,最终是在 Instrumentation.java 中调用如下方法去实例化(Android 8.1.0):

 

   /**
     * Perform instantiation of the process's {@link Application} object.  The
     * default implementation provides the normal system behavior.
     * 
     * @param clazz The class used to create an Application object from.
     * @param context The context to initialize the application with
     * 
     * @return The newly instantiated Application object.  // 最终方法会被调用到这里
     */
    static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }

 

        到这里才知道,原来Android 系统中,应用进程的 Application 对象的创建,是通过反射整出来的。并且自己写了一个例子,发现,在通过反射调用  (Coder) clazz.newInstance();  方法实例化对象的时候,是去调用了 Coder 的无参构造器去实例化了一个 Coder 对象。并且如果开发人员忘记写了 Coder 的无参构造器的情况下还去使用反射获取 Coder 实例化对象的时候会报错。具体尝试如下:

        try {
            Log.d("heguodong","----- begin -----");
            Class clazz = Class.forName("com.hegd.spinnerdemo.Coder");
            Coder coder = (Coder)clazz.newInstance();

            coder.setAge(18);
            coder.setName("HeGuodong");

            Log.d("heguodong","coder 的名字是:" + coder.getName() + " ; 年龄是:" +  coder.getAge());

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } finally {
            Log.d("heguodong","----- end -----");
        }

在 Coder 中添加无参构造器之后,日志打印:

07-27 17:11:00.928 32384-32384/com.hegd.spinnerdemo D/heguodong: ----- begin -----
07-27 17:11:00.929 32384-32384/com.hegd.spinnerdemo D/heguodong: coder 的名字是:HeGuodong ; 年龄是:18
07-27 17:11:00.929 32384-32384/com.hegd.spinnerdemo D/heguodong: ----- end -----

而如果在 Coder 中没写无参构造器的话,日志打印是:

07-27 17:12:40.999 32515-32515/? I/zygote: Late-enabling -Xcheck:jni
07-27 17:12:41.241 32515-32515/com.hegd.spinnerdemo D/heguodong: ----- begin -----
07-27 17:12:41.242 32515-32515/com.hegd.spinnerdemo W/System.err: java.lang.InstantiationException: java.lang.Class<com.hegd.spinnerdemo.Coder> has no zero argument constructor
07-27 17:12:41.242 32515-32515/com.hegd.spinnerdemo W/System.err:     at java.lang.Class.newInstance(Native Method)
07-27 17:12:41.243 32515-32515/com.hegd.spinnerdemo W/System.err:     at com.hegd.spinnerdemo.MainActivity.onCreate(MainActivity.java:49)
07-27 17:12:41.243 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.Activity.performCreate(Activity.java:7036)
07-27 17:12:41.243 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.Activity.performCreate(Activity.java:7027)
07-27 17:12:41.243 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
07-27 17:12:41.244 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
07-27 17:12:41.244 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
07-27 17:12:41.244 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.ActivityThread.-wrap11(Unknown Source:0)
07-27 17:12:41.244 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
07-27 17:12:41.245 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:106)
07-27 17:12:41.245 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.os.Looper.loop(Looper.java:164)
07-27 17:12:41.245 32515-32515/com.hegd.spinnerdemo W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6494)
07-27 17:12:41.245 32515-32515/com.hegd.spinnerdemo W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
07-27 17:12:41.246 32515-32515/com.hegd.spinnerdemo W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
07-27 17:12:41.246 32515-32515/com.hegd.spinnerdemo W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
07-27 17:12:41.246 32515-32515/com.hegd.spinnerdemo D/heguodong: ----- end -----

后来去慕课网恶补了一课,终于反射入门。

在 Android 中,万事万物都是对象(只有 静态的成员(属于某个类的)、普通的数据类型(包装类弥补了这点) 不是对象)。

那类是不是一个对象呢?类是一个对象,类是 java.lang.Class 的实例对象。  

那写的 Coder.java 这个类是不是一个对象呢?是的,它是 Class 类实例对象。Android 官方文档中管这个对象叫做 Coder  类的类类型。

那怎么去表示这个类对象?比如表示一下 Coder.java 的类对象:

private void test() {
        
        //Coder 的实例对象怎么表示
        Coder vach = new Coder("vach", 18);

        //Coder 这个类本身也是一个对象:是 Class 类的实例对象,并且有三种方式表示 Class 类的实例对象

        /**方式一*/
        Class<Coder> c1 = Coder.class;//这种表达方式也告诉我们了任何一个类都有一个隐含的静态成员变量class.

        /**方式二*/
        Class<? extends Coder> c2 = vach.getClass();//已知该类的对象,通过 getClass(); 方法获取到 Class 类的实例对象

        /*Note: Android 官网说 c1 、c2 表示了 Coder 类的类类型(Class type) */
        /*Note:并且打印 c1 == c2 ,发现他们是相等的,也证明了一个类有且只有一个  Class 类的实例对象*/

        /**方式三*/
        try {
            Log.d("heguodong","----- begin -----");
            Class<?> c3 = null;
            c3 = Class.forName("com.hegd.spinnerdemo.Coder");

            /*Note:并且打印 c3 == c2 ,发现他们完全相等 */
            /*Note:我们完全可以通过这个类类型 (c1/c2/c3 都可以) 获取到这个类的实例对象,但是注意做强制类型转换 */
            
            Coder coder1 = (Coder) c3.newInstance();//必须保证 Coder 类有无参构造器
            coder1.setAge(18);
            coder1.setName("HeGuodong");

            Log.d("heguodong","coder 的名字是:" + coder1.getName() + " ; 年龄是:" +  coder1.getAge());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } finally {
            Log.d("heguodong","----- end -----");
        }
    }

        另外知识点延伸,看到 Coder 类的实例对象,可以通过new 出来,还可以通过反射创建出来,那这两个对象到底有什么异同?这两种实例化对象的方式分别有什么限制条件,分别可以解决什么问题?

        是这样的,通过 new 的方式创建实例对象是类的静态加载,在编译时刻就需要加载可能用到的所有类。不管用不用到这个类的实例,只要是通过new 的方式创建对象,都需要在编译的时候去加载这个类。而通过反射的方式创建实例化对象的时候,是动态加载类,在运行时加载(所以反射创建对象,在编译的时候是不会报错加载不到类的,而如果代码中真的没有目标类,那么会在代码运行的时候报错  ClassNotFoundException )。功能性的类一般都是动态加载,而不是静态加载。

        再次知识点延伸,上面看到使用了泛型,那么泛型在集合中( 类 / 方法中)的本质是什么?

        泛型在集合中使用的本质:是在编译时限制往集合中放置数据的类型的。

        那就有意思了:通过做实验,两个集合,第一个集合不使用泛型,第二个集合使用泛型限制存放数据的类型:后来通过反射拿到这两个集合的类类型,发现他们是相等的。所以:java 集合中的泛型是防止数据错误传入的,只是在代码编译时对数据做限制操作。而我们又知道反射是在代码运行时操作,那我们岂不是可以通过反射越过泛型的限制 ?当然是可以的,但是一般没人这么做,没啥意义,因为当跨过泛型的限制向集合中添加一些乱七八糟类型的数据之后,集合中的数据就混乱了,代码中对集合数据循环遍历的时候就很麻烦。

        再次知识点延伸,说说对泛型的理解?

 

 

其他参考链接:

https://www.imooc.com/learn/199 (反射——Java高级开发)

https://www.cnblogs.com/yrstudy/p/6500982.html   (Java反射机制--是什么,为什么,怎么用。

 

 

 

<think>嗯,用户想了解在Android组件化项目中如何获取Application实例。首先,我需要回忆一下组件化的基本概念。组件化通常是将一个大型应用拆分成多个模块或组件,每个组件可以独立开发、测试和运行。这时候,每个组件可能是一个独立的模块,比如使用Android Library模块,或者动态特性模块。 接下来,Application的作用。在Android中,Application类是一个全局的单例,它在应用启动时初始化,用于保存全局的配置和状态。在传统的单模块项目中,直接通过context.getApplicationContext()或者自定义的Application类来获取实例是很简单的。但在组件化项目中,各个模块可能无法直接依赖主应用Application类,尤其是基础模块或者库模块,这时候如何获取Application实例就成了一个问题。 然后,用户的问题是如何在组件化项目中获取Application。可能的解决方案有几个:一种是通过反射,但反射效率低且不安全;另一种是通过接口暴露方法,比如在基础模块中定义一个接口,主应用实现该接口,然后通过工具类或者服务发现机制来获取Application实例。 记得以前看过一些文章,提到使用ContentProvider或者ARouter这样的路由框架来初始化模块,并在初始化时获取Application。例如,ARouter可以在初始化时传入Application,各模块通过ARouter的API来获取。或者,每个模块可以定义一个ContentProvider,在onCreate方法中获取Context,然后通过静态变量保存Application实例。不过ContentProvider的方式可能会增加启动时间,需要谨慎使用。 还有一种方法是依赖注入,比如使用Dagger或Hilt,将Application实例注入到需要的模块中。但这种方式可能需要模块之间有更多的依赖关系配置,尤其是在组件化中,模块可能不希望直接依赖其他模块的DI配置。 另外,可以考虑在基础模块中提供一个工具类,主应用在启动时调用该工具类的初始化方法,传入Application实例。其他模块通过这个工具类来获取Application。例如,创建一个ApplicationHolder类,里面有静态方法setApplication和getApplication。主应用Application类在onCreate时调用ApplicationHolder.setApplication(this),其他模块通过ApplicationHolder.getApplication()来获取实例。这种方法简单有效,但需要确保主应用正确初始化,且可能需要在多个模块中处理潜在的null情况。 不过要注意内存泄漏的问题,因为Application实例是全局的,通常不会导致内存泄漏,但在工具类中持有Context时还是要确保正确性。此外,跨模块的依赖需要确保基础模块被所有其他模块依赖,这样才能访问工具类。 可能还需要考虑模块的独立性,比如某些业务模块可能不希望直接依赖基础模块,这时候需要使用接口隔离,通过接口让主应用实现获取Application的方法,然后各模块通过接口来调用,而不是直接引用具体类。例如,基础模块定义一个接口AppContextProvider,主应用实现该接口,并在初始化时注册到基础模块的工具类中。其他模块通过工具类获取AppContextProvider实例,进而获取Application。这样解耦了模块之间的直接依赖,符合组件化的设计原则。 总结一下,常见的解决方案包括:工具类持有Application实例、接口暴露、使用路由框架或依赖注入,以及ContentProvider自动初始化。每种方法都有优缺点,需要根据项目具体情况选择。比如,如果项目已经使用了ARouter,那么利用其提供的API获取Application会更方便;如果项目结构简单,工具类可能是最快捷的方式。 另外,用户可能需要考虑模块如何初始化的问题。比如,某些基础库需要在Application的onCreate中初始化,这时候可能需要主应用显式调用模块的初始化方法,或者通过自动注册机制(如注解处理器或SPI)来收集所有模块的初始化入口,由主应用统一调用。这种情况下,获取Application实例可能和模块初始化流程相关。 最后,需要提醒用户注意Application获取时机,避免在Application的onCreate方法未完成时就尝试获取实例,导致空指针异常。同时,多进程应用中,Application实例在不同进程中的表现可能不同,需要特别处理。</think>在 Android 组件化项目中,由于模块化拆分后各组件可能独立运行(如 `library` 或动态特性模块),直接通过 `getApplication()` 或自定义 `Application` 类会遇到跨模块访问问题。以下是几种常见解决方案: --- ### 1. **基座模块 + 接口暴露** - **核心思想**:将 `Application` 实例通过基座模块(如 `base` 或 `common` 模块)提供的接口暴露给其他组件。 - **实现步骤**: 1. 在基座模块定义接口: ```kotlin interface IApplicationProvider { fun getApplication(): Application } ``` 2. 主工程(`app` 模块)实现接口并注册: ```kotlin class MainApplication : Application(), IApplicationProvider { override fun onCreate() { super.onCreate() // 注册到基座模块的工具类 AppContext.registerApplication(this) } override fun getApplication(): Application = this } ``` 3. 其他模块通过基座工具类获取: ```kotlin val app = AppContext.getApplication() ``` --- ### 2. **工具类静态持有** - **直接方式**:在基座模块中定义一个全局静态工具类,主工程初始化时注入 `Application`。 - **代码示例**: ```kotlin // 基座模块工具类 object AppContext { private var application: Application? = null fun init(application: Application) { this.application = application } fun getApplication(): Application { return application ?: throw IllegalStateException("Application未初始化!") } } // 主工程Application中初始化 class MainApplication : Application() { override fun onCreate() { super.onCreate() AppContext.init(this) } } // 其他模块调用 val context = AppContext.getApplication() ``` --- ### 3. **使用反射(不推荐)** - 通过反射主工程的 `Application` 类,但存在性能损耗和兼容性问题: ```kotlin val appClass = Class.forName("com.example.MainApplication") val app = appClass.getMethod("getInstance").invoke(null) as Application ``` --- ### 4. **依赖注入框架** - 使用 **Dagger/Hilt** 等框架将 `Application` 注入到需要的地方: ```kotlin // 在模块中声明依赖 @Module @InstallIn(SingletonComponent::class) object AppModule { @Provides fun provideAppContext(application: Application): Context = application } // 在需要的地方注入 @Inject lateinit var appContext: Context ``` -- ### 5. **使用 ContentProvider 自动初始化** - 利用 `ContentProvider` 的 `onCreate()` 方法在应用启动时自动获取 `Context`: ```kotlin class AppInitProvider : ContentProvider() { override fun onCreate(): Boolean { val application = context!!.applicationContext as Application AppContext.init(application) return true } // 其他空实现... } ``` - 需在 `AndroidManifest.xml` 中注册该 `Provider`。 --- ### 6. **路由框架(如 ARouter)** - 若已集成 ARouter,可在初始化时传递 `Application`,其他模块通过路由获取: ```kotlin // 初始化 ARouter.init(application) // 其他模块获取 val app = ARouter.getInstance().navigation(Application::class.java) ``` --- ### **注意事项** 1. **初始化时机**:确保在 `Application#onCreate()` 完成后再调用获取方法。 2. **空安全**:使用工具类时需处理未初始化时的异常(如结合 `checkNotNull`)。 3. **多进程**:跨进程时 `Application` 可能重新创建,需根据业务处理多进程逻辑。 选择方案时,**推荐优先使用接口暴露或工具类静态持有**,结合项目架构决定是否引入依赖注入或路由框架。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值