Android稳定性规范

988 阅读3分钟

一、导致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:使用相同顺序加锁和释放锁,防止死锁;

二、空指针

  1. 跨信任域方法传入参数需要判空:如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();
        }
    }
}

三、数据校验

  1. 跨信任域传参,参数使用前需进行有效性验证;

  2. 类型转换前做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对象,
                此时当CustomServiceJVM回收时,由于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();
		}
	}
}