Android Application.java以及其作用

本文详细介绍了Android开发中Application组件的作用及其应用场景,包括如何利用Application进行数据传递和缓存,并探讨了潜在的内存泄漏问题及解决方案。

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

What is Application

Application和Activity,Service一样是android框架的一个系统组件,当android程序启动时系统会创建一个 application对象,用来存储系统的一些信息。通常我们是不需要指定一个Application的,这时系统会自动帮我们创建,如果需要创建自己 的Application,也很简单创建一个类继承 Application并在manifest的application标签中进行注册(只需要给Application标签增加个name属性把自己的 Application的名字定入即可)。

android系统会为每个程序运行时创建一个Application类的对象且仅创建一个,所以Application可以说是单例 (singleton)模式的一个类.且application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。因为它是全局 的单例的,所以在不同的Activity,Service中获得的对象都是同一个对象。所以通过Application来进行一些,数据传递,数据共享 等,数据缓存等操作。

 

Data passing between components using Application

假如有一个Activity A, 跳转到 Activity B ,并需要推荐一些数据,通常的作法是Intent.putExtra() 让Intent携带,或者有一个Bundle把信息加入Bundle让Intent推荐Bundle对象,实现传递。但这样作有一个问题在 于,Intent和Bundle所能携带的数据类型都是一些基本的数据类型,如果想实现复杂的数据传递就比较麻烦了,通常需要实现 Serializable或者Parcellable接口。这其实是Android的一种IPC数据传递的方法。如果我们的两个Activity在同一个 进程当中为什么还要这么麻烦呢,只要把需要传递的对象的引用传递过去就可以了。

基本思路是这样的。在Application中创建一个HashMap<String,Object> ,以字符串为索引,Object为value这样我们的HashMap就可以存储任何类型的对象了。在Activity A中把需要传递的对象放入这个HashMap,然后通过Intent或者其它途经再把这人索引的字符串传递给Activity B ,Activity B 就可以根据这个字符串在HashMap中取出这个对象了。只要再向下转个型 ,就实现了对象的传递。

Data caching in Application

我一般会习惯在application中建立两个HashMap<String,Object>一个用于数据的传递,一个用于缓 存一些数据。比如有一个Activity需要从网站获取一些数据,获取完之后我们就可以把这个数据cache到Application 当中,当页面设置到其它Activity再回来的时候,就可以直接使用缓存好的数据了。但如果需要cache一些大量的数据,最好是cache一些 (软引用)SoftReference ,并把这些数据cache到本地rom上或者sd卡上。如果在application中的缓存不存在,从本地缓存查找,如果本地缓存的数据也不存在再从网 络上获取。

PitFalls

使用Application如果保存了一些不该保存的对象很容易导致内存泄漏。如果在Application的oncreate中执行比较 耗时的操作,将直接影响的程序的启动时间。不些清理工作不能依靠onTerminate完成,因为android会尽量让你的程序一直运行,所以很有可能 onTerminate不会被调用。

MemoryLeak

在Java中内存泄漏是只,某个(某些)对象已经不在被使用应该被gc所回收,但有一个对象持有这个对象的引用而阻止这个对象被回收。比如我 们通常会这样创建一个View TextView tv = new TextView(this);这里的this通常都是Activity。所以这个TextView就持有着这个Activity的引用。下面看张图 (Google IO 2011 ppt中抄得)

Android笔记 <wbr>Application对象的使用-数据传递以及内存泄漏问题

通常情况下,当用户转动手机的时候,android会重新调用OnCreate()方法生成一个新的Activity,原来的 Activity应该被GC所回收。但如果有个对象比如一个View的作用域超过了这个Activity(比如有一个static对象或者我们把这个 View的引用放到了Application当中),这时候原来的Activity将不能被GC所回收,Activity本身又持有很多对象的引用,所以 整个Activity的内存被泄漏了。

经常导致内存泄漏的一些原因:

keeping a long-lived reference to a Context.持有一个context的对象,从而gc不能回收。

1,一个View,的作用域超出了所在的Activity的作用域,比如一个static的View或者 把一个View cache到了application当中 etc

