一、导致ANR的几个场景
1. Activity主线程不处理耗时操作,必须新起线程: 耗时操作在UI线程会导致UI响应阻塞,导致ANR;
public static class MyAsyncTask extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... params) {...}
@Override
protected void onPostExecute(String result) {
showTv.setText("主线程显示UI");
}
}
2. Service、BroadcastReceiver中耗时操作必须在工作线程:Service和BroadcastReceiver工作在主线程,耗时操作需要异步处理,短暂耗时操作可以用IntentService执行;
public class MyService extends IntentService {
....
@Override
public void onCreate() {
super.onCreate();
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
...
doHeavyOperation(); // 运行在工作线程。
}
}
3. 多线程死锁导致ANR:使用相同顺序加锁和释放锁,防止死锁;
二、空指针
-
跨信任域方法传入参数需要判空:如Receiver回调方法中;
public class MyReceiver extends BroadcastReceiver { ..... @Override public void onReceive(Context context, Intent intent) { ... if (context == null || intent == null) { return; } String action = intent.getAction(); // intent非空,可以调用其方法。 ... } }
2. 信任域内的方法,可以通过注解的方式声明参数是否可为空;
public void nonNullParamFunction(@NonNull Context context) { // 增加非空注解
context.getColor();
.....
}
3. 通过调用方法获取的对象,使用前判空;
public class MyFragment extends Fragment {
private void myFunction() {
Activity activity = getActivity();
if (activity instanceof ActivityA) {
ActivityA activityA = (ActivityA) activity; // 强转前进行判断
activityA.functionA();
}
}
}
三、数据校验
-
跨信任域传参,参数使用前需进行有效性验证;
-
类型转换前做instanceof判断,不可不加判断进行强转;
public class MyFragment extends Fragment { private void myFunction() { Activity activity = getActivity(); if (activity instanceof ActivityA) { // 强转前进行判断 ActivityA activityA = (ActivityA) activity; activityA.functionA(); } } }
3. String转换为指定数字类型调用parse*方法需捕获异常;
private void parse(String string) {
int parsedValue = 0;
try {
int parsedValue = Integer.parseInt(string); // 当字符串不满足要求时,抛出异常
} catch (NumberFormatException exception) {
Log.w(TAG, "error string format!");
}
}
4. 使用除/运算符前,要判定除数非零;
四、兼容性
1. 通过非Activity context启动Activity,必须设置FLAG_ACTIVITY_NEW_TASK(10.0以后新增);
public startActivityFromAppContext() {
Intent intent = new Intent(getApplicationContext(), MainScreenActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //intent增加FLAG_ACTIVITY_NEW_TASK
getApplicationContext().startActivity(intent);
}
2. 大版本升级,新增接口需要反射调用;
BatteryManager bm = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
if (bm == null) {
return false;
}
int result = 0;
Method method = ReflectUtils.getDeclaredMethod(BatteryManager.class, "getWirelessTxSwitch");
Object invokedResult = ReflectUtils.invoke(method, bm);
if (invokedResult instanceof Integer) {
result = (Integer) invokedResult;
}
}
五、内存泄漏
1. Handler实现需要引用外部类时,用静态内部类+弱引用;
public class CustomService extends Service {
private CustomHandler mHandler;
@Override
public void onCreate() {
super.onCreate();
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new CustomHandler(this, mHandlerThread.getLooper());
}
private static class CustomHandler extends Handler{ // 静态内部类
private WeakReference<Context> mReference; // 使用WeakReference对象,
此时当CustomService被JVM回收时,由于CustomHandler是持有的它的弱引用,
因此可以被正常垃圾回收。
public CustomHandler(Context context,Looper looper){
super(looper);
mReference = new WeakReference<Context>(context);
}
@Override
public void handleMessage(Message msg) {
Context context = mReference.get();
context.XXXX();
....
}
}
}
2. Handler的外部类结束时,需要移除handler所有消息;
public class CustomService extends Service {
private CustomHandler mHandler;
@Override
protected void onCreate() {
super.onCreate();
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages();
mHandlerThread.quitSafely();
}
}
3. 单例生命周期和应用一致,如引用context需要用ApplicationContext;
public class SingleInstance {
private static SingleInstance sInstance;
private Context context;
private SingleInstance context) {
this.context = context.getApplicationContext();
}
public static SingleInstance getInstance(Context context) {
if (sInstance!= null) {
sInstance = new SingleInstance(context);
}
return sInstance;
}
}
4. 资源对象未关闭:Cursor/InputStream/OutputStream/IO流
5. Bitmap占用的Native内存,JVM垃圾回收机制回收不了Native内存,使用完需要主动调用recycle()方法回收;
6. 组件注册和解注册不成对:BroadcastReceiver和ContextOberver;
7. AlertDialog挂在window上,依赖的Activity失去焦点时,dialog需要dismiss;
public class MyActivity extends Activity {
private AlertDialog mDialog;
@Override
protected void Resume() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
mDialog = dialogBuilder.setIcon(R.mipmap.ic_launcher)
.setTitle("标题")
.setMessage("消息")
.setCancelable(true)
.create();
mDialog.show();
}
@Override void onStop() {
super.onStop();
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
}
}