一、Android资源文件命名与使用
1.【推荐】资源文件需要带模块前缀。
2.【推荐】layout文件的命名方式。
Activity的layout以module_activity开头
Fragment的laytout以module_fragment开头
ListView的layout以modlue_lits_item开关等
3.【推荐】
drawable 资源名称以小写单词+下划线的方式命名,根据分辨率不同存放 在不同的 drawable 目录下,建议只使用一套,例如 drawable-xhdpi。采用规则如下:
模块名_业务功能描述_控件描述_控件状态限定词
如:module_login_btn_pressed,module_tabs_icon_home_normal
4.【推荐】anim 资源名称以小写单词+下划线的方式命名,采用以下规则:
模块名_逻辑名称_[方向|序号]
tween 动画资源:尽可能以通用的动画名称命名,如 module_fade_in , module_fade_out , module_push_down_in (动画+方向);
frame 动画资源:尽可能以模 块+功能命名+序号。如:module_loading_grey_001
5. 【推荐】 color 资源使用#AARRGGBB 格式,写入 module_colors.xml 文件中,命 名格式采用以下规则:
模块名_逻辑名称_颜色
如:
<color name="module_btn_bg_color">#33b5e5e5</color>
6. 【推荐】dimen 资源以小写单词+下划线方式命名,写入 module_dimens.xml 文件中, 采用以下规则:
模块名_描述信息
如:
<dimen name="module_horizontal_line_height">1dp</dimen>
7. 【推荐】style 资源采用小写单词+下划线方式命名,写入 module_styles.xml 文件中, 采用以下规则:
父 style 名称.当前 style 名称
如:
<style name="ParentTheme.ThisActivityTheme">
…
</style>
8. 【推荐】 string资源文件或者文本用到字符需要全部写入module_strings.xml文件中, 字符串以小写单词+下划线的方式命名,采用以下规则:
模块名_逻辑名称
如:moudule_login_tips,module_homepage_notice_desc
9. 【推荐】Id 资源原则上以驼峰法命名,View 组件的资源 id 需要以 View 的缩写作为 前缀。常用缩写表如下:
控件 缩写
LinearLayout ll RelativeLayout rl ConstraintLayout cl ListView lv ScollView sv TextView tv Button btn ImageView iv CheckBox cb RadioButton rb EditText et
10.【推荐】大分辨率图片(单维度超过 1000)大分辨率图片建议统一放在 xxhdpi 目录 下管理,否则将导致占用内存成倍数增加。
说明:
为了支持多种屏幕尺寸和密度,Android 为多种屏幕提供不同的资源目录进行适配。 为不同屏幕密度提供不同的位图可绘制对象,可用于密度特定资源的配置限定符(在 下面详述) 包括 ldpi(低)、mdpi(中)、 hdpi(高)、xhdpi(超高)、xxhdpi (超 超高)和 xxxhdpi(超超超高)。例如,高密度屏幕的位图应使用 drawable-hdpi/。
根据当前的设备屏幕尺寸和密度,将会寻找最匹配的资源,如果将高分辨率图片放 入低密度目录,将会造成低端机加载过大图片资源,又可能造成 OOM,同时也是资 源浪费,没有必要在低端机使用大图。
正例:
将 144*144 的应用图标 PNG 文件放在 drawable-xxhdpi 目录
反例:
将 144*144 的应用图标 PNG 文件放在 drawable-mhdpi 目录
二、Android的基本组件
Android的基本组件指Activity、Fragment、Service、BroadCastReceiver、ContentProvider等
1.【强制】Activity间的数据通信,对于数据量比较大的,避免使用Intent+Parcelable的方式,可以考虑EventBus等替代方案,以免造成TransactionToolargeException.
2.【推荐】Activity#onSaveInstanceState()方法不是Activity生命周期方法,也不保证一定会被调用,例如UI控件的属性等,不能跟数据的持久化存储混为一谈,持久化存储应该
在Activity#onPause()/onStop()中实行。
3.【强制】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");
4.【强制】避免在Service#onStartCommand()/onBind()方法中执行耗时操作,如果确实有需求,应该用IntentSevice或采用其他异步机制完成。
正例: 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) { } 正例: 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) { } } } }
5.【强制】避免在BroadcastReceiver#onReceiver()中执行耗时操作,如果有耗时工作,应该创建IntentService完成,而不应该在BroadCastReceiver内创建子线程去做。
说明:
由于该方法是在主线程执行,如果执行耗时操作会导致Ui不流畅。可以使用IntentService、创建HandlerThread或者调用Context#registerReceiver方法等方式 ,在其他 Worker线程执行onReceiver方法。BroadCastReceiver#onRecive()方法耗时超过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
}
};
8. 【推荐】不要在 Activity#onDestroy()内执行释放资源的工作,例如一些工作线程的 销毁和停止,因为 onDestroy()执行的时机可能较晚。可根据实际需要,在 Activity#onPause()/onStop()中结合 isFinishing()的判断来执行。
9. 【推荐】如非必须,避免使用嵌套的 Fragment。
说明:
嵌套 Fragment 是在 Android API 17 添加到 SDK 以及 Support 库中的功能, Fragment 嵌套使用会有一些坑,容易出现 bug,比较常见的问题有如下几种:
1) onActivityResult()方法的处理错乱,内嵌的 Fragment 可能收不到该方法的回调, 需要由宿主 Fragment 进行转发处理;
2) 突变动画效果;
3) 被继承的 setRetainInstance(),导致在 Fragment 重建时多次触发不必要的逻 辑。
非必须的场景尽可能避免使用嵌套 Fragment,如需使用请注意上述问题。
10.【推荐】总是使用显式Intent启动或者绑定Service,且不要为服务声明IntentFilter,保证应用的安全性。如果确实需要 使用隐式调用,则可为Service提供IntentFilter并从Intent中排除相应的组件名称,但必须搭配使用Intent#setPackage()方法设置Intent的指定包名,这样可以充分消除目标服务的不确定性。
11【推荐】Service需要以多线程来并发处理多个启动请求,建议使用IntentService,可避免各种复杂的设置。
说明:
Service组件一般运行主线程,应该避免耗时操作,如果有耗时操作应该在Worker线程执行。可以使用IntentService执行后台任务。
12.【推荐】当前Activity的onPause方法执行结束后才会执行下一个Activity的onCreate 方法,所以在 onPause 方法中不适合做耗时较长的工作,这会影响到页面之间的跳 转效率。
13.【强制】不要在 Android 的 Application 对象中缓存数据。基础组件之间的数据共享 请使用 Intent 等机制,也可使用 SharedPreferences 等数据持久化机制。
14.【推荐】使用 Toast 时,建议定义一个全局的 Toast 对象,这样可以避免连续显示 Toast 时不能取消上一次 Toast 消息的情况(如果你有连续弹出 Toast 的情况,避免 使用 Toast.makeText)。
15. 【强制】使用 Adapter 的时候,如果你使用了 ViewHolder 做缓存,在 getView()的 方法中无论这项 convertView 的每个子控件是否需要设置属性(比如某个 TextView 设置的文本可能为 null,某个按钮的背景色为透明,某控件的颜色为透明等),都需 要为其显式设置属性(Textview的文本为空也需要设置 setText(""),背景透明也需要 设置),否则在滑动的过程中,因为 adapter item 复用的原因,会出现内容的显示错 乱。
16.【强制】Activity或者Fragment中动态注册BroadCastReceiver时,registerReceiver() 和 unregisterReceiver()要成对出现。
说明:
如果 registerReceiver()和 unregisterReceiver()不成对出现,则可能导致已经注册的 receiver没有在合适的时机注销,导致内存泄漏,占用内存空间,加重SystemService 负担。
部分华为的机型会对 receiver 进行资源管控,单个应用注册过多 receiver 会触发管 控模块抛出异常,应用直接崩溃。