Android Work Manager一次性请求从Worker返回空结果

4

我正在使用Work Manager进行网络调用并获取一些数据,为此我使用了OneTimeRequest。调用在worker类中完美响应,但在观察工作的生命周期所有者中返回空结果。

workManager= WorkManager.getInstance();
    OneTimeWorkRequest.Builder encryptionWork =new OneTimeWorkRequest.Builder(NetworkWorker.class);

    getUsersWorkReq=encryptionWork.setInputData(getWorkerInput(EXPERT_LIST_REQUEST))
            .addTag(Constant.WORK_GETUSER)
            .build();

    workManager.enqueue(getUsersWorkReq);

观察工作类的响应

 workManager.getStatusById(getUsersWorkReq.getId()).observe(this, workStatus -> {
        if (workStatus != null && workStatus.getState().isFinished()) {
            String status=workStatus.getOutputData().getString(Constant.WORK_RESULT);
            String response=workStatus.getOutputData().getString(Constant.WORK_RESPONSE);
            if(status!=null && !status.equalsIgnoreCase("")){
            }
        }
    });

但问题在于,响应字符串始终为空!! 即使工作管理器已完成任务并且网络响应存在于工作器中,但当我使用 setOutputData 时,它会给出空数据。

outPut = new Data.Builder()
                        .putString(Constant.WORK_RESULT,Constant.WORK_SUCCESS)
                        .putString(Constant.WORK_RESPONSE, String.valueOf(response.body()))
                        .build();
                Log.e("WORKER", "onResponse: "+ response.body().getMsg() );
                setOutputData(outPut);

工作者类

public class NetworkWorker extends Worker {
Data outPut;
@NonNull
@Override
public Result doWork() {

        ApiService service = RetrofitInstance.getRetrofitInstance().create(ApiService.class);

        Call<ExpertListResponse> call = service.get_recommended_users();

        Log.wtf("URL Called", call.request().url() + "");

        call.enqueue(new Callback<ExpertListResponse>() {
            @Override
            public void onResponse(Call<ExpertListResponse> call, Response<ExpertListResponse> response) {
                //onFinishedListener.onFinished(requestTag, response.body() != null ? response.body().getExpertInfo() : null);
                Data outPut = new Data.Builder()
                        .putString(Constant.WORK_RESULT,Constant.WORK_SUCCESS)
                        .putString(Constant.WORK_RESPONSE, String.valueOf(response.code()))
                        .build();
                Log.e("WORKER", "onResponse: "+ response.body().getMsg() );
                setOutputData(outPut);
            }

            @Override
            public void onFailure(Call<ExpertListResponse> call, Throwable t) {
                //onFinishedListener.onFailure(requestTag,t);
                Data outPut = new Data.Builder()
                        .putString(Constant.WORK_RESULT,Constant.WORK_FAILURE)
                        .putString(Constant.WORK_RESPONSE, String.valueOf(t))
                        .build();
                setOutputData(outPut);
            }
        });

    return Result.SUCCESS;
}

}


请粘贴您的Worker类。 - Qasim
@Qasim,这是给你的。 - AndroMasPC
doWork()onResponse() 触发之前就返回了,因此输出还没有被设置。 - greenapps
首先感谢@Qasim。那么你在这里有什么建议,有没有解决方法? - AndroMasPC
2
在您的代码中,请使用call.execute(同步)而不是call.enqueue(异步),并根据您在call.execute方法中获得的响应处理Result.SUCCESS / Result.FAILURE。 - silwar
3个回答

10
问题在于你在异步的Retrofit请求中,在onResponse被触发之前就返回了Result.Success,因此没有数据被设置到worker状态中。
其中一个解决方法是在你的worker中发送一个同步的Retrofit请求,这样你的doWork()方法将会被阻塞,直到你获得网络响应。你需要将你的异步Retrofit请求改成同步,类似以下代码片段:
    public Result doWork() {
       try {
          ApiService service = RetrofitInstance.getRetrofitInstance()
                                   .create(ApiService.class);
          Call<ExpertListResponse> call = service.get_recommended_users();
          ExpertListResponse response = call.execute().body();
          Data outPut = new Data.Builder()
                    .putString(Constant.WORK_RESULT,Constant.WORK_SUCCESS)
                    .putString(Constant.WORK_RESPONSE, String.valueOf(response.code()))
                    .build();

          setOutputData(outPut);
          return Result.SUCCESS;
       } catch(Exception ex){
            Data outPut = new Data.Builder()
                    .putString(Constant.WORK_RESULT,Constant.WORK_FAILURE)
                    .putString(Constant.WORK_RESPONSE, String.valueOf(t))
                    .build();
            setOutputData(outPut);
            return Result.Failure;
       }
    }

我认为既然你的输出是Data类型,为什么不直接返回Result.success(output)和Result.failure(output)呢? - Theophilus Abiola Alamu

5
您也可以使用类似这样的方法:

您也可以使用类似这样的方法:

public class NetworkWorker extends Worker {

private static final long MAX_WAIT_TIME_SECONDS = 10L;

Data outPut;
CountDownLatch latch;

@NonNull
@Override
public Result doWork() {
    // Need to wait for the onResponse() call.
    latch = new CountDownLatch(1);

    ApiService service = RetrofitInstance.getRetrofitInstance().create(ApiService.class);
    Call<ExpertListResponse> call = service.get_recommended_users();
    call.enqueue(new Callback<ExpertListResponse>() {
        @Override
        public void onResponse(Call<ExpertListResponse> call, Response<ExpertListResponse> response) {
            Data outPut = new Data.Builder()
                    .putString(Constant.WORK_RESULT,Constant.WORK_SUCCESS)
                    .putString(Constant.WORK_RESPONSE, String.valueOf(response.code()))
                    .build();
            Log.e("WORKER", "onResponse: "+ response.body().getMsg() );
            latch.countdown();
            setOutputData(outPut);
        }

        @Override
        public void onFailure(Call<ExpertListResponse> call, Throwable t) {
            Data outPut = new Data.Builder()
                    .putString(Constant.WORK_RESULT,Constant.WORK_FAILURE)
                    .putString(Constant.WORK_RESPONSE, String.valueOf(t))
                    .build();
            latch.countdown();
            setOutputData(outPut);
        }
    });

latch.await(MAX_WAIT_TIME_SECONDS, TimeUnit.SECONDS);
return Result.SUCCESS;

这个想法是通过Retrofit本身公开同步API,或者使用CountDownLatch。请记住,当您执行此类操作时(因为Retrofit可能会使用不同的线程池),您可以在您的线程池中使用附加线程。


是的,更何况请求可能需要比我们设置的倒计时更长的时间,因此我将请求改为同步方式。 - AndroMasPC

0
==========================Activity==========================


OneTimeWorkRequest movieOneTimeRequest;
WorkManager workManager;

private void callWorkerApiInBackground() {
        Constraints constraints = new Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build();

        Data inputData = createInputData();

        movieOneTimeRequest = new OneTimeWorkRequest.Builder(MovieWorker.class)
                .setConstraints(constraints)
                .setInputData(inputData)
                .build();
        workManager.getWorkInfoByIdLiveData(movieOneTimeRequest.getId()).observe(this, workInfo -> {
            if (workInfo != null) {
                Log.e(TAG, "WorkInfo received: state: " + workInfo.getState());
                switch(workInfo.getState()){
                    case FAILED:
                        Log.e(TAG, "OBSERVING :: fail");
                        break;
                    case BLOCKED:
                        Log.e(TAG, "OBSERVING :: blocked");
                        break;
                    case RUNNING:
                        Log.e(TAG, "OBSERVING :: running");
                        break;
                    case ENQUEUED:
                        Log.e(TAG, "OBSERVING :: enqueued");
                        break;
                    case CANCELLED:
                        Log.e(TAG, "OBSERVING :: cancelled");
                        break;
                    case SUCCEEDED:
                        Log.e(TAG, "OBSERVING :: succeeded");
                        String workManagerOutput = workInfo.getOutputData().getString(MovieWorker.KEY_OUTPUT);
                        Log.e(TAG, " workManagerOutput: " + workManagerOutput);
                        break;
                }

            }
        });

        workManager.enqueue(movieOneTimeRequest);
    }

    private Data createInputData() {
        Data.Builder builder = new Data.Builder();
        String imageUrl = "www.image.come";
        if (!TextUtils.isEmpty(imageUrl)) {
            builder.putString(MovieWorker.KEY_IMAGE,imageUrl);
        }
        return builder.build();
    }

=================================== 电影工作者 =======================

    import android.content.Context;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

import com.aiplocationtest.models.MovieData;
import com.aiplocationtest.network.NetworkApiClient;

import java.util.List;

public class MovieWorker extends Worker {
    public static final String KEY_IMAGE = "image";
    public static final String KEY_OUTPUT = "output";
    String TAG = MovieWorker.class.getSimpleName();
    String imageUrl;

    public MovieWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
        imageUrl = workerParams.getInputData().getString(KEY_IMAGE);
        Log.e(TAG, " imageUrl : " + imageUrl);
    }

    @NonNull
    @Override
    public Result doWork() {
        try {
            List<MovieData> response = NetworkApiClient.getMovieList().execute().body();

            Data outputData = new Data.Builder()
                    .putString(KEY_OUTPUT, response.toString())
                    .build();
            return Result.success(outputData);
//            return  Result.success();
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, " callMovieListApi Exception : " + e.getMessage());
            return Result.failure();
        }

    }
}

====================== 网络 API 客户端 ====================

import com.aiplocationtest.models.MovieData;

import java.util.List;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.GET;

public class NetworkApiClient {


    public static Retrofit getRestAdapter() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
        return new Retrofit.Builder()
                .baseUrl(HttpConstant.BASE_URL)
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }

    public static Call<List<MovieData>> getMovieList() {
        return getRestAdapter().create(GetAPi.class).getMovieList();
    }

    public interface GetAPi {

        @GET(HttpConstant.MOVIE_LIST)
        Call<List<MovieData>> getMovieList();

    }


}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接