以下内容摘自 阿里巴巴Android开发手册

我们的目标是:

  • 防患未然,提升质量意识,降低故障率和维护成本;
  • 标准统一,提升协作效率;
  • 追求卓越的工匠精神,打磨精品代码。
  • 【强制】必须遵守,违反本约定或将会引起严重的后果;
  • 【推荐】尽量遵守,长期遵守有助于系统稳定性和合作效率的提升;
  • 【参考】充分理解,技术意识的引导,是个人学习、团队沟通、项目合作的方向。

阿里Android开发规范:资源文件命名与使用规范
阿里Android开发规范:四大基本组件
阿里Android开发规范:UI 与布局
阿里Android开发规范:进程、线程与消息通信
阿里Android开发规范:文件与数据库
阿里Android开发规范:Bitmap、Drawable 与动画
阿里Android开发规范:安全与其他

Android 基本组件指 Activity、Fragment、Service、BroadcastReceiver、ContentProvider 等等。

1、【强制】Activity 间的数据通信,对于数据量比较大的,避免使用 Intent + Parcelable的方式,可以考虑 EventBus 等替代方案,以免造成 TransactionTooLargeException。
2、【推荐】Activity#onSaveInstanceState()方法不是 Activity 生命周期方法,也不保证一定会被调用。它是用来在 Activity 被意外销毁时保存 UI 状态的,只能用于保存临时性数据,例如 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()方法中执行耗时操作,如果确实有需求,应改用 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) {            }        }    }}

5、【强制】避免在 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    }};

扩展参考:
https://developer.android.com/reference/android/content/BroadcastReceiver.html#onReceive(android.content.Context,android.content.Intent)
6、【强制】避免使用隐式 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();intent.setAction("com.sample.action.server_running");intent.putExtra("local_ip", v0.h);intent.putExtra("port", v0.i);intent.putExtra("code", v0.g);intent.putExtra("connected", v0.s);intent.putExtra("pwd_predefined", v0.r);if (!TextUtils.isEmpty(v0.t)) {    intent.putExtra("connected_usr", v0.t);}context.sendBroadcast(intent);

以上广播可能被其他应用的如下 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);            }        }    }}

扩展参考:

  1. https://wiki.sei.cmu.edu/confluence/display/android/DRD03-J.+Do+not+broadcast+sensitive+information+using+an+implicit+intent
  2. https://cwe.mitre.org/data/definitions/927.html

7、【推荐】添加 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();    }}

扩展参考:
1) https://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
2) https://developer.android.com/reference/android/app/FragmentTransaction.html#commit())
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,如需使用请注意上述问题。
正例:

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();

扩展参考:
1) https://inthecheesefactory.com/blog/onactivityresult-nested-fragment-support-library-v23.2/en
2) http://blog.csdn.net/megatronkings/article/details/51417510
10、【推荐】总是使用显式 Intent 启动或者绑定 Service,且不要为服务声明 Intent Filter,保证应用的安全性。如果确实需要使用隐式调用,则可为 Service 提供 Intent Filter并从 Intent 中排除相应的组件名称,但必须搭配使用 Intent#setPackage()方法设置Intent 的指定包名,这样可以充分消除目标服务的不确定性。
11、【推荐】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();    ...    }}

扩展参考:
https://developer.android.com/training/run-background-service/index.html
12、【推荐】对于只用于应用内的广播,优先使用 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 broadcastIntent intent = new Intent("com.example.broadcastreceiver.SOME_ACTION");sendBroadcast(intent);

13、【推荐】当前Activity的onPause方法执行结束后才会执行下一个Activity的onCreate方法,所以在 onPause 方法中不适合做耗时较长的工作,这会影响到页面之间的跳转效率。
14、【强制】不要在 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());    }}

15、【推荐】使用 Toast 时,建议定义一个全局的 Toast 对象,这样可以避免连续显示Toast 时不能取消上一次 Toast 消息的情况(如果你有连续弹出 Toast 的情况,避免使用 Toast.makeText)。
16、【强制】使用 Adapter 的时候,如果你使用了 ViewHolder 做缓存,在 getView()的方法中无论这项 convertView 的每个子控件是否需要设置属性(比如某个 TextView设置的文本可能为 null,某个按钮的背景色为透明,某控件的颜色为透明等),都需要为其显式设置属性(Textview 的文本为空也需要设置setText(“”),背景透明也需要设置),否则在滑动的过程中,因为 adapter item 复用的原因,会出现内容的显示错乱。
正例:

@Overridepublic 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;    myViews.mUsername.setText(StringUtils.isEmpty(dn) ? "" : dn);    return convertView;}static class ViewHolder {    private TextView mUsername;}

17、【推荐】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 产生内存泄漏。

阿里Android开发规范:资源文件命名与使用规范
阿里Android开发规范:四大基本组件
阿里Android开发规范:UI 与布局
阿里Android开发规范:进程、线程与消息通信
阿里Android开发规范:文件与数据库
阿里Android开发规范:Bitmap、Drawable 与动画
阿里Android开发规范:安全与其他

更多相关文章

  1. Google 菜市场(Android Market)上不去的解决方法
  2. Android高手进阶教程(二十一)之---Android中创建与几种解析xml的
  3. Android 线程完全解析
  4. 主线程中Looper的轮询死循环为何没有阻塞主线程?
  5. Android 应用程序查找设备的方法——以串口为例

随机推荐

  1. Android(安卓)Studio Build Output 栏内
  2. Android的Shape图片资源,用于做些自定义样
  3. Android(安卓)视频录制 动态图
  4. android-PullToRefreshListView自动刷新
  5. 解决android studio国内开发者Gradle下载
  6. Android(安卓)纹理定距离移动
  7. android 删除开机动画
  8. Android(安卓)studio配置Kotlin
  9. 免费看 Unable to find method 'org.grad
  10. Android侧拉框的简单实现