background

I’ve been working for a new company for about two months, and I was about to start a new company when I found out that the server was using Apollo-GraphQL to process network requests from clients, so I asked clients to use the same graphQL. After all, the previous Android clients were retrofit+ OKHTTP + RxJava, and I took the time to encapsulate them into what I thought was a pretty good network layer architecture. Only to find that they don’t use it anymore when they come to their new company. Now I do Android development, handling network requests, not only 100%, but also some 70% or 80% use this set, the rest is basically OKHttp package library; I’ve got 10,000 horses racing inside me, but I’ve got to get on with it. To be fair, there are few articles on the web about using GraphQL on Android, as shown by the library’s star count.

For those of you who have used Postman, you may have noticed that it now supports GraphQL as well.

(Disclosure: OKHttp is also used in the Apollo-Android library network layer, which shows how powerful OKHttp is.)

Review the RESTful

Before we get to GraphQL, let’s talk about RESTful, because graphQL was created to replace RESTful. We all know that most servers are designed and developed in a RESTful fashion, as are clients (Retrofit is a web framework similar to RESTful), but how do we understand RESTful? REST is an abbreviation of Representational State Transfer, a set of architectural constraints and principles that an architecture complies with. This is called RESTful architecture, and it is a software architecture style, not a standard, that is, not mandatory. After reading these difficult terms you may still not be quite clear, with zhihu god Lvony’s words summed up.

Use URLS to locate resources and HTTP verbs (GET,POST,DELETE,PUT) to describe operationsCopy the code

The body of REST is a resource, such as an image, a text, a video, something that actually exists on the web, and the URL is used to point to that resource, such as:

https://www.baidu.com/user
Copy the code

This API is used to operate on the user resource. It uses a noun to specify the resource. There is no declaration about the operation. Because the basic operations are add, delete, change, and check, if you don’t use restful, you get four interfaces:

https://www.baidu.com/add_user
https://www.baidu.com/update_user
https://www.baidu.com/delete_user
https://www.baidu.com/query_user
Copy the code

Obviously this design style is too redundant, so what if we switched to restful? Operations on resources are nothing more than adding, deleting, modifying, or searching resources. In restful terms, each HTTP verb describes an operation:

  • GET: indicates the query operation
  • POST: Indicates the operation to be added
  • DELETE: indicates the corresponding deletion operation
  • PUT: indicates the update operation

So all we need is one interface. Some students may have questions. In the actual development, many query operations use POST instead of GET, because some queries require a lot of parameters. If you use GET, the interface will be ugly and unsafe. So I’m not RESTful here? As mentioned above, RESTful is a software architecture style that is not enforced, that is, it is only recommended that developers do so, and there is no problem with appropriate flexibility. There is also a classic interpretation of RESTful on Zhihu.

If you look at the HTTP url and you look at the HTTP method and you look at the HTTP status code and you know what's going to happenCopy the code

There are many HTTP states, and seeing some of them will give you an idea of what the result of the request is.

  • 200 OK, the request is successful. The meaning of success depends on the HTTP method
  • 201 Created, the request was successful and a new resource was Created as a result. This is usually the response that is returned after a POST request, or some PUT request
  • 204, OK, resource deleted successfully
  • 304 Not Modified, the resource is Not updated, the client can use the cache
  • 400 Bad Request: 1. The semantics are incorrect and the current Request cannot be understood by the server. 2. The request parameters are incorrect
  • 401 Unauthorized, the current request requires user authentication
  • 403 Forbidden, the server understands the request but refuses to execute it. Unlike the 401 response, authentication does not help
  • 404 Not Found, the request failed. The requested resource was Not Found on the server
  • 414 URI Too Long: The request URI is longer than the server can interpret. Common cases include: A form submission that should have used the POST method becomes a GET method, resulting in a Query String that is Too Long
  • 500 Internal Server Error. Did the Server encounter a problem that it did not know how to handle
  • 501 Not Implemented, this request method is Not supported by the server and cannot be processed. Only GET and HEAD require server support