2,某些与View关联的Drawable的作用域超出了Activity的作用域。

3,Runnable对象:比如在一个Activity中启用了一个新线程去执行一个任务,在这期间这个Activity被系统回收了, 但Runnalbe的任务还没有执行完毕并持有Activity的引用而泄漏,但这种泄漏一般来泄漏一段时间,只有Runnalbe的线程执行完闭,这个 Activity又可以被正常回收了。

4,内存类的对象作用域超出Activity的范围:比如定义了一个内存类来存储数据,又把这个内存类的对象传给了其它Activity 或者Service等。因为内部类的对象会持有当前类的引用,所以也就持有了Context的引用。解决方法是如果不需要当前的引用把内部类写成 static或者,把内部类抽取出来变成一个单独的类,或者把避免内部对象作用域超出Activity的作用域。

out Of Memery Error 在android中每一个程序所分到的内存大小是有限的,如果超过了这个数就会报Out Of Memory Error。android给程序分配的内存大小与手机硬件有关,以下是一些手机的数据:

G1:16M Droid:24 Nexus One:32M Xoom:48Ms

所以尽量把程序中的一些大的数据cache到本地文件。以免内存使用量超标。

Snippets

1,通过Application在两个Activity间传递数据

Android笔记 <wbr>Application对象的使用-数据传递以及内存泄漏问题  Android笔记 <wbr>Application对象的使用-数据传递以及内存泄漏问题

记得数据传递完成之后,把存放在application的HashMap中的数据remove掉,以免发生内存的泄漏。

 

 

其实我们开发的每个android应用程序就是一个Appliction,定义这个类往往是在AndroidManifes.xml中用到。例如:

 

<application android:icon="@drawable/icon"
      android:label="@string/app_name"
      android:name=".MyApplication">
 
这里定义了我们整个应用程序的属性,例如名称和图标。
我们可以自定义Appliction,例如:
public class MyApplication extends Application {
  }
