4

我需要一个GraphQl在 android 中使用的简单示例。

如何GraphQl在 android 中使用 from(教程)。

4

4 回答 4

7

为了使用 GraphQL(通常),您需要两件事:

1. GraphQL 服务器

有几种方法可以解决这个问题。当然,您可以简单地用您喜欢的任何服务器端语言自己实现一个。其他(更快)的方法是利用现有工具并使用诸如graphql-upcreate-graphql-server 之类的服务甚至诸如Graphcool之类的服务生成 GraphQL API (免责声明:我为他们工作)。

2. 一个 GraphQL 客户端库

虽然这不是绝对必要的,你也可以通过简单的 HTTP 与 GraphQL 服务器进行交互(在 POST 请求的主体中发送你的查询和突变),使用像缓存或缓存这样的重复工作的现有工具肯定是有益的。 UI 集成让您轻松应对。目前最受欢迎的 GraphQL 客户端之一是Apollo,他们也在积极开发 Android 版本。不过,这还没有正式发布。因此,您现在要么必须使用他们现有的开发版本,要么选择使用纯 HTTP 的前一种方法。

于 2017-04-08T10:23:50.857 回答
4

这是从客户端查询 GraphQl 的示例。在此示例中,我使用的是 Retrofit 2:

// QueryHelper.java
// This line below is the simple format of Gql query
query = "query{me{name, location, majorOfInterest,profilePhoto{url(size: 400) }}}";

//Post the query using Retrofit2
GqlRetrofitClient.getInstance(getContext()).fetchUserDetails(new GqlQueryRequest(queryUserDetails)).enqueue(new Callback<UserDetails>() {
  @Override
  public void onResponse(Call<UserDetails> call, Response<UserDetails> response) {
     //OnResponse do something();       
     }

  @Override
  public void onFailure(Call<UserDetails> call, Throwable t) {
    Log.d(TAG, "Failed to fetch User details");
    }
    });


//GqlClient.java
public class GqlRetrofitClient {
public static final String BASE_URL = BuildConfig.DOMAIN;
private static GqlRetrofitClient sInstance;
private GqlRetrofitService mGqlRetrofitService;

Gson gson = new GsonBuilder().create();

  private GqlRetrofitClient(final Context context) {
    // Network Interceptor for logging
    HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
    httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .addNetworkInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request request = chain.request().newBuilder()
                            .addHeader("X-User-Token", "AUTH_TOKEN")
                            .addHeader("X-User_Email", "Email")
                            .addHeader("content-type", "application/json")
                            .build();
                    return chain.proceed(request);
                }
            })
            .addInterceptor(httpLoggingInterceptor)
            .build();

    // Retrofit initialization
    final Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .client(okHttpClient)
            .build();

    mGqlRetrofitService = retrofit.create(GqlRetrofitService.class);
 }

  // Create an instance of GqlRetrofitClient to create retrofit service
  public static GqlRetrofitClient getInstance(Context context){
    if(sInstance == null){
        sInstance = new GqlRetrofitClient(context.getApplicationContext());
    }
    return sInstance;
  }

  // Method call to get User details
  public Call<UserDetails> fetchUserDetails(GqlQueryRequest queryUserDetails){
    return mGqlRetrofitService.getUserDetails(queryUserDetails);
  }
}

//GqlRetrofitService.java
public interface GqlRetrofitService{
  @POST("/api/graph.json")
  Call<UserDetails> getUserDetails(@Body GqlQueryRequest body);
}
于 2017-07-15T20:36:48.477 回答
0

personally I use Retrofit and I took this Link Credits and changed some things.

This is the code:

In File "GraphQLConverter.java":

public class GraphQLConverter extends Converter.Factory {

    private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");

    private GraphQueryProcessor graphProcessor;
    private final Gson mGson;

    private GraphQLConverter(Context context) {
        graphProcessor = new GraphQueryProcessor(context);
        mGson = new GsonBuilder()
                .enableComplexMapKeySerialization()
                .setLenient()
                .create();
    }

    public static GraphQLConverter create(Context context) {
        return new GraphQLConverter(context);
    }

