介紹

什麼是JSON?,JSON的全名是JavaScript Object Notation,用途是資料交換。之前會使用XML來做資料交換,可是當資料量很多時,XML檔會變很肥大,而JSON是輕量級的資料交換格式。物件是以左大括號 { 開始,右大括號 } 結束。並以key:value的方式來儲存資料,多組的話則以逗號分開,看起來如下

{
  "Data": [
    {
      "Id": "0f4e3749-fdfa-42a3-9023-752e478297db",
      "Name": "雷霆專案"
    },
    {
      "Id": "be92c897-56fa-43b6-9d5b-12aebdc5bbce",
      "Name": "蜂眼專案"
    }
  ]
}

Gson是google開發用來解析JSON格式資料的Library,主要為方便將Java物件序列化至輕量化的JSON格式,或從JSON反序列化至JAVA物件。

OkHttp是一個網路的應用程式框架,可快速實作資料交換的動作,讓Http連線的過程更有效率。使用OkHttp,可以簡化需先設計AsyncTask、輸出入迴圈或是許多執行緒等工作,讓開發的速度加快。官方網址

RxJava最主要的兩個是Observables和Subscribers。Observables會發出事件,而Subscribers處理這一些事件。一個Observable可以發出零個或多個事件,直到結束或出錯。每發出一個事件,就會使用他的subscriber的onNext方法,最後使用subscriber.onCompleted()或subscriber.onError()結束。 有一點要注意的是,如果Observerble沒有Subscriber,那這個Observerble就不會發出事件。 RxJava有一個很棒的優點,假設你要照順序的做處理,用RxJava可輕鬆的完成同步。

gradle設置

加入OkHttp、Gson和RxJava。

compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.google.code.gson:gson:2.3.1'
compile 'io.reactivex:rxandroid:1.2.1'

設置NumberPicker

先設定NumberPicker,將字串加入到NumberPicker,之後要將取得的資料,以NumberPicker的方式呈現。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.royshow.gsontest.MainActivity">

    <NumberPicker
        android:id="@+id/showNumProjectPicker"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="60dp"
        android:layout_marginTop="40dp" >
    </NumberPicker>
</RelativeLayout>

先將NumberPicker放入字串,並初始值。

public class MainActivity extends AppCompatActivity {

    private NumberPicker showNumProjectPicker;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String[] items = {"test1", "test2", "test3", "test4"};

        showNumProjectPicker = (NumberPicker) findViewById(R.id.showNumProjectPicker);
        showNumProjectPicker.setMinValue(0);
        showNumProjectPicker.setMaxValue(items.length - 1);
        showNumProjectPicker.setDisplayedValues(items);
        showNumProjectPicker.setWrapSelectorWheel(false);
        showNumProjectPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
    }
}

OkHttp讀取JSON

在最外面宣告OkHttpClient。

private OkHttpClient client = new OkHttpClient();

然後在OnCreate裡面實作,如下,執行後會發現,是以非同步的方式執行的,再之後要將資料放進NumberPicker,會是一個問題,這裡先不用管,之後會利用RxJava來解決。

Request request = new Request.Builder().url("你的JSON Url").build();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String json = response.body().string();
                Log.d("OkHttp", json);
            }
        });

可以讀取到JSON的資料,如下圖 ParseJson

定義JAVA格式的Data

新增Project.java、ProjectResponse.java和ProjectViewModel.java,定義需要儲存的格式。

Project.java

public class Project {
    public Project() {

    }

    @SerializedName("Id")
    private String id;
    @SerializedName("Name")
    private String name;
    @SerializedName("Code")
    private String code;
    @SerializedName("Description")
    private String description;

    public String getId() {
        return id;
    }

    public Project setId(String id) {
        this.id = id;
        return this;
    }

    public String getName() {
        return name;
    }

    public Project setName(String name) {
        this.name = name;
        return this;
    }

    public String getCode() {
        return code;
    }

    public Project setCode(String code) {
        this.code = code;
        return this;
    }

    public String getDescription() {
        return description;
    }

    public Project setDescription(String description) {
        this.description = description;
        return this;
    }
}

ProjectResponse.java

public class ProjectResponse {

    @SerializedName("Data")
    private List<Project> projects;

    public List<Project> getProjects() {
        return projects;
    }
}

ProjectViewModel.java

public class ProjectViewModel {
    private List<Project> projects;

    public ProjectViewModel(List<Project> projects) {
        this.projects = projects;
    }

    public List<Project> getProjects() {
        return projects;
    }
}

RxJava框架解析JSON格式資料成Gson

現在要將之前解析Json的Code,放到RxJava的方法裡,在這裡新增一個function,將RxJava的方法寫在這裡面,將Json格式解析成Java格式。為了達到同步的效果,才能讓資料先解析後,再放到NumberPicker顯示,使用了RxJava的subscribeOn建立一個新的Thread,以及observeOn保證一定會同步。

public void init() {
        Observable.create(new Observable.OnSubscribe<ProjectViewModel>() {
            @Override
            public void call(Subscriber<? super ProjectViewModel> subscriber) {
                try {
                    Request request = new Request.Builder().url("你的JSON Url").build();
                    Response projectResponse = client.newCall(request).execute();
                    ProjectResponse tmpProjectResponse = new Gson().fromJson(projectResponse.body().string(), ProjectResponse.class);

                    subscriber.onNext(new ProjectViewModel(tmpProjectResponse.getProjects()));
                    subscriber.onCompleted();
                } catch (Exception e) {
                    subscriber.onError(e);
                }
            }
        })
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Action1<ProjectViewModel>() {
                @Override
                public void call(ProjectViewModel projectViewModel) {
                    String[] items = new String[projectViewModel.getProjects().size()];
                    for (int i = 0; i < items.length; i++) {
                        items[i] = projectViewModel.getProjects().get(i).getName();
                    }
                    showNumProjectPicker.setMinValue(0);
                    showNumProjectPicker.setMaxValue(projectViewModel.getProjects().size() - 1);
                    showNumProjectPicker.setDisplayedValues(items);
                    showNumProjectPicker.setWrapSelectorWheel(false);
                    showNumProjectPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
                }
            }, new Action1<Throwable>() {
                @Override
                public void call(Throwable throwable) {
                    Log.d("call", "call: " + throwable.getLocalizedMessage());
                }
            });
    }

執行結果

Result

程式碼已放上Github