Android開發(fā)細節(jié)--查漏補缺(二):易忘難懂

這篇文章先匯總一下部分在平常容易遇到或可能遇到的一些難以理解或者容易忘記是什么的問題。


  1. android:supportsRtl="true"是什么?
    android:supportsRtl="true" 故名思義,“Rtl”就是“right to left” ,一般用于適配language文本方向,如切換系統(tǒng)語言為阿拉伯文時,actionbar布局要變?yōu)閺挠蚁蜃笈帕校托枰@個屬性。

注意:
此屬性最低SDK版本為17(Android 4.2.x)
在使用這個屬性時,盡量不適用layout_marginRight這類,而是使用layout_marginEnd這種。


  1. Android res/menu/my_nemu.xml中不易記的屬性:
  • android:alphabeticShortcut="n" :表示快捷鍵為:Ctrl+n 、Alt+n
  • android:orderInCategory = "3":表示在菜單列表中的排序,值可以為大于等于0的任何整數(shù),數(shù)值越小,排序越前。
  • android:checkableBehavior = "single|all|none":分別表示radio button、checkbox、checkable=false
  • <group android:id="xx"……> <item android:id="yy"/>……</group>: 用一個組,包含幾個menu_item,這樣,組內(nèi)部的item的順序就可以重新定義。

  1. Android中用Java代碼設置菜單項快捷鍵的方法:
menu.setQwertyMode(true);
menu.findItem(TWO_ID).setNumericShortcut('2');

  1. <RelativeLayout>中子view的屬性:
  • android:below="@+id/xx" :表示本view在xx的下面
  • android:above="@+id/xx":表示本view在xx的上面

  1. implements Parcelable的對象的一般寫法:(詳見我的另一篇博文:Android AIDL基礎 -- Parcelable接口
public class Student implements Parcelable{
  ///-------------------------------
  /// (1):基本類型、String/CharSequence、Bundle、IBinder、Serializable、Parcelable 的成員
  private T t;
  ……
  ///---------------------------------
  ///(2):構(gòu)造方法(可選,怎么方便賦值成員變量值并構(gòu)建實例,就怎么寫)
  public Student() {
      ……
  }
 //=========================================================
 //  下面是Parcelable需要實現(xiàn)的方法
 //=========================================================
  protected Student(Parcel in) {
      ///建議:按成員變量順序?qū)憽疽驗椋盒枰cwriteToParcel()方法中的寫入順序一致】
      id = in.readInt();//如:基本數(shù)據(jù)類型成員
      name = in.readString();//如:String類型成員
      home = in.readParcelable(Address.class.getClassLoader());//如:Parcelable類型成員
      ……
  }
  public static final Creator<Student> CREATOR = new Creator<Student>() {
      ///CREATOR成員,格式必須如下,‘CREATOR’變量名也固定。
      @Override
      public Student createFromParcel(Parcel in) {
          return new Student(in);
      }
      @Override
      public Student[] newArray(int size) {
          return new Student[size];
      }
  };
  @Override
  public int describeContents() {
      /// 描述標志,默認為0即可
      return 0;
  }
  @Override
  public void writeToParcel(Parcel dest, int flags) {
      ////寫入Parcel的方法【需要與writeToParcel()方法中的寫入順序一致】
      dest.writeInt(id);
      dest.writeString(name);
      dest.writeParcelable(home, flags);
      ……
  }
}
  1. Android Studio獲取Android簽名證書SHA1的簡易步驟:
  • Android Studio中打開terminal或者配置了全局變量之后直接打開終端輸入:
    keytool -list -v -keystore 你的簽名文件所在的路徑
  • 輸入密鑰庫密碼
  • 成功顯示:MD5、SHA1、SHA256等信息

    想知道Eclipse的獲取方式,更詳細的地址可以看:SHA1查看


  1. JSON封裝庫的效率對比:
    如果對JSON的解析速度敏感:大文件用:Jackson,小文件用:Gson,兩種文件都有用:JSON.simple

  1. Java代碼執(zhí)行new一個類實例對象的過程中,static塊、static方法、main方法的執(zhí)行順序:
  • static塊【最先執(zhí)行,因為靜態(tài)代碼塊屬于類層次,在類編譯時就執(zhí)行】
  • -> main方法
  • -> 成員塊
  • -> 構(gòu)造方法
  • -> static方法【在被調(diào)用時才執(zhí)行】

詳細可以看看本人的相關文章:Java面試相關(一)-- Java類加載全過程


  1. 在寫Fragment的時候,F(xiàn)ragment.onCreateView()方法中,一般是這樣的一個結(jié)構(gòu):
@Nullable
@Override
public View onCreateView(LayoutInflater inflater,  ViewGroup container,  Bundle savedInstanceState) {
if (rootLayout == null) {
  rootLayout = inflater.inflate(R.layout.fragment_reclist, null);///①
  rootLayout = inflater.inflate(R.layout.fragment_reclist, container, false);///②
  ///rootLayout = inflater.inflate(R.layout.fragment_reclist, container);///③這樣不行!!
  ……  
}
……
return rootLayout;
}

這里,要注意代碼中注釋掉的地方,這里有一個對于Fragment的知識點:
inflater.inflate(id, ViewGroup);
說明:
?這個方法傳入:【目的布局的R.layout 的id】 + 【目的布局的父容器】。那么,對于Fragment的來說,F(xiàn)ragment的onCreateView()方式最終返回的View會自動判斷本Fragment所在的父容器位置。
?為什么上面代碼③不行呢? 答:因為inflater.inflate(id, ViewGroup);最終會調(diào)用inflater.inflate(id, ViewGroup,boolean); 方法,當ViewGroup不為null時,這方法的第三個參數(shù)值會為true,表示的是‘inflate出來的布局自動加到它的父viewgroup中’,那么問題就來了,如果這個父布局是一個ViewPager或者其他不易確定子View放置位置的容器,那么,方法就會報錯,因為找不到插入點!所以,對于Fragment的布局,一般情況不需要設置自動添加到父布局,所以,上述代碼①和②二選一都可以。


  1. Fragment 與 它所在的Activity的生命周期方法執(zhí)行次序:
    下面用一個常用布局作為示例【FragmentActivity中用ViewPager裝載兩個Fragment 部分代碼示例】來演示 ,看運行的Log就可以說明整個執(zhí)行順序:
public class CommonTabActivity extends FragmentActivity {
……
/// TabLayout + ViewPager的主界面容器
PagerAdapter mPagerAdapter;
ViewPager mViewPager;
TabLayout mTabLayout;
////兩個 Fragment
RecListFragment homeFragment;
RecListFragment collectionFragment;
……
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_commontab);
   initView();
}
private void initView() {
  ///初始化ViewPager等控件
  ……
   
      mPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
          @Override
          public Fragment getItem(int position) {
              switch (position) {
/////////////////////////使用第一個Fragment
                  case 0:
                  default:  
                      if(homeFragment==null){
                          homeFragment = new RecListFragment();
                      }
                      return homeFragment;
///////////////////////////使用第二個Fragment
                  case 1: 
                      if (collectionFragment==null){
                          collectionFragment =  new RecListFragment();
                      }
                      return collectionFragment;
              }
          }
          …………
      };
      mViewPager.setAdapter(mPagerAdapter);
      mTabLayout.setupWithViewPager(mViewPager);
}
}