    /** Override Converter.Factory Methods **/
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        return null;
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        if(type == QueryContainerBuilder.class){
            return new GraphRequestConverter(methodAnnotations);
        } else {
            return null;
        }
    }

    /** RequestConverter Class **/
    private class GraphRequestConverter implements Converter<QueryContainerBuilder, RequestBody> {

        private Annotation[] mAnnotations;

        private GraphRequestConverter(Annotation[] annotations) {
            mAnnotations = annotations;
        }

        @Override
        public RequestBody convert(@NonNull QueryContainerBuilder containerBuilder) {
            QueryContainerBuilder.QueryContainer queryContainer = containerBuilder
                    .setQuery(graphProcessor.getQuery(mAnnotations))
                    .build();
            return RequestBody.create(MEDIA_TYPE, mGson.toJson(queryContainer).getBytes());
        }

    }
}

In File "GraphQuery.java":

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GraphQuery {

    String value() default "";

}

In File "GraphQueryProcessor.java":

class GraphQueryProcessor {

    private static final String TAG = GraphQueryProcessor.class.getSimpleName();
    // GraphQl Constants
    private static final String EXT_GRAPHQL = ".graphql";
    private static final String ROOT_FOLDER_GRAPHQL = "graphql";

    private final Map<String, String> mGraphQueries;
    private Context mContext;

    GraphQueryProcessor(Context context) {
        mGraphQueries = new WeakHashMap<>();
        mContext = context;
        populateGraphQueries(ROOT_FOLDER_GRAPHQL);
    }

    /** Package-Private Methods **/
    String getQuery(Annotation[] annotations) {
        if(mGraphQueries == null || mGraphQueries.isEmpty()){
            populateGraphQueries(ROOT_FOLDER_GRAPHQL);
        }

        GraphQuery graphQuery = null;
        for (Annotation annotation : annotations) {
            if (annotation instanceof GraphQuery) {
                graphQuery = (GraphQuery) annotation;
                break;
            }
        }

        if (graphQuery != null) {
            String fileName = String.format("%s%s", graphQuery.value(), EXT_GRAPHQL);
            if (mGraphQueries != null && mGraphQueries.containsKey(fileName)) {
                return mGraphQueries.get(fileName);
            }
        }
        return null;
    }

    /** Private Methods **/
    private void populateGraphQueries(@NonNull String path) {
        try {
            String[] paths = mContext.getAssets().list(path);
            if (paths != null && paths.length > 0x0) {
                for (String item : paths) {
                    String absolute = path + "/" + item;
                    if (!item.endsWith(EXT_GRAPHQL)) {
                        populateGraphQueries(absolute);
                    } else {
                        mGraphQueries.put(item, getFileContents(mContext.getAssets().open(absolute)));
                    }
                }
            }
        } catch (IOException ioE) {
            BaseEnvironment.onExceptionLevelLow(TAG, ioE);
        }
    }

    private String getFileContents(InputStream inputStream) {
        StringBuilder queryBuffer = new StringBuilder();
        try {
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            for (String line; (line = bufferedReader.readLine()) != null; )
                queryBuffer.append(line);
            inputStreamReader.close();
            bufferedReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return queryBuffer.toString();
    }

}

In File "QueryContainerBuilder.java":

public class QueryContainerBuilder {

    // Mask Types
    private static final byte MASK_REPLACE_QUERY_ARGUMENTS =    0b1;     // Invece di inviare il json con le variabili va a inserirle nella query i valori sostituendo i tipi degli argomenti.
    private static final byte MASK_REPLACE_EXPLICIT_QUOTES =    MASK_REPLACE_QUERY_ARGUMENTS << 0b1;     // Alle stringhe non vengono automaticamente messe le virgolette ma devono essere aggiunte nei valori passati per le variabili.
    private static final byte MASK_REPLACE_WITH_PLACEHOLDERS =  MASK_REPLACE_EXPLICIT_QUOTES << 0b1;     // Va a sostituire i placeholders "<key_var_name>" presenti nella query con i valori delle variabili.

    private QueryContainer mQueryContainer;
    private byte mMask;

    public QueryContainerBuilder() {
        mQueryContainer = new QueryContainer();
    }

    /** Setter Methods **/
    public QueryContainerBuilder setQuery(String query) {
        mQueryContainer.setQuery(query);
        return this;
    }

    public QueryContainerBuilder setReplaceQueryArguments(){
        mMask = MASK_REPLACE_QUERY_ARGUMENTS;
        return this;
    }