GraphQL is a data query language developed internally by Facebook in 2012 and opened source in 2015. It aims to provide an alternative to RESTful architecture. GraphQL can be seen as Graph+QL. QL is short for Query Language, so GraphQL stands for visual (graphical) Query Language, and is an API syntax that describes how the client requests data from the server. Given that SQL(Structured Query Language) is short for Structured Query Language, you should get a pretty good idea of what GraphQL is, but note that even though GraphQL is called a Query Language, it has nothing to do with databases. When data from MySQL, NoSQL and other databases to check out, how to front-end or mobile terminal? At this time, GraphQL stepped forward. It acted on the data interface, allowing the client to freely filter and obtain the pre-determined data of the server, improving the flexibility of the API interface. For example, the back-end can query the four fields A,B,C and D from the data, and use GraphQL to process the interface, so that the client can obtain data at will. It can be A, B, B,C, etc. You don’t need to say hello to the server buddy anymore. Here’s another reason to use GraphQL on mobile: With RSTful style web requests, you can see that there are some situations where this can be a problem.

  1. One request corresponds to one resource, so some pages with a large number of network requests will bring a great burden. Usually, the home page of the application will carry a lot of content, so that when the application is opened, a lot of network requests will be sent out, which will bring complex processing when obtaining the response. The most obvious is when the chrysanthemum (loading animation) should disappear;
  2. Another disgusting situation is that the parameters and return values of some API interfaces will be determined after design. If the interface fields or return values need to be modified due to design or business changes, the new version of APP can be used if the interface is changed. However, the old version of the app may call this interface error (can not force the user to upgrade every time), serious direct APP crash; If the original interface is not modified, a new interface must be added.
  3. The most annoying is to maintain the interface document, in the long development process, need to update the API document according to the interface changes, this pain we should know.

Of course, there are other drawbacks, which I won’t go into here, but none of them exist in Apollo GraphQL.

  1. In graphQL, multiple requests become one request, so four interfaces are called at the same time before the home page. Now you only need to call one interface, and you don’t need to worry about chrysanthemums anymore.
  2. At the same time, the fields and parameters in graphQL can be customized. Before, the server defines what the client sends and returns to the client. Now it’s the other way around. The client can define what fields it wants to pass and what return values it needs. This makes the API interface very flexible, so that if the new version wants to change the interface, it only needs to add fields.
  3. Graphql also generates interface documentation and supports export

Graphql is just a query language for apis. Unlike REST apis, which require multiple urls to query multiple data, it can get all the data you need in one request, no more, no less. / Use GraphQL/With that said, let’s see how to access GraphQL in Android projects. GraphQL comes in many versions, as shown in the figure below.

The library that supports Android is Apollo-Android, which requires Gradle version 5.1.1 or higher. When I first started using this library, was I worried that it supported OKHttp? Does RxJava support? This is the first step to create a new project, and the first step to do is to add the plugin dependency in the project root directory build.gradle:

 dependencies {
        classpath 'com. Apollographql. Apollo: Apollo - gradle - plugin: 1.2.1'
    }
Copy the code

Step 2: Add plugin dependencies and library dependencies to app Module build.gradle. Remember that plugin dependencies must come after Android plug-ins:

apply plugin: 'com.apollographql.android'

dependencies {
    implementation 'com. Apollographql. Apollo, Apollo - runtime: 1.2.1'
}
Copy the code

Step 3: If you want to use RxJava (which you definitely will), add Apollo-rx2-support.

dependencies {
    implementation ('com. Apollographql. Apollo: Apollo - rx2 - support: 1.2.1')
    implementation 'the IO. Reactivex. Rxjava2: rxandroid: 2.1.1'
}
Copy the code

The rxJava library does not need to be added. The Apollo-rx2-support library already relies on it. Step 4: Since the Apollo plug-in generates Java code from the.graphQL file and schema.json file, and the code generator uses JetBrains Annotations, you need to add dependencies.

dependencies {
  compileOnly '13.0' org. Jetbrains: annotations.
  testCompileOnly '13.0' org. Jetbrains: annotations./ / optional}Copy the code

The next step is to add the.graphQL and schema.json files mentioned above. These two configuration files are actually provided to you by the backend development, and they are required to correspond to each other.

  • Create a folder in SRC /main of your App Module that is at the same level as your Java folder. I’ll call it GraphQL. Then create a new three-tier directory like com/mango/ Apollo
  • Add a schema.json file to the same directory as before. Json file that describes your GraphQL API, all the fields and input parameters. This file is a bit scary. Even a simple interface can be tens of thousands of bytes
  • It’s a GraphQL query file. These queries are used by Apollo CodeGen to generate the data structures that return the data. Most importantly, you can also customize the query methods in this file. Including creating a new.graphQL file

Next, build project in app/build/generated/source/Apollo/classes/debug below find the generated several files (through the website. Graphql files, and schema. Json file generated), as shown in figure:

Here’s a quick look at the contents of the.graphQL file.

query FeedQuery($type: FeedType! .$limit: Int!) {
  feedEntries: feed(type$type.limit$limit) { id repository { ... RepositoryFragment } postedBy { login } } } fragment RepositoryFragment on Repository { name full_name owner { login } }Copy the code

Look at the first sentence:

