Android 基本组件
Android 基本组件指 Activity、Fragment、Service、BroadcastReceiver、ContentProvider 等等。
Activity 间的数据通信【强制】
Activity 间的数据通信,对于数据量比较大的,
避免使用 Intent + Parcelable的方式,
可以考虑 EventBus 等替代方案,
以免造成 TransactionTooLargeException。
Activity#onSaveInstanceState()方法不保证一定会被调用【推荐】
Activity#onSaveInstanceState()方法不是 Activity 生命周期方法,也不保证一定会被调用。
它是用来在 Activity 被意外销毁时保存 UI 状态的,只能用于保存临时性数据,例如 UI 控件的属性等,不能跟数据的持久化存储混为一谈。 持久化存储应该在 Activity#onPause()/onStop()中实行。
跳转Activity前检查resolveActivity【强制】
Activity 间通过隐式 Intent 的跳转,在发出 Intent 之前必须通过 resolveActivity 检查,避免找不到合适的调用组件,造成 ActivityNotFoundException 的异常。
正例:
public void viewUrl(String url, String mimeType) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(url), mimeType);
if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
if (Config.LOGD) {
Log.d(LOGTAG, "activity not found for " + mimeType + " over " + Uri.parse(url).getScheme(), e);
}
}
}
}
反例:
Intent intent = new Intent();
intent.setAction("com.great.activity_intent.Intent_Demo1_Result3");
避免在 Service#onStartCommand()/onBind()方法中执行耗时操作【强制】
避免在 Service#onStartCommand()/onBind()方法中执行耗时操作, 如果确实有需求,应改用 IntentService 或采用其他异步机制完成。
正例:
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void startIntentService(View source) {
Intent intent = new Intent(this, MyIntentService.class);
startService(intent);
}
}
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
synchronized (this) {
try {
// ...
} catch (Exception e) {
// ...
}
}
}
}
避免在 BroadcastReceiver#onReceive()中执行耗时操作【强制】
避免在 BroadcastReceiver#onReceive()中执行耗时操作, 如果有耗时工作,应该创建 IntentService 完成,而不应该在 BroadcastReceiver 内创建子线程去做。
说明:
由于该方法是在主线程执行,如果执行耗时操作会导致 UI 不流畅。可以使用 IntentService 、 创 建 HandlerThread 或者调用 Context#registerReceiver (BroadcastReceiver, IntentFilter, String, Handler)方法等方式,在其他 Wroker 线程 执行 onReceive 方法。BroadcastReceiver#onReceive()方法耗时超过 10 秒钟,可 能会被系统杀死。
正例:
IntentFilter filter = new IntentFilter();
filter.addAction(LOGIN_SUCCESS);
this.registerReceiver(mBroadcastReceiver, filter);
mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Intent userHomeIntent = new Intent();
userHomeIntent.setClass(this, UseHomeActivity.class);
this.startActivity(userHomeIntent);
}
};
反例:
mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
MyDatabaseHelper myDB = new MyDatabaseHelper(context);
myDB.initData();
// have more database operation here
}
};
避免使用隐式 Intent 广播敏感信息【强制】
避免使用隐式 Intent 广播敏感信息,信息可能被其他注册了对应BroadcastReceiver 的 App 接收。
说明:
通过 Context#sendBroadcast()发送的隐式广播会被所有感兴趣的 receiver 接收,恶 意应用注册监听该广播的 receiver 可能会获取到 Intent 中传递的敏感信息,并进行 其他危险操作。如果发送的广播为使用 Context#sendOrderedBroadcast()方法发送 的有序广播,优先级较高的恶意 receiver 可能直接丢弃该广播,造成服务不可用, 或者向广播结果塞入恶意数据。
如果广播仅限于应用内,则可以使用 LocalBroadcastManager#sendBroadcast()实 现,避免敏感信息外泄和 Intent 拦截的风险。
正例:
Intent intent = new Intent("my-sensitive-event");
intent.putExtra("event", "this is a test event");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
反例:
Intent intent = new Intent();
v1.setAction("com.sample.action.server_running");
v1.putExtra("local_ip", v0.h);
v1.putExtra("port", v0.i);
v1.putExtra("code", v0.g);
v1.putExtra("connected", v0.s);
v1.putExtra("pwd_predefined", v0.r);
if (!TextUtils.isEmpty(v0.t)) {
v1.putExtra("connected_usr", v0.t);
}
context.sendBroadcast(v1);
以上广播可能被其他应用的如下 receiver 接收导致敏感信息泄漏
final class MyReceiver extends BroadcastReceiver {
public final void onReceive(Context context, Intent intent) {
if (intent != null && intent.getAction() != null) {
String s = intent.getAction();
if (s.equals("com.sample.action.server_running") {
String ip = intent.getStringExtra("local_ip");
String pwd = intent.getStringExtra("code");
String port = intent.getIntExtra("port", 8888);
boolean status = intent.getBooleanExtra("connected", false);
}
}
}
}
添加fragment时FragmentTransaction相关【推荐】
添加 Fragment 时 , 确保 FragmentTransaction#commit() 在Activity#onPostResume()或者 FragmentActivity#onResumeFragments()内调用。
不要随意使用 FragmentTransaction#commitAllowingStateLoss()来代替, 任何commitAllowingStateLoss()的使用必须经过 code review,确保无负面影响。
说明:
Activity可能因为各种原因被销毁,Android支持页面被销毁前通过Activity#onSaveInstanceState()保存自己的状态。
但如果FragmentTransaction.commit()发生在 Activity 状态保存之后,就会导致 Activity 重建、恢复状态时无法还原页面状态,从而可能出错。
为了避免给用户造成不好的体验,系统会抛出 IllegalStateExceptionStateLoss 异常。 推荐的做法是在 Activity 的 onPostResume() 或 onResumeFragments() ( 对 FragmentActivity )里执行 FragmentTransaction.commit(),如有必要也可在 onCreate()里执行。
不要随意改用 FragmentTransaction.commitAllowingStateLoss() 或者直接使用 try-catch 避免crash,这不是问题的根本解决之道,当且仅当你确认 Activity 重建、恢复状态时,本次 commit 丢失不会造成影响时才可这么做。
正例:
public class MainActivity extends FragmentActivity {
FragmentManager fragmentManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
MyFragment fragment = new MyFragment();
ft.replace(R.id.fragment_container, fragment);
ft.commit();
}
}
反例:
public class MainActivity extends FragmentActivity {
FragmentManager fragmentManager;
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
MyFragment fragment = new MyFragment();
ft.replace(R.id.fragment_container, fragment);
ft.commit();
}
}
不要在 Activity#onDestroy()内执行释放资源的工作【推荐】
不要在 Activity#onDestroy()内执行释放资源的工作,例如一些工作线程的销毁和停止。 因为 onDestroy()执行的时机可能较晚。 可根据实际需要,在Activity#onPause()/onStop()中结合 isFinishing()的判断来执行。
如非必须,避免使用嵌套的 Fragment。【推荐】
说明:
嵌套 Fragment 是在 Android API 17 添加到 SDK 以及 Support 库中的功能, Fragment 嵌套使用会有一些坑,容易出现 bug,比较常见的问题有如下几种:
- onActivityResult()方法的处理错乱,内嵌的 Fragment 可能收不到该方法的回调, 需要由宿主 Fragment 进行转发处理;
- 突变动画效果;
- 被继承的 setRetainInstance(),导致在 Fragment 重建时多次触发不必要的逻 辑。
非必须的场景尽可能避免使用嵌套 Fragment,如需使用请注意上述问题。
正例:
FragmentManager fragmentManager = getFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(FragmentB.TAG);
if (null == fragment) {
FragmentB fragmentB = new FragmentB();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragment_container, fragmentB,
FragmentB.TAG).commit();
}
反例:
Fragment videoFragment = new VideoPlayerFragment();
FragmentTransaction transaction =
currentFragment.getChildFragmentManager().beginTransaction();
transaction.add(R.id.video_fragment, videoFragment).commit();
总是使用显式 Intent 启动或者绑定 Service【推荐】
总是使用显式 Intent 启动或者绑定 Service,且不要为服务声明 Intent Filter, 保证应用的安全性。如果确实需要使用隐式调用,则可为 Service 提供 Intent Filter 并从 Intent 中排除相应的组件名称,但必须搭配使用 Intent#setPackage()方法设置 Intent 的指定包名,这样可以充分消除目标服务的不确定性。
使用 IntentService代替 Service 以多线程来并发处理多个启动请求【推荐】
Service 需要以多线程来并发处理多个启动请求,建议使用 IntentService, 可避免各种复杂的设置。
说明:
Service 组件一般运行主线程,应当避免耗时操作,如果有耗时操作应该在 Worker 线程执行。 可以使用 IntentService 执行后台任务。
正例:
public class SingleIntentService extends IntentService {
public SingleIntentService() {
super("single-service thread");
}
@Override
protected void onHandleIntent(Intent intent) {
try {
// 。。。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
反例:
public class HelloService extends Service {
...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
new Thread(new Runnable() {
@Override
public void run() {
//操作语句
}
}).start();
...
}
}
优先使用 LocalBroadcastManager
对于只用于应用内的广播,优先使用 LocalBroadcastManager 来进行注册 和发送,LocalBroadcastManager 安全性更好,同时拥有更高的运行效率。
说明:
对于使用 Context#sendBroadcast()等方法发送全局广播的代码进行提示。如果该广 播仅用于应用内,则可以使用 LocalBroadcastManager 来避免广播泄漏以及广播被 拦截等安全问题,同时相对全局广播本地广播的更高效。
正例:
public class MainActivity extends ActionBarActivity {
private MyReceiver receiver;
private IntentFilter filter;
private Context context;
private static final String MY_BROADCAST_TAG = "com.example.localbroadcast";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
receiver = new MyReceiver();
filter = new IntentFilter();
filter.addAction(MY_BROADCAST_TAG);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setAction(MY_BROADCAST_TAG);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
});
}
@Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(context).registerReceiver(receiver, filter);
}
@Override
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver);
}
class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context arg0, Intent arg1) {
// message received
}
}
}
反例:
所有广播都使用全局广播
// In activity, sending broadcast
Intent intent = new Intent("com.example.broadcastreceiver.SOME_ACTION");
sendBroadcast(intent);
不要在 onPause 方法中做耗时较长的工作【推荐】
当前Activity的onPause方法执行结束后才会执行下一个Activity的onCreate 方法,所以在 onPause 方法中不适合做耗时较长的工作,这会影响到页面之间的跳 转效率。
不要在 Android 的 Application 对象中缓存数据。【强制】
不要在 Android 的 Application 对象中缓存数据。基础组件之间的数据共享 请使用 Intent 等机制,也可使用 SharedPreferences 等数据持久化机制。
反例:
class MyApplication extends Application {
String username;
String getUsername() {
return username;
}
void setUsername(String username) {
this.username = username;
}
}
class SetUsernameActivity extends Activity {
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.set_username);
MyApplication app = (MyApplication) getApplication();
app.setUsername("tester1");
startActivity(new Intent(this, GetUsernameActivity.class));
}
}
class GetUsernameActivity extends Activity {
TextView tv;
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.get_username);
tv = (TextView)findViewById(R.id.username);
}
void onResume() {
super.onResume();
MyApplication app = (MyApplication) getApplication();
tv.setText("Welcome back ! " + app.getUsername().toUpperCase());
}
}
定义一个全局的 Toast 对象【推荐】
使用 Toast 时,建议定义一个全局的 Toast 对象,这样可以避免连续显示 Toast 时不能取消上一次 Toast 消息的情况(如果你有连续弹出 Toast 的情况,避免 使用 Toast.makeText)。
Adapter ViewHolder getView() convertView的每个子控件的属性【强制】
使用 Adapter 的时候,如果你使用了 ViewHolder 做缓存,在 getView()的 方法中无论这项 convertView 的每个子控件是否需要设置属性(比如某个 TextView 设置的文本可能为 null,某个按钮的背景色为透明,某控件的颜色为透明等),都需 要为其显式设置属性(Textview 的文本为空也需要设置 setText(""),背景透明也需要 设置),否则在滑动的过程中,因为 adapter item 复用的原因,会出现内容的显示错 乱。
正例:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder myViews;
if (convertView == null) {
myViews = new ViewHolder();
convertView = mInflater.inflate(R.layout.list_item, null);
myViews.mUsername = (TextView)convertView.findViewById(R.id.username);
convertView.setTag(myViews);
} else {
myViews = (ViewHolder)convertView.getTag();
}
Info p = infoList.get(position);
String dn = p.getDisplayName;
三、Android 基本组件
- 20 -
myViews.mUsername.setText(StringUtils.isEmpty(dn) ? "" : dn);
return convertView;
}
static class ViewHolder {
private TextView mUsername;
}
Activity或者 Fragment 中动态注册BroadCastReceiver 时,registerReceiver()和 unregisterReceiver()要成对出现
说明:
如果 registerReceiver()和 unregisterReceiver()不成对出现,则可能导致已经注册的 receiver 没有在合适的时机注销,导致内存泄漏,占用内存空间,加重 SystemService 负担。 部分华为的机型会对 receiver 进行资源管控,单个应用注册过多 receiver 会触发管 控模块抛出异常,应用直接崩溃。
正例:
public class MainActivity extends AppCompatActivity {
private static MyReceiver myReceiver = new MyReceiver();
...
@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter("com.example.myservice");
registerReceiver(myReceiver, filter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(myReceiver);
}
...
}
反例:
public class MainActivity extends AppCompatActivity {
private static MyReceiver myReceiver;
@Override
protected void onResume() {
super.onResume();
myReceiver = new MyReceiver();
IntentFilter filter = new IntentFilter("com.example.myservice");
registerReceiver(myReceiver, filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myReceiver);
}
}
Activity 的生命周期不对应,可能出现多次 onResume 造成 receiver 注册多个,但 最终只注销一个,其余 receiver 产生内存泄漏。