    public QueryContainerBuilder setReplaceExplicitQuotes(){
        mMask =  MASK_REPLACE_QUERY_ARGUMENTS | MASK_REPLACE_EXPLICIT_QUOTES;
        return this;
    }

    public QueryContainerBuilder setReplaceWithPlaceholders(){
        mMask = MASK_REPLACE_QUERY_ARGUMENTS | MASK_REPLACE_WITH_PLACEHOLDERS;
        return this;
    }

    /** Public Methods **/
    public QueryContainerBuilder putVariable(String key, Object value) {
        mQueryContainer.putVariable(key, value);
        return this;
    }

    public boolean containsVariable(String key) {
        return mQueryContainer.containsVariable(key);
    }

    /** Builder Methods **/
    public QueryContainer build() {
        if((mMask & MASK_REPLACE_QUERY_ARGUMENTS) != 0x0){
            if((mMask & MASK_REPLACE_WITH_PLACEHOLDERS) != 0x0){
                mQueryContainer.replaceVariablesPlaceholdersInQuery();
            } else {
                mQueryContainer.replaceVariablesInQuery(mQueryContainer.mVariables, 0x0);
            }
            mQueryContainer.mVariables = null;
        }
        return mQueryContainer;
    }

    /** Public Static Classes **/
    public class QueryContainer {

        @SerializedName("variables")
        private LinkedHashMap<String, Object> mVariables;
        @SerializedName("query")
        private String mQuery;

        QueryContainer() {
            mVariables = new LinkedHashMap<>();
        }

        /** Private Methods **/
        private void setQuery(String query) {
            mQuery = query;
        }

        private void putVariable(String key, Object value) {
            mVariables.put(key, value);
        }

        private boolean containsVariable(String key) {
            return mVariables != null && mVariables.containsKey(key);
        }

        private void replaceVariablesInQuery(LinkedHashMap<String, Object> map, int index){
            if(!TextUtils.isEmpty(mQuery) && map.size() > 0x0){
                List<String> keys = new ArrayList<>(map.keySet());
                for(String key : keys){
                    Object value = map.get(key);
                    if(value instanceof LinkedHashMap){
                        replaceVariablesInQuery((LinkedHashMap<String, Object>) value, index);
                    } else {
                        int i = mQuery.indexOf(key + ":", index) + key.length() + 0x1;
                        int z;
                        if(keys.indexOf(key) < keys.size() - 0x1){
                            z = mQuery.indexOf(",", i);
                        } else {
                            z = mQuery.indexOf(")", i);
                            int x = mQuery.substring(i, z).indexOf('}');
                            if(x != -0x1){
                                if(mQuery.substring(i, i + 0x4).contains("{")){
                                    x++;
                                }
                                z -= ((z - i) - x);
                            }
                        }

                        String replace;
                        if((mMask & MASK_REPLACE_EXPLICIT_QUOTES) != 0x0){
                            replace = String.valueOf(value);
                        } else {
                            replace = value instanceof String ?
                                    "\"" + value.toString() + "\"" : String.valueOf(value);
                        }
                        String sub = mQuery.substring(i, z)
                                .replaceAll("[\\\\]?\\[", "\\\\\\[").replaceAll("[\\\\]?\\]", "\\\\\\]")
                                .replaceAll("[\\\\]?\\{", "\\\\\\{").replaceAll("[\\\\]?\\}", "\\\\\\}");
                        mQuery = mQuery.replaceFirst(sub.contains("{}") ? sub.replace("{}", "").trim() : sub.trim(), replace);
                        index = z + 0x1;
                    }
                }
            }
        }

