Android Work Manager Один раз Запрос возвращает пустой результат от работника
Я использую Work Manger для сетевого вызова и получения некоторых данных, для этого я использовал OneTimeRequest. Вызов прекрасно отвечает в рабочем классе, но он возвращает пустой результат у владельца жизненного цикла, который наблюдает за работой.
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;
}
}
2 ответа
Проблема в том, что вы возвращаетесь Result.Success
до onResponse
срабатывает, когда вы выполняете асинхронный запрос на модификацию, следовательно, данные в рабочем состоянии не задаются.
Одним из многих обходных путей является то, что вы отправляете синхронный запрос на модернизацию внутри вашего работника, таким образом, ваш doWork()
метод будет заблокирован, пока вы не получите ответ по сети. Вы должны изменить свой асинхронный запрос на модификацию на синхронный, что-то вроде следующего фрагмента кода:
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;
}
}
Вы также можете использовать что-то вроде этого:
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;
}
Идея состоит в том, чтобы предоставить синхронный API через саму модернизацию или использовать CountDownLatch
, Имейте в виду, что вы можете использовать дополнительный поток в вашем пуле потоков, когда вы делаете что-то подобное (так как Retrofit потенциально может использовать другой пул потоков).
==========================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();
}
=================================== MovieWorker =======================
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();
}
}
}
====================== NetworkApiClient ====================
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();
}
}