上面的結(jié)構(gòu)大家應該都比較熟悉,那么,我測試后給大家列出幾種情況下Activity和兩個Fragment的生命周期方法執(zhí)行情況:

  • 本Activity被startActivity()等方式啟動:


  • 本Activity由于點擊HOME、MENU等彈回桌面時:
    ?下圖是Android N版本下點擊MENU按鍵時的操作截圖(類似平常我們長按HOME鍵彈出運行中程序):



  • 本Activity啟動其他Activity,并被完全遮掩覆蓋時:


  • Activity退出時:


  • 總結(jié):

    • 啟動Activity時,由于ViewPager默認采用預加載的方式,所以,雖然默認顯示第一個Fragment,第二個Fragment依然會被加載和初始化。
    • 啟動Activity時,Activity先執(zhí)行了它本身的onCreate()->onStart()->onResume(),之后,ViewPager才真的開始運行起來并加載指定給他的Fragment,F(xiàn)ragment才開始它的構(gòu)造方法和生命周期方法的執(zhí)行。
    • 不論Activity是被另一個Activity覆蓋、還是App被返回桌面、或者是彈出啟動中應用的界面遮掩Activity,此時,Activity都是被完全遮掩(彈出Dialog等不全屏的窗體時,Activity屬于部分被遮掩),此時,Activity都會調(diào)用到onStop()方法,并且這種方式中Fragment與Activity的生命周期方法執(zhí)行順序都一樣(如上面截圖)。
    • 當Activity退出時,①Fragment執(zhí)行onStop()后,Activity再執(zhí)行onStop() ②Fragment執(zhí)行onDestroyView()和onDestroy()后,Activity才執(zhí)行onDestroy()
  • 另外,如果到這里,忘了Fragment整個生命周期是怎么樣的,這里,本人附上<u>Fragment的生命周期圖解</u>

    Fragment的生命周期


  1. 關于v4、v7、v13兼容包:
  • Android Support v4:為了兼容Android1.6及更高版本而設計的。(eclipse新建工程時,都默認帶有v4包)
  • Android Support v7:為了兼容Android2.1及以上版本而設計的,但不包含更低,故如果不考慮1.6,我們可以采用再加上這個包,另外注意,v7是要依賴v4這個包的,即,兩個得同時被包含。
  • Android Support v13:為了Android 3.2及更高版本的,一般我們都不常用,平板開發(fā)中能用到。

  1. Activity中,onSaveInstanceState() 和 onRestoreInstanceState() 兩個方法何時執(zhí)行?
  • onSaveInstanceState():
    • 執(zhí)行情況:當Activity處于容易被殺死的狀態(tài)下時。
    1. 資源相關的系統(tǒng)配置發(fā)生改變時,如:手機突然橫屏等系統(tǒng)配置變化;
    2. 點擊HOME鍵退回桌面時(此時應用處于后臺,Activity可能因內(nèi)存不足而被回收);
    3. 長按HOME鍵啟動任務管理器程序的時候(同樣,Activity處于后臺狀態(tài),容易被回收);
    4. 本Activity啟動其他Activity時(原Activity進入后臺后,容易被回收)。
    5. 鎖屏時。
    • <u>onSaveInstanceState()在onStop()方法前發(fā)生。</u>
  • onRestoreInstanceState():
    • 執(zhí)行情況:在Activity以異常情況下終止后重新創(chuàng)建時
    • 需理解以下這種情況:當正在顯示activity A的時候,用戶按下HOME鍵回到主界面,然后用戶緊接著又返回到activity A,這種情況下activity A一般不會因為內(nèi)存的原因被系統(tǒng)銷毀,故activity A的onRestoreInstanceState方法不會被執(zhí)行。
    • <u>onRestoreInstanceState()在onStart()方法之后執(zhí)行。</u>
    • 注意:onRestoreInstanceState的bundle參數(shù)也會傳遞到onCreate方法中,你也可以選擇在onCreate方法中做數(shù)據(jù)還原。
  • 示例截圖(手機突然橫屏):



  1. Java中,String、StringBuffer、StringBuilder三者的區(qū)別:
  • String 相對與后面兩者來說,指向的內(nèi)存空間是不可變的,也就是每次賦值,String對象指向的內(nèi)存地址都將發(fā)生改變,相當于摒棄原地址、重建新地址。
  • StringBuffer和StringBuilder都是指向可變大小的內(nèi)存空間的,即每次append(String)等字符串更改操作,都不會使其重新申請新一塊內(nèi)存地址然后指向它。
  • StringBuffer相對于StringBuilder來說,字符串的修改、添加等操作相對較慢(也就是字符串操作效率低一點),但是StringBuffer線程安全,對于多個線程訪問時,StringBuffer通過內(nèi)部鎖機制進行多線程同步,所以,StringBuffer更加安全。
  • StringBuilder則沒有了線程安全的功能,于是它提高了字符串操作的效率,但是線程不安全。

  1. Android自定義View的三個構(gòu)造方法(一參,二參,三參)的區(qū)別:
  • public View(Context context):在java代碼創(chuàng)建視圖的時候被調(diào)用
  • public View (Context context, AttributeSet attrs):在xml創(chuàng)建,但是沒有指定Style的時候被調(diào)用
  • public View (Context context, AttributeSet attrs, int defStyle):在xml中創(chuàng)建、并且指定了Style時調(diào)用

  1. onWindowFocusChanged() 和 onFocusChanged()的用法和區(qū)別?
  • 區(qū)別
    1. onWindowFocusChanged()在window$callback 和 View.java 兩個類中,主要作為Window的焦點獲取和失去時的一個事件監(jiān)聽,而window$callback當中所使用的該方法實際上也來自View.java,而onFocusChanged()只位于View.java類中,主要作為View的焦點獲取與失去時的一個事件監(jiān)聽。
  • 兩者的具體用法
    • <u>onWindowFocusChanged()調(diào)用時機</u>:由于window的焦點和view的焦點是分離的,所以這個方法被回調(diào)的時機是:窗口獲取或者失去焦點時,如:
      1. 當啟動Activity時,Activity當中的窗口獲取到焦點時,即onResume()方法之后,會調(diào)用一次onWindowFocusChanged()。
      2. 從Activity跳轉(zhuǎn)到另一個Activity,新的窗口會獲取焦點,舊的Activity的窗口就會失去焦點。
      3. 打開軟鍵盤進行輸入,窗口失去焦點。
      4. 軟鍵盤輸入完畢消失,窗口重新獲取焦點。
      5. 應用被移到后臺,窗口失去焦點。
      6. 應用重新返回前臺,窗口重新獲取焦點。
    • <u>onWindowFocusChanged()妙用(部分)</u>:
      1. 重寫onWindowFocusChanged() 方法并在里面調(diào)用getWidth()和getHeight()方法,可以正確得到整個視圖的寬高。【原理:onResume()完畢后Window才獲取焦點,此時視圖已經(jīng)加載完畢,這樣就避免了視圖未加載完成時獲取錯誤寬高】
      2. 當由于HOME鍵等其他原因?qū)е翧pp移到后臺時,對數(shù)據(jù)的臨時保存,此時,除了可以使用onPause()、onStop()、onSaveInstanceState()方式,還可以使用onWindowFocusChanged()方法來處理數(shù)據(jù)的自定義保存操作。

    • <u>onFocusChanged()用法</u>:在EditText、Button等本身可獲取焦點的控件或者設置了可獲取焦點、可通過觸摸事件獲取焦點的自定義View、TextView等控件下可以重寫來處理焦點獲取和失去焦點時的事件。【可以用于喚起系統(tǒng)軟鍵盤:通過:重寫View.onCreateInputConnection()等方法獲取一個軟鍵盤連接器并可以從中設定彈出的鍵盤形式,然后利用(InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);獲取InputMethodManager對象,就可以調(diào)用它來啟動和關閉軟鍵盤】

  1. 讓Activity不隨著屏幕旋轉(zhuǎn)而銷毀
  • 法一:(API13后有效)在manifest的Activity標簽添加
    android:configChanges="orientation|keyboardHidden"

    注意:
    orientation表示屏幕方向,keyboardHidden鍵盤可見性(是否調(diào)出鍵盤)
    旋轉(zhuǎn)屏幕時,此時Activity只調(diào)用:onConfigurationChanged(),不會調(diào)用onRestore…onSave…

  • 法二:設置App不可轉(zhuǎn)屏
    android:screenOrientation=”landscape|portrait”表示:橫向或者縱向,二選一

  1. Fragment與Activity的生命周期方法執(zhí)行順序:
    詳細可以參考本人的另一篇文章:Android -- Fragment 基本用法、生命周期與細節(jié)注意

  1. 我們在Android中寫xml中控件或者布局、主題等屬性值的時候,會遇到“@android:drawable/xxx”,“@+id/yyyy”,“?attr/colorPrimary”等樣子的屬性值引用寫法。那么,這幾種寫法的區(qū)別是什么?
  • @+id/ thing_id這個好理解,有個 + 號表示需要在R.xml中新添加一個這個id值,然后,供以后被調(diào)用。【Java中調(diào)用:findViewById("R.id.thing_id"); xml中調(diào)用:android:toLeftOf="@id/thing.id" 等方式】
  • @[+][package:]type/name:這種形式,表示引用一個已存在的屬性值。
    1. @drawable/img1:表示引用我們自定義的drawable屬性值(引用自己的內(nèi)部資源)
    2. @+android:drawable/search_icon:帶了一個 + 號,表示引用android.R.drawable中的資源,如果此資源不存在,則在自己項目的R.drawable中創(chuàng)建一個search_icon的標識。
    3. @android:drawable/search_icon:表示引用系統(tǒng)(android.*)的資源,沒有則報Android.content.res.Resource$NotFoundException的異常。
  • ?[namespace:type/]name:表示引用屬性。【不需要明確指出它的類型】
    如:①?android:attr/android:textDisabledColor 和②?android:textDisabledColor,兩者是一樣的,前者聲明了索要屬性值的類型而后者沒有說明,兩者都可以獲取系統(tǒng)中的主題屬性,只要我們提供想要的屬性名,它將會在主題中被查找(因為資源工具知道需要的屬性資源,所以我們不需要顯式聲明這個屬性)。

感謝閱讀~

本篇文章將持續(xù)更新~
喜歡的讀者可以點個關注,本人將持續(xù)發(fā)布Android相關文章~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容