        private void replaceVariablesPlaceholdersInQuery(){
            if(!TextUtils.isEmpty(mQuery) && mVariables.size() > 0x0){
                for(String key : mVariables.keySet()){
                    mQuery = mQuery.replaceFirst("\\<" + key + "\\>", mVariables.get(key) != null ? mVariables.get(key).toString() : "null");
                }
                mVariables = null;
            }
        }

    }

}

Put your queries in a "graphql" directory in the "assets" folder with the ".graphql" extension for your query files. You can change the extension or the folder by changing the "EXT_GRAPHQL" or "ROOT_FOLDER_GRAPHQL" constants in "GraphQueryProcessor". You can use these formats for the queries:

query {
    myQuery(param1: <myParam1>) {
        ....
    }
}

If you use this format you need to use "MASK_REPLACE_WITH_PLACEHOLDERS" in your QueryContainerBuilder. Also you need to pass as the HashMap key the name of the placeholder without the "<...>", so in this case "myParam1".

The others format are just common GraphQL queries, like:

query ($p1: String!) {
    muQuery(p1: $id) {
        ...
    }
}

With this format you can use normal QueryContainerBuilder behaviour (no mask applyed, so it will pass and generate the "variables" json object.) or the "MASK_REPLACE_QUERY_ARGUMENTS" which will remove the "$id" and place the value.

When you init Retrofit add the "GraphQLConverter". Take care about the "ConvertFactories" order! You can put more ConvertFactory, but they consume the input so if in this case you put "Gson" before "GraphQL" the "GsonConverted" will consume the input data:

new Retrofit.Builder()
                .baseUrl(mBaseUrl)
                .addConverterFactory(GraphQLConverter.create(context))
                .addConverterFactory(GsonConverterFactory.create(gson))
                .client(getBaseHttpClient(interceptor))
                .build();

In your Retrofit API:

@POST(AppConstants.SERVICE_GQL)
@GraphQuery(AppConstants.MY_GRAPHQL_QUERY_FILENAME)
fun callMyGraphQlQuery(@Body query: QueryContainerBuilder): Call<MyGraphQlResponse>

Call examples:

val query = QueryContainerBuilder()
                .putVariable("myParam1", myValue)
                .setReplaceWithPlaceholders()
            createService(API::class.java).callMyGraphQlQuery(query)

val query = QueryContainerBuilder()
            .putVariable("p1", myValue)
            .setReplaceQueryArguments()
        createService(API::class.java).callMyGraphQlQuery(query)


val query = QueryContainerBuilder()
            .putVariable("p1", myValue)
        createService(API::class.java).callMyGraphQlQuery(query)

Idk if the "MASK_REPLACE_QUERY_ARGUMENTS" works right, I used it only 2/3 times and then the back-end was changed and wrote better.

I did those cases (masks) to process the queries because I had this 3 case of queries with the back-end I was calling. You can just add others query processing behavior just by adding another mask and the code in the "QueryContainerBuilder".

If anyone use this code and change it making it better, please write me the changes so I will change the code in my library too.

Thanks you, have a nice coding and day :D

Bye!

于 2020-08-28T08:47:22.763 回答
0

在您的清单中添加

<uses-permission android:name="android.permission.INTERNET"/>

你的依赖

// Kotlin Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4'

//OkHttp
implementation ("com.squareup.okhttp3:okhttp:3.12.12"){
  force = true //API 19 support
}
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.12'

//retrofit
implementation "com.squareup.retrofit2:retrofit:2.7.1"
implementation "com.squareup.retrofit2:converter-scalars:$2.7.1"

还兼容 Java 8

android {

    ...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }
}

随着服务

import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.Headers
import retrofit2.http.POST

interface GraphQLService {

    @Headers("Content-Type: application/json")
    @POST("/")
    suspend fun postDynamicQuery(@Body body: String): Response<String>
}

你可以创建一个对象

import retrofit2.Retrofit
import retrofit2.converter.scalars.ScalarsConverterFactory

object GraphQLInstance {

    private const val BASE_URL: String = "http://192.155.1.55:2000/"

    val graphQLService: GraphQLService by lazy {
        Retrofit
            .Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(ScalarsConverterFactory.create())
            .build().create(GraphQLService::class.java)
    }
}

在活动中,您可以创建此方法

private fun post(userId: String){
    val retrofit = GraphQLInstance.graphQLService
    val paramObject = JSONObject()
    paramObject.put("query", "query {users(userid:$userId){username}}")
    GlobalScope.launch {
        try {
            val response = retrofit.postDynamicQuery(paramObject.toString())
            Log.e("response", response.body().toString())
        }catch (e: java.lang.Exception){
            e.printStackTrace()
        }
    }
}

您可以查看GitHub 中的示例我的帖子

注意:如果你需要一个突变应该改变这一行

paramObject.put("query", "query {users(userid:$userId){username}}")

paramObject.put("query", "mutation {users(userid:$userId){username}}")
于 2021-02-02T16:06:14.597 回答