就是这儿,将我们以前一直用的默认Application给他设置成我们自己做的MyApplication
MyApplication类的作用是为了放一些全局的和一些上下文都要用到变量和方法之类的。
分析报错信息: FATAL EXCEPTION: main Process: com.example.myapplication, PID: 7164 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.myapplication/com.example.myapplication.PhoneActivity}: java.lang.RuntimeException: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ArrayAdapter.notifyDataSetChanged()' on a null object reference at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:223) at android.app.ActivityThread.main(ActivityThread.java:7656) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) Caused by: java.lang.RuntimeException: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ArrayAdapter.notifyDataSetChanged()' on a null object reference at com.example.myapplication.PhoneActivity.readContacts(PhoneActivity.java:53) at com.example.myapplication.PhoneActivity.onCreate(PhoneActivity.java:33) at android.app.Activity.performCreate(Activity.java:7994) at android.app.Activity.performCreate(Activity.java:7978) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)  at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)  at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)  at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)  at android.os.Handler.dispatchMessage(Handler.java:106)  at android.os.Looper.loop(Looper.java:223)  at android.app.ActivityThread.main(ActivityThread.java:7656)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)  Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ArrayAdapter.notifyDataSetChanged()' on a null object reference at com.example.myapplication.PhoneActivity.readContacts(PhoneActivity.java:50) at com.example.myapplication.PhoneActivity.onCreate(PhoneActivity.java:33)  at android.app.Activity.performCreate(Activity.java:7994)  at android.app.Activity.performCreate(Activity.java:7978)  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)  at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)  at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)  at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)  at android.os.Handler.dispatchMessage(Handler.java:106)  at android.os.Looper.loop(Looper.java:223)  at android.app.ActivityThread.main(ActivityThread.java:7656)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)  2025-07-10 10:40:05.447 7164-7180 System com.example.myapplication W A resource failed to call close.
最新发布
07-11
<think>我们正在解决一个特定的空指针异常问题:`java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ArrayAdapter.notifyDataSetChanged()' on a null object reference` 这个异常表明我们在一个null对象上调用了`notifyDataSetChanged()`方法。也就是说,我们尝试调用`ArrayAdapter`的`notifyDataSetChanged()`时,这个`ArrayAdapter`对象是null。 根据问题描述,我们需要定位并解决ArrayAdapter为null导致的崩溃问题。下面将逐步分析原因并提供解决方案。 ### 问题原因分析 1. **ArrayAdapter未初始化**:在使用`ArrayAdapter`之前,必须通过`new ArrayAdapter(...)`进行初始化。如果忘记初始化,则变量为null。 2. **初始化时机不当**:可能在初始化之前就调用了`notifyDataSetChanged()`。 3. **作用域问题**:可能在一个方法中初始化了`ArrayAdapter`,但在另一个方法中调用时,该变量未被正确传递或赋值。 4. **生命周期事件**:在配置变更(如旋转屏幕)后,如果没有重新初始化,可能会导致`ArrayAdapter`变为null。 ### 解决方案步骤 #### 1. 确保正确初始化ArrayAdapter 在创建ArrayAdapter时,需要传入上下文、布局资源以及数据源。例如: ```java ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, dataList); ``` 然后,将适配器设置给ListView或Spinner: ```java listView.setAdapter(adapter); ``` 注意:确保在调用`notifyDataSetChanged()`之前,`adapter`已经初始化。 #### 2. 检查调用notifyDataSetChanged()的时机 在调用`adapter.notifyDataSetChanged()`之前,检查`adapter`是否为null: ```java if (adapter != null) { adapter.notifyDataSetChanged(); } else { // 处理未初始化的情况,例如重新初始化适配器 } ``` #### 3. 确保数据更新后调用notifyDataSetChanged() 当数据源(如List)发生变化时,需要调用`notifyDataSetChanged()`来刷新视图。但前提是适配器已经初始化。 #### 4. 处理配置变更 如果Activity因配置变更(如屏幕旋转)而重建,那么成员变量会被重新初始化。如果适配器是Activity的成员变量,则需要在`onCreate`中重新初始化。 为了避免每次配置变更都重新初始化,可以考虑在AndroidManifest.xml中为Activity配置`android:configChanges`,但这通常不是推荐做法。更常见的做法是正确保存和恢复状态。 #### 5. 使用单例或保留Fragment(谨慎使用) 如果数据需要跨配置变更保存,可以考虑使用ViewModel(Android Architecture Components)或保留Fragment。但注意,适配器通常与视图相关,不建议跨配置变更保存适配器,而是重新创建。 ### 示例代码 下面是一个正确的示例: ```java public class MainActivity extends AppCompatActivity { private ArrayAdapter<String> adapter; private List<String> dataList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化适配器 adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, dataList); ListView listView = findViewById(R.id.listView); listView.setAdapter(adapter); // 添加一些初始数据 dataList.add("Item 1"); dataList.add("Item 2"); adapter.notifyDataSetChanged(); } // 在某个事件中更新数据 private void addItem(String newItem) { dataList.add(newItem); // 在调用之前检查适配器是否为空(虽然在这个例子中onCreate已经初始化,但在其他地方调用时可能还未初始化) if (adapter != null) { adapter.notifyDataSetChanged(); } } } ``` ### 常见错误场景 1. **在初始化适配器之前调用notifyDataSetChanged**:例如,在onCreate中,如果初始化适配器的代码在调用notifyDataSetChanged之后,则会导致空指针。 2. **在Fragment的onCreateView中未初始化适配器**:在Fragment中,我们通常在onCreateView中初始化视图和适配器。如果在其他方法(如网络请求回调)中调用notifyDataSetChanged,而Fragment视图还未创建(适配器未初始化),则会导致空指针。 ### 相关问题 1. 如何避免在Android中发生空指针异常? 2. 如何在ListView更新数据时避免出现空指针? 3. ArrayAdapter和BaseAdapter有什么区别? 4. 在Fragment中如何正确初始化适配器? 5. 使用RecyclerView时出现空指针异常如何解决?[^1]</think>### 🔍 Android ArrayAdapter空指针异常分析及解决 #### ⚠️ 问题根源分析 当出现 `java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ArrayAdapter.notifyDataSetChanged()' on a null object reference` 错误时,核心原因是**在未正确初始化 ArrayAdapter 的情况下调用了其方法**。具体表现为: ```java // 错误示例:adapter 未初始化就被使用 arrayAdapter.notifyDataSetChanged(); // 当 arrayAdapter 为 null 时崩溃 ``` #### 🔧 解决方案步骤 1. **确保正确初始化 ArrayAdapter** ```java // 在 Activity/Fragment 中声明成员变量 private ArrayAdapter<String> arrayAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // ✅ 正确初始化(关键步骤) arrayAdapter = new ArrayAdapter<>( this, android.R.layout.simple_list_item_1, new ArrayList<String>() // 初始空列表 ); ListView listView = findViewById(R.id.listView); listView.setAdapter(arrayAdapter); // 绑定适配器 } ``` 2. **添加空值检查防御** ```java // 在调用适配器方法前进行检查 public void updateData(List<String> newData) { if (arrayAdapter != null) { arrayAdapter.clear(); arrayAdapter.addAll(newData); arrayAdapter.notifyDataSetChanged(); // 安全调用 } else { Log.e("TAG", "ArrayAdapter 未初始化!"); // 可在此处重新初始化适配器 } } ``` 3. **处理生命周期变化** ```java // 在配置变更后重新初始化 @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); // 检查是否需要重新初始化 if (arrayAdapter == null) { arrayAdapter = new ArrayAdapter<>(...); listView.setAdapter(arrayAdapter); } } ``` 4. **使用 ViewModel 保持数据 (推荐)** ```java // 在 ViewModel 中管理适配器数据 public class MyViewModel extends ViewModel { private MutableLiveData<List<String>> listData = new MutableLiveData<>(); public void updateList(List<String> newItems) { listData.setValue(newItems); } } // Activity 中观察数据变化 viewModel.getListData().observe(this, items -> { if (arrayAdapter != null) { arrayAdapter.clear(); arrayAdapter.addAll(items); arrayAdapter.notifyDataSetChanged(); } }); ``` #### 🛡️ 预防措施 1. **初始化顺序检查**: - 确保在 `onCreate()` 或 `onCreateView()` 中完成适配器初始化 - 避免在构造方法中初始化(此时视图未加载) 2. **组件生命周期同步**: ```java // 在 Fragment 的 onViewCreated 中初始化 @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); arrayAdapter = new ArrayAdapter<>(...); listView.setAdapter(arrayAdapter); } ``` 3. **使用 Kotlin 安全调用** (Kotlin 项目): ```kotlin // 安全调用操作符 ?. arrayAdapter?.notifyDataSetChanged() ``` 4. **单元测试验证**: ```java @Test public void testAdapterInitialization() { Activity activity = Robolectric.setupActivity(MainActivity.class); ListView listView = activity.findViewById(R.id.listView); assertNotNull("适配器未初始化", listView.getAdapter()); } ``` #### 💡 典型错误场景修复 ```java // 错误场景:异步回调中直接使用适配器 networkRequest.getData(new Callback() { @Override public void onSuccess(List<String> data) { // ❌ 风险:可能在适配器初始化前被调用 arrayAdapter.addAll(data); arrayAdapter.notifyDataSetChanged(); } }); // ✅ 修复方案:添加空检查+延迟执行 networkRequest.getData(data -> { if (arrayAdapter == null) { // 延迟到主线程初始化 runOnUiThread(() -> initAdapter(data)); } else { arrayAdapter.addAll(data); arrayAdapter.notifyDataSetChanged(); } }); ``` #### 📚 相关问题 1. ListView 和 RecyclerView 在适配器使用上有何区别?[^3] 2. 如何在 Android ViewModel 中正确管理适配器数据? 3. 配置变更(如屏幕旋转)导致适配器为 null 如何预防? 4. Kotlin 的空安全特性如何帮助避免此类异常? 5. 单元测试中如何模拟和验证适配器初始化?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值