query FeedQuery($type: FeedType! .$limit: Int!) 
Copy the code
  • Query: Indicates that the operation type is query. There are other types, such as mutation or subscription, that describe what type of operation you intend to do. Operation types are required unless you use query shorthand syntax
  • FeedQuery: Represents the operation name, which is a meaningful and unambiguous name for your operation
  • The symbol defines the variable, the variable name is type, the variable type is FeedType, plus one! The meaning is not empty

Then look at the second sentence:

feedEntries: feed(type$type.limit$limit)

Copy the code
  • FeedEntries: is an alias for the feed, and the final body name returned is feedEntries
  • Feed: If no alias is defined, the body name returned is feed
  • type: type

Let’s go ahead and look at the structure.

{ id repository { ... RepositoryFragment } postedBy { login } }Copy the code
  • “Id”, “repository”, “postedBy” : these are all fields. The server determines the type of the returned field. For example, “ID” may return a string or an int
  • The repository field points to an Object type, which is RepositoryFragment and is defined as follows
fragment RepositoryFragment on Repository {
  name
  full_name
  owner {
    login
  }
}
Copy the code

Use fragment modification. When you return data with many fields, you can use fragment to organize a group of fields for reuse. Step 5: Initialize ApolloClient. We used to use Retrofit+OKHttp, now it’s Apollo+OKHttp.

  OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .build();
        ApolloClient apolloClient = ApolloClient.builder()
                .serverUrl(BASE_URL)
                .okHttpClient(okHttpClient)
                .build();
Copy the code

Step 6: Initiate the request. Building a request using Apollo is a bit similar to using OKHTTP. Remember how to initiate a request in OKHTTP? Construct a Request, then instantiate a Call, and then initiate the Request. The same thing happened at Apollo.

//Limit and Type are the dynamic parameters in our graphQL query, FeedQuery FeedQuery = feedQuery.builder ().limit(10).type(feedtype.hot).build(); ApolloQueryCall<FeedQuery.Data> queryCall = apolloClient.query(feedQuery); queryCall.enqueue(new ApolloCall.Callback<FeedQuery.Data>() { @Override public void onResponse(@NotNull Response<FeedQuery.Data> response) { FeedQuery.Data data = response.data(); List<FeedQuery.FeedEntry> feedEntries = data.feedEntries();for (FeedQuery.FeedEntry feedEntry : feedEntries) {
            Log.i("MainActivity".""+feedEntry.toString());
        }

    }

    @Override
    public void onFailure(@NotNull ApolloException e) {
        Log.i("MainActivity"."onFailure "+ e.getMessage()); }});Copy the code

Feedquery.feedentry in the response reads as follows:

feedEntries={
            id = 105,
            repository = Repository {
                    fragments = Fragments {
                        repositoryFragment = RepositoryFragment {
                            name = pairhub,
                            full_name = pairhub / pairhub,
                            owner = Owner {
                                login = pairhub
                            }
                        }
                    }
            },
            postedBy = PostedBy {
                login = gustavlrsn
            }
        }
Copy the code

Step 7: Use the following with RxJava:

FeedQuery feedQuery = FeedQuery
                        .builder()
                        .limit(10)
                        .type(FeedType.HOT)
                        .build();
        Rx2Apollo.from(apolloClient.query(feedQuery))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new DefaultObserver<Response<FeedQuery.Data>>(){

                    @Override
                    public void onNext(Response<FeedQuery.Data> dataResponse) {
                        FeedQuery.Data data = dataResponse.data();
                        List<FeedQuery.FeedEntry> feedEntries = data.feedEntries();
                        for (FeedQuery.FeedEntry feedEntry : feedEntries) {
                            Log.i("MainActivity"."feedEntry:"+feedEntry.toString());
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.i("MainActivity"."onError " + e.getMessage());
                    }

                    @Override
                    public void onComplete() {}});Copy the code

Transform ApolloCall(the parent of ApolloQueryCall) into Observable and other conversions as follows:

An example conversion is as follows:

ApolloCall<FeedQuery.Data> apolloCall = apolloClient.query(query);
Observable<Response<FeedQuery.Data>> observable = Rx2Apollo.from(apolloCall);

ApolloPrefetch<FeedQuery.Data> apolloPrefetch = apolloClient.prefetch(query);
Completable completable = Rx2Apollo.from(apolloPrefetch);
Copy the code

Can also be changed to Disposable:

Disposable disposable = Rx2Apollo.from(query).subscribe();
disposable.dispose();
Copy the code

conclusion

There are many other uses of GraphQL, such as database caching, HTTP caching, file uploading, etc. It can greatly simplify our interface requests and make the interface definition more free and open.

Ali P6P7【 android 】 Advanced information sharing + salary job-hopping essential interview questions