修改Activity的继承类导致程序闪退

本文记录了一次老项目重构过程中遇到的问题:将Activity替换为AppCompatActivity后应用启动时出现闪退现象。通过日志发现该问题与主题资源文件有关。

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

今天对老项目重构,重新写了BaseActivity。其中有一处改动是把原来的父类Activity改成了AppCompatActivity。

这一处的改动导致了程序在启动的时候直接闪退。

查看log输出,发现是跟主题资源文件有关。

package com.example.locationtest; import android.Manifest; import android.annotation.SuppressLint; import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Log; import android.widget.TextView; import android.widget.Toast; import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import com.baidu.location.BDAbstractLocationListener; import com.baidu.location.BDLocation; import com.baidu.location.BDLocationListener; import com.baidu.location.LocationClient; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity{ public LocationClient mlocationClient; private TextView positiontext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); try { mlocationClient = new LocationClient(getApplicationContext()); } catch (Exception e) { throw new RuntimeException(e); } mlocationClient.registerLocationListener(new MyLocationListner()); Log.d("MapLocation", "error"); setContentView(R.layout.activity_main); positiontext = (TextView)findViewById(R.id.position_view); List<String> permissonlist = new ArrayList<>(); if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED){ permissonlist.add(Manifest.permission.ACCESS_FINE_LOCATION); } if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_PHONE_STATE)!= PackageManager.PERMISSION_GRANTED){ permissonlist.add(Manifest.permission.READ_PHONE_STATE); } if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){ permissonlist.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); } if(!permissonlist.isEmpty()){ String [] permislist=permissonlist.toArray(new String[permissonlist.size()]); ActivityCompat.requestPermissions(MainActivity.this,permislist,1); } else { requestlocation(); } } @SuppressLint("MissingSuperCall") @Override public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults){ //super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode){ case 1: if(permissions.length>0){ for(int result:grantResults){ if(result!=PackageManager.PERMISSION_GRANTED){ Toast.makeText(MainActivity.this,"impermission!!",Toast.LENGTH_LONG).show(); finish(); return; } } requestlocation(); } else{ Toast.makeText(MainActivity.this,"Error!!",Toast.LENGTH_LONG).show(); finish(); } break; default: } } private void requestlocation(){ mlocationClient.start(); } public class MyLocationListner extends BDAbstractLocationListener { @Override public void onReceiveLocation(BDLocation bdLocation) { StringBuffer currentposition = new StringBuffer(); currentposition.append("latitude:").append(bdLocation.getLatitude()).append("\n"); currentposition.append("longitude:").append(bdLocation.getLongitude()).append("\n"); currentposition.append("locationtype:"); if(bdLocation.getLocType()==BDLocation.TypeGpsLocation){ currentposition.append("gps"); } else if(bdLocation.getLocType()==BDLocation.TypeNetWorkLocation){ currentposition.append("network"); } positiontext.setText(currentposition); } } }代码运行,手机app直接闪退
最新发布
07-12
<think>我们正在解决的是在Android中使用`mHandler.post()`时导致程序闪退的问题。根据之前的讨论,我们知道Handler与Looper密切相关,尤其是在非主线程中使用Handler时,需要确保线程有Looper并且已经循环起来。同时,也要注意内存泄漏问题,比如Handler持有Activity的引用导致无法被回收,以及Handler在Activity销毁后仍然被调用导致空指针等问题。常见的导致crash的原因及解决方案:1.**没有为当前线程准备Looper**:在非主线程中直接使用Handler的post方法,而该线程没有Looper。解决:在非主线程中使用Handler前,先调用`Looper.prepare()`和`Looper.loop()`,或者使用`Handler(Looper.getMainLooper())`来确保使用主线程的Looper。2.**内存泄漏**:Handler作为内部持有了外部Activity的引用,当Activity销毁后,Handler仍然被消息队列持有,导致Activity无法被回收,进而可能引起内存溢出(OOM)而崩溃。解决:使用静态内部+弱引用的方式,并在Activity的`onDestroy()`中移除所有回调。3.**在Activity销毁后仍然更新UI**:Activity已经被销毁,但Handler中的任务试图更新UI,导致空指针异常。解决:在Activity的`onDestroy()`中移除Handler的所有回调和消息。4.**主线程阻塞**:在主线程中执行耗时操作,导致ANR(程序无响应)而崩溃。虽然这不会立即闪退,但也是崩溃的一种。注意:Handler的post方法会将任务放入主线程队列,如果任务本身耗时,也会导致ANR。因此,在post的任务中不能做耗时操作。5.**空指针异常**:如果post的Runnable中访问了可能为空的对象,例如未初始化的View或者已经销毁的Activity的成员变量。解决:在访问对象前进行非空判断。我们分别来看:###1.确保Looper存在如果你在非主线程中使用Handler,需要先调用`Looper.prepare()`和`Looper.loop()`,例如:```javanewThread(newRunnable(){@Overridepublicvoidrun(){Looper.prepare();//为该线程创建LooperHandlerhandler=newHandler();//现在可以在这个线程中使用handler.post了Looper.loop();//启动消息循环}}).start();```但通常我们更倾向于使用主线程的Handler,因为更新UI必须在主线程。所以,建议这样创建Handler:```java//使用主线程的Looper创建HandlerprivateHandlermHandler=newHandler(Looper.getMainLooper());```###2.避免内存泄漏将Handler定义为静态内部,并通过弱引用持有Activity:```javaprivatestaticclassMyHandlerextendsHandler{privatefinalWeakReference<MyActivity>mActivity;MyHandler(MyActivityactivity){super(Looper.getMainLooper());mActivity=newWeakReference<>(activity);}@OverridepublicvoidhandleMessage(Messagemsg){MyActivityactivity=mActivity.get();if(activity!=null){//处理消息,注意要检查activity是否为空}}}```在Activity中,在`onDestroy()`里移除消息:```java@OverrideprotectedvoidonDestroy(){super.onDestroy();mHandler.removeCallbacksAndMessages(null);}```注意:如果Handler是发送Runnable,同样需要在销毁时移除:```java//假设我们有一个RunnableprivateRunnablemyRunnable=newRunnable(){@Overridepublicvoidrun(){//更新UI}};//在Activity销毁时移除@OverrideprotectedvoidonDestroy(){super.onDestroy();mHandler.removeCallbacks(myRunnable);}```###3.在onDestroy中移除回调在Activity或Fragment销毁时,确保移除所有Handler的回调,避免在销毁后调用。###4.不在UI线程执行耗时操作如果post的任务是耗时的,应该将其放到后台线程中执行,例如使用AsyncTask或线程池,然后在需要更新UI时再使用Handler。###5.避免空指针在Runnable中访问Activity的成员或View时,先检查Activity是否被销毁:```javamHandler.post(newRunnable(){@Overridepublicvoidrun(){if(!isFinishing()&&!isDestroyed()){//更新UI}}});```注意:`isFinishing()`和`isDestroyed()`是Activity的方法,用于检查Activity是否正在结束或已经被销毁。另外,根据引用[4]的内容,我们可以利用`removeCallback`来移除Runnable,以避免在不需要的时候执行。结合引用[2]中的示例,我们可以看到在onCreate中使用postDelayed发送了一个延迟任务,然后在Activity退出时(onDestroy)需要移除这个延迟任务,否则当Activity销毁后任务被执行,可能会访问已经不存在的View。因此,总结解决方案:1.确保Handler使用正确的Looper(主线程使用`Looper.getMainLooper()`,非主线程确保已调用`Looper.prepare()`和`Looper.loop()`)。2.使用静态内部+弱引用的方式定义Handler,避免内存泄漏。3.在Activity的`onDestroy()`方法中移除所有Handler的回调和消息。4.在Runnable中执行代码时,检查Activity是否已经销毁。5.避免在Handler的post中执行耗时操作。示例代码:在Activity中:```javaprivateHandlermHandler;privateRunnablemRunnable;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//使用主线程的Looper创建HandlermHandler=newHandler(Looper.getMainLooper());mRunnable=newRunnable(){@Overridepublicvoidrun(){if(!isFinishing()&&!isDestroyed()){//更新UITextViewtextView=findViewById(R.id.textView);textView.setText("更新文本");}}};//延迟执行mHandler.postDelayed(mRunnable,1000);}@OverrideprotectedvoidonDestroy(){super.onDestroy();//移除所有回调和消息mHandler.removeCallbacks(mRunnable);//或者移除所有消息和回调//mHandler.removeCallbacksAndMessages(null);}```这样处理可以避免大部分因Handler导致闪退问题。如果仍然遇到问题,可以检查是否在非主线程中直接创建了Handler(没有指定Looper)并且该线程没有Looper。这种情况下会抛出异常:`Can'tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()`。因此,请确保在非主线程中使用Handler时,为该线程准备了Looper,或者使用主线程的Looper。最后,引用[1]中提到关于AlertDialog的内存泄漏问题,也提醒我们在使用一些系统组件时要注意释放资源,同样Handler也存在似问题,需要我们在Activity销毁时移除回调。希望这些解决方案能帮助你解决问题。§§相关问题§§1.如何正确使用HandlerThread?2.什么是ANR?如何避免?3.如何检测Android中的内存泄漏?4.Handler的post方法和postDelayed方法有什么区别?5.为什么在Activity销毁后,Handler中的任务仍然会被执行?</think> ### Android 中使用 `mHandler.post()` 导致程序闪退的解决方案 在 Android 中使用 `mHandler.post()` 导致闪退通常与**线程管理**、**内存泄漏**或**空指针异常**有关。以下是常见原因及解决方案,结合站内引用优化代码: --- ### 核心原因与解决方案 #### 1. **未正确绑定主线程 Looper(线程问题)** - **问题**:在非主线程中使用 Handler 未指定 `Looper.getMainLooper()`,导致无法更新 UI。 - **解决方案**:确保 Handler 绑定主线程 Looper: ```java // ✅ 正确:使用主线程 Looper private Handler mHandler = new Handler(Looper.getMainLooper()); ``` - **错误示例**: ```java // ❌ 错误:普通 Handler 可能绑定后台线程 private Handler mHandler = new Handler(); ``` #### 2. **内存泄漏导致空指针** - **问题**(引用[1][2][4]):Handler/Runnable 持有 Activity 引用,当 Activity 销毁后仍被调用,导致空指针闪退。 - **解决方案**: - **在 `onDestroy()` 移除所有回调**: ```java @Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); // 移除所有回调 // 如果是自定义 Runnable,直接移除它 mHandler.removeCallbacks(yourRunnable); } ``` - **使用弱引用避免内存泄漏**(引用[4]): ```kotlin private val yourRunnable = Runnable { // 检查 Activity 是否存活 if (!isFinishing) { // 更新 UI 的代码 } } ``` #### 3. **任务执行时 Activity 已销毁** - **问题**(引用[2]):延迟任务执行时 Activity 已被销毁。 - **解决方案**:在 Runnable 中检查 Activity 状态: ```java mHandler.postDelayed(() -> { if (!isFinishing() && !isDestroyed()) { // 检查 Activity 状态 updateUI(); // 安全更新 UI } }, 2000); ``` #### 4. **耗时操作阻塞主线程** - **问题**:在 `post()` 中执行耗时操作导致 ANR。 - **解决方案**: - 耗时操作使用后台线程: ```java mHandler.post(() -> { // 仅处理 UI 更新 runOnUiThread(() -> updateUI()); }); ``` --- ### 完整示例代码 ```java private Handler mHandler = new Handler(Looper.getMainLooper()); private Runnable updateTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 初始化 Runnable(弱引用 Activity) updateTask = () -> { if (!isFinishing()) { ((TextView) findViewById(R.id.tv_message)).setText("更新成功"); } }; // 发送延迟任务 mHandler.postDelayed(updateTask, 1000); } @Override protected void onDestroy() { super.onDestroy(); // 移除所有回调(关键!) mHandler.removeCallbacks(updateTask); } ``` --- ### 关键优化点总结 1. **强制绑定主线程**:`new Handler(Looper.getMainLooper())` 2. **及时清理资源**:在 `onDestroy()` 中调用 `removeCallbacks()`[^4] 3. **状态检查**:在 Runnable 中通过 `isFinishing()` 验证 Activity 状态 4. **避免内存泄漏**:使用静态内部+弱引用(参考引用[1]的 Dialog 内存泄漏方案) --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值