版權聲明:本文出自汪磊的博客,轉載請務必注明出處。
2月28日阿里巴巴首次公開內部安卓編碼規范,試想那么多業務線,開發人員,沒有一套規范管理起來是多么麻煩,以下是個人閱讀Android基本組件部分過程中覺得不錯的地方,摘錄下來。
Android基本組件部分
1,Activity#onSaveInstanceState()方法不是Activity生命周期方法,也不保證一定會被調用。它是用來在Activity被意外銷毀時保存UI狀態的,只能用於保存臨時性數據,例如UI控件的屬性等,不能跟數據持久化存儲混為一談。持久化存儲應該在Activity#onPause()/onStop()中實行。
2,Activity間通過隱式Intent的跳轉,在發出Intent之前必須通過resolveActivity檢查,避免找不到合適的調用組件造成ActivityNotFoundException的異常。如:
1 public void viewUrl(String url, String mimeType){ 2 3 Intent intent = new Intent(Intent.ACTION_VIEW); 4 intent.setDataAndType(Uri.parse(url), mimeType); 5 6 if(getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)!=null){ 7 8 try{ 9 10 startActivity(intent); 11 }catch(ActivityNotFoundException e){ 12 13 } 14 } 15 }
3,避免使用隱式Intent廣播敏感信息,信息可能被其他注冊了對應BroadcastReceiver的App接。
說明:
通過 Context#sendBroadcast()發送的隱式廣播會被所有感興趣的 receiver 接收,惡意應用注冊監聽該廣播的 receiver 可能會獲取到 Intent 中傳遞的敏感信息,並進行其他危險操作。如果發送的廣播為使用 Context#sendOrderedBroadcast()方法發送的有序廣播,優先級較高的惡意receiver 可能直接丟棄該廣播,造成服務不可用,或者向廣播結果塞入惡意數據。如果廣播僅限於應用內,則可以使LocalBroadcastManager#sendBroadcast()實現,避免敏感信息外泄和 Intent 攔截的風險。
如:
1 Intent intent = new Intent("my-sensitive-event"); 2 intent.putExtra("event", "this is a test event"); 3 LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
4,不要在 Activity#onDestroy()內執行釋放資源的工作,例如一些工作線程的銷毀和停止,因為 onDestroy() 執行的時機可能較晚。可根據實際需要,在Activity#onPause()/onStop()中結合 isFinishing()的判斷來執行。
5,如非必須,避免使用嵌套的 Fragment。
說明:
嵌套 Fragment 是在 Android API 17 添加到 SDK 以及 Support 庫中的功能,
Fragment 嵌套使用會有一些坑,容易出現 bug,比較常見的問題有如下幾種:
1)onActivityResult()方法的處理錯亂,內嵌的 Fragment 可能收不到該方法的回調,需要由宿主 Fragment 進行轉發處理;
2)突變動畫效果;
3)被繼承的 setRetainInstance(),導致在 Fragment 重建時多次觸發不必要的邏輯。
非必須的場景盡可能避免使用嵌套 Fragment,如需使用請注意上述問題。
正例:
1 FragmentManager fragmentManager = getFragmentManager(); 2 Fragment fragment =fragmentManager.findFragmentByTag(FragmentB.TAG); 3 if (null == fragment) { 4 FragmentB fragmentB = new FragmentB(); 5 6 FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 7 8 fragmentTransaction.add(R.id.fragment_container, fragmentB, FragmentB.TAG).commit(); 9 10 11 } 12
反例:
1 Fragment videoFragment = new VideoPlayerFragment(); 2 3 FragmentTransaction transaction = currentFragment.getChildFragmentManager().beginTransaction(); 4 5 transaction.add(R.id.video_fragment, videoFragment).commit();
6,對於只用於應用內的廣播,優先使用 LocalBroadcastManager 來進行注冊和發送,LocalBroadcastManager 安全性更好,同時擁有更高的運行效率。
說明:
對於使用 Context#sendBroadcast()等方法發送全局廣播的代碼進行提示。如果該廣播僅用於應用內,則可以使用 LocalBroadcastManager 來避免廣播泄漏以及廣播被攔截等安全問題,同時相對全局廣播本地廣播的更高效。
7,當前 Activity 的 onPause 方法執行結束后才會執行下一個 Activity 的 onCreate 方法,所以在 onPause 方法中不適合做耗時較長的工作,這會影響到頁面之間的跳轉效率。
8,不要在 Android 的 Application 對象中緩存數據。基礎組件之間的數據共享請使用 Intent 等機制,也可使用 SharedPreferences 等數據持久化機制。
9,使用 Adapter 的時候,如果你使用了 ViewHolder 做緩存,在 getView()的方法中無論這項 convertView 的每個子控件是否需要設置屬性(比如某個 TextView 設置的文本可能為 null,某個按鈕的背景色為透明,某控件的顏色為透明等),都需要為其顯式設置屬性(Textview 的文本為空也需要設置 setText(""),背景透明也需要設置),否則在滑動的過程中,因為 adapter item 復用的原因,會出現內容的顯示錯亂。
正例:
1 @Override 2 public View getView(int position, View convertView, ViewGroup parent) { 3 ViewHolder myViews; 4 5 if (convertView == null) { 6 7 myViews = new ViewHolder(); 8 9 convertView = mInflater.inflate(R.layout.list_item, null); 10 11 myViews.mUsername = (TextView) convertView 12 .findViewById(R.id.username); 13 convertView.setTag(myViews); 14 } else { 15 16 myViews = (ViewHolder) convertView.getTag(); 17 18 } 19 20 Info p = infoList.get(position); 21 22 String dn = p.getDisplayName; 23 24 myViews.mUsername.setText(StringUtils.isEmpty(dn) ? "" : dn); 25 return convertView; 26 27 } 28 29 static class ViewHolder { 30 31 private TextView mUsername; 32 33 }
10,Activity 或者 Fragment 中動態注冊 BroadCastReceiver 時,registerReceiver()和 unregisterReceiver()要成對出現。
說明:
如果 registerReceiver()和 unregisterReceiver()不成對出現,則可能導致已經注冊的
receiver 沒有在合適的時機注銷,導致內存泄漏,占用內存空間,加重 SystemService 負擔。
部分華為的機型會對 receiver 進行資源管控,單個應用注冊過多 receiver 會觸發管控模塊拋出異常,應用直接崩潰。
正例:
1 public class MainActivity extends AppCompatActivity { 2 3 private static MyReceiver myReceiver = new MyReceiver(); 4 5 @Override 6 protected void onResume() { 7 super.onResume(); 8 IntentFilter filter = new IntentFilter("com.example.myservice"); 9 registerReceiver(myReceiver, filter); 10 } 11 12 @Override 13 protected void onPause() { 14 super.onPause(); 15 unregisterReceiver(myReceiver); 16 } 17 }
反例:
1 public class MainActivity extends AppCompatActivity { 2 3 private static MyReceiver myReceiver; 4 5 @Override 6 protected void onResume() { 7 super.onResume(); 8 myReceiver = new MyReceiver(); 9 IntentFilter filter = new IntentFilter("com.example.myservice"); 10 registerReceiver(myReceiver, filter); 11 } 12 13 @Override 14 protected void onDestroy() { 15 super.onDestroy(); 16 unregisterReceiver(myReceiver); 17 } 18 }
Activity 的生命周期不對應,可能出現多次 onResume 造成 receiver 注冊多個,但最終只注銷一個,其余 receiver 產生內存泄漏。
以上就是阿里巴巴安卓編碼規范中Android基本組件部分提及的一些規范,並沒有全部列出來,只是記錄一些個人覺得不錯的地方,整體下來感覺都是細微的地方,感興趣的同學可以自行查找完整版《阿里巴巴Android編碼規范》閱讀一下,里面還是有點干貨的。
好了,本篇到此結束,希望對你有用。