《阿里巴巴Android編碼規范》閱讀紀要(一)


版權聲明:本文出自汪磊的博客,轉載請務必注明出處。

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編碼規范》閱讀一下,里面還是有點干貨的。

好了,本篇到此結束,希望對你有用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM