Skip to main content
Select a menu in the customizer

Android issue – Avoid non-default constructors in fragments

 image-16

本篇重點

  1. 我遇到了 non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead。
  2. Parcelable 很厲害。

這次開發 side project 用到大量的 fragment出於方便的情況就使用底下的用法

public class MainPageTabFragment extends Fragment {
    String tab;
    User user;
    public MainPageTabFragment(String tab , User user) {
        this.tab = tab;
        this.user = user;
    }
}
// How to use
MainPageTabFragment fragment = new MainPageTabFragment(tab, user);

這在用 debug build 時並沒有遇到任何問題,但是到要用 release build 時卻出現了 “Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead” 這樣的提示錯誤,導致 release build 不過,主因是由於 lint check code ,並同時建議使用 setArgument() 來取代使用含有參數的建構子。

看到這邊如果就只是想單純解決 lint 的問題,那提供最簡單的方法~~~~~那就不要檢查啊,只要在 build.gradle 內加入底下的 code

android {
          lintOptions {
              checkReleaseBuilds false
          }
      }

或是在 fragment 加上 @SuppressLint(“ValidFragment”)
ref:stackoverflow

到這裡,就解決了 lint 導致 release build 的問題,客倌想先交差給老闆的話,這邊可以收工啦,如果還有興趣的話就繼續來研究為何 lint 不建議這樣搞。

主要原因是 fragment 的特性
『All subclasses of Fragment must include a public no-argument constructor. The framework will often re-instantiate a fragment class when needed, in particular during state restore, and needs to be able to find this constructor to instantiate it. If the no-argument constructor is not available, a runtime exception will occur in some cases during state restore.

結論就是在某些情況下(螢幕方向轉換),fragment 被系統重建時,只會呼叫無參數的建構子,你特地用有參數的建構子存起來的參數不會被存起來。看到這邊就知道,過去的用法多危險(雖然我鎖定螢幕方向了,但還是很難保證有啥鬼情況),因此接下來就來重構程式碼

public static MainPageTabFragment newInstance(String tab, User user) {
        MainPageTabFragment fragment = new MainPageTabFragment();
        Bundle argument = new Bundle();
        argument.putString(MainPageTabFragment.KEY_TAB, tab);
        argument.putParcelable(MainPageTabFragment.KEY_LOGIN_USER, user);
        fragment.setArguments(argument);
        return fragment;
    }
// then get argument on onCreate() or onCreateView()
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Bundle argument = getArguments();
        this.tab = argument.getString(KEY_TAB);
        this.loginUser = (User) argument.getParcelable(KEY_LOGIN_USER);
}
// How to use
MainPageTabFragment fragment = MainPageTabFragment.newInstance(tab,user);

看到這邊發現一件糟糕的事情,就是 User 並非 java 基本型別,根本無法放進 Bundle 裡面,那就不能 setArgument(),然後整套重來。

事情當然沒有那麼嚴重,把 User 序列化就可以了,序列化的方法不少(自己寫都可以),但目前最主流的有三種
1. Serializable
2. Parcelable
3. Gson
正當頭痛要用哪一套時,看到這篇文章還有這張圖
image-16

Parcelable 超級快,就決定是你了。(至於為何那麼快,可以詳細閱讀文章,主要是 gson and Serializable 都有需要型別映射的時間)定案後接者就要來實作 User 的 Parcelable interface,這部分主要有幾個模塊

    // 講一下做啥
    @Override
    public int describeContents() {
        return 0;
    }
    // 寫入容器 parcel
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeValue(this.id);
        dest.writeString(this.userName);
        dest.writeString(this.email);
        dest.writeString(this.userID);
        dest.writeString(this.userCode);
        dest.writeString(this.userIcon);
    }
    // 傳入容器 parcel
    protected User(Parcel in) {
        this.id = (Long) in.readValue(Long.class.getClassLoader());
        this.userName = in.readString();
        this.email = in.readString();
        this.userID = in.readString();
        this.userCode = in.readString();
        this.userIcon = in.readString();
    }
    // 提供還原方法
    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        @Override
        public User createFromParcel(Parcel source) {
            return new User(source);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

最後提一下,上面的 Parcelable 根本就是 dirty code ,所以直覺一定有自動化工具,所以分享 android parcelable code generator 給大家。

screenshot

一個 CLICK 就全部自動生出 CODE,感謝各位看到這裡。打烊啦

ref:
http://www.developerphil.com/parcelable-vs-serializable/
http://blog.csdn.net/kroclin/article/details/40902721

http://blog.prolificinteractive.com/2014/07/18/why-we-love-parcelable/