为什么 Android 使用 Retrofit 从服务器向本地数据库同步大量数据时,Circle Progress 对话框会冻结

Why Android Circle Progress Dialog Getting Freezes when Bunch of Data Syncing from Server to Local DB Using Retrofit

1.Iam 从服务器获取 批量数据 并在登录时插入本地数据库 android。

2.For 我使用的同步目的 Retrofit 库,同步和插入工作正常。

我的问题:使用 Retrofit 从服务器同步大量数据时循环进度对话框冻结

帮我解决这个问题。

编辑:1

在异步任务中调用了 Retrofit 方法,仍然 Circle ProgressDialog 正在冻结

//调用异步任务

Asynctask_Getdata task=new Asynctask_Getdata(TokenType_StringValue,Access_Token_StringValue, ID_StringValue);
   task.execute();

//异步任务方法

public class Asynctask_Getdata extends AsyncTask<String,Void,Void>
    {

        String TokenType_StringValueitem;
        String Access_Token_StringValueitem;
        String ID_StringValueitem;

        public Asynctask_Getdata(String tokenType_StringValueitem, String access_Token_StringValueitem, String ID_StringValueitem) {
            TokenType_StringValueitem = tokenType_StringValueitem;
            Access_Token_StringValueitem = access_Token_StringValueitem;
           ID_StringValueitem = ID_StringValueitem;
        }

        @Override
        protected void onPreExecute()
        {
            super.onPreExecute();

            if (!pDialog.isShowing())
            {
                pDialog.setIndeterminate(true);
                pDialog.setCanceledOnTouchOutside(false);
                pDialog.setMessage("Please Wait Getting Data...");
                pDialog.show();
            }
        }

        @Override
        protected void onPostExecute(Void aVoid)
        {

            super.onPostExecute(aVoid);
        }

        @Override
        protected Void doInBackground(String... params)
        {
            getLoginDataCall(TokenType_StringValueitem, Access_Token_StringValueitem, ID_StringValueitem );

            return null;
        }
    }


    private void getLoginDataCall(String TokenType_String, String Access_Token_StringValue, String RouteID_String) {

                String Tokenadd = TokenType_String + " Access_Token_StringValue;
                sisClient.getApi().getAllData(Tokenadd, RouteID_String,
                        new Callback<CommonResponse>() {
                            @Override
                            public void success(CommonResponse commonResponse, Response response) {

                                Timber.d("sendOtpAPICall%s", commonResponse.getStatusCode());

                                switch (commonResponse.getStatusCode()) {
                                    case 200:


                                        try {


                                            JSONArray jsonarray = null;
                                            try {
                                                jsonarray = new JSONArray(commonResponse.getRouteMaster());

                                                if (!commonResponse.getRouteMaster().equals("[]")) 
                                                 {
                                                    for (int i = 0; i < jsonarray.length(); i++) 
                                                      {
                                                        JSONObject jsonobject = jsonarray.getJSONObject(i);


                                                        RouteId_StringValue = jsonobject.getString("RouteId");



                                                        Asynxfor_Route_Master_insert(RouteId_StringValue);


                                                    }
                                                } else {
                                                    System.out.println("ROute Master Is NULL ::" + commonResponse.getStatusMessage() + "\n");
                                                }
                                            } catch (Exception e) {
                                                e.printStackTrace();
                                                ;
                                            }

                                      break;

                                    case 404:

                                        pDialog.dismiss();
                                        Toast.makeText(LoginPage.this, R.string.wrong_Username_or_password, Toast.LENGTH_LONG).show();

                                        break;
                                    case 500:
                                        pDialog.dismiss();
                                        Toast.makeText(LoginPage.this, R.string.something_wrong, Toast.LENGTH_LONG).show();
                                        break;

                                }
                            }

                            @Override
                            public void failure(RetrofitError error) {

                                try {
                                    if (error != null) {
                                        pDialog.dismiss();
                                        Timber.i("sendOtpAPICall error %s", error.getResponse().getStatus());
                                        String json = new String(((TypedByteArray) error.getResponse().getBody()).getBytes());
                                        Timber.i("failure  error %s", json.toString());

                                        JSONObject json1 = new JSONObject(json.toString());
                                        String json1string = json1.getString("StatusMessage");


                                        switch (error.getResponse().getStatus()) {
                                            case 404:
                                                Toast.makeText(getApplicationContext(), R.string.wrong_Username_or_password, Toast.LENGTH_LONG).show();
                                                break;
                                            case 500:
                                                Toast.makeText(LoginPage.this, R.string.something_wrong, Toast.LENGTH_LONG).show();
                                                break;
                                            default:
                                                Toast.makeText(LoginPage.this, json1string, Toast.LENGTH_LONG).show();
                                                break;
                                        }


                                    } else {
                                        Timber.i("failure  error %s", "Recieving error null rom server");
                                    }


                                } catch (Exception e) {
                                    e.printStackTrace();
                                    Toast.makeText(LoginPage.this, R.string.something_wrong, Toast.LENGTH_LONG).show();
                                }


                            }

                        });

            }

您不能在 doInBackground 中使用 pDialog.dismiss();Toast.makeText

您必须在一个方法中执行此操作,该方法可以访问 UI 线程,例如 onProgressUpdate()onPostExecute()

在这种情况下没有充分的理由进行同步 Retrofit 调用,您应该使用 Retrofit 开箱即用的 async call。这非常简单,您可以使用回调来管理 ProgressBar 状态。

IMO 你根本不应该使用 AsyncTask。如果您想探索此内容,请查看 this, this and this.

示例代码从使用 AsyncTask 的 Retrofit v1 同步调用转换为 Retrofit 2 异步调用,在 api class 中管理并使用 Otto 事件总线进行消息传递:

public class ApiService {

    private static ApiService INSTANCE;

    // thwart instantiation by protecting
    protected ApiService() {
        // nothing
    }

    public static ApiService getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new ApiService();
        }
        return INSTANCE;
    }

    // -----------------------------------------------------------------------------------

    private static final String BASE_URL = "http://api.service.com/";

    private interface ApiServiceInterface {
        @GET("your/endpoint")
        Call<CommonResponse> postLogin(@Query("token_add") String tokenAdd, @Query("route_id") String RouteId);
    }

    private ApiServiceInterface apiServiceInterface = null;

    private ApiServiceInterface getInterface() {
        if (apiServiceInterface == null) {
            HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
            interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            OkHttpClient client = new OkHttpClient.Builder()
                    .addInterceptor(interceptor)
                    .build();

            Retrofit retrofit = new Retrofit.Builder()
                    .client(client)
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

            apiServiceInterface = retrofit.create(ApiServiceInterface.class);
        }
        return apiServiceInterface;
    }

    public void doGetData(Dialog pDialog, String TokenType_String, String Access_Token_StringValue, String RouteID_String) {
        if (!pDialog.isShowing())
        {
            pDialog.setIndeterminate(true);
            pDialog.setCanceledOnTouchOutside(false);
            pDialog.setMessage("Please Wait Getting Data...");
            pDialog.show();
        }
        String Tokenadd = TokenType_String + Access_Token_StringValue;
        getInterface().postLogin(Tokenadd, RouteID_String)
                .enqueue(new Callback<SocketCtrlResponse>() {
            @Override
            public void onResponse(Call<CommonResponse> call, Response<CommonResponse> response) {
                Timber.d("sendOtpAPICall%s", commonResponse.getStatusCode());
                switch (commonResponse.getStatusCode()) {
                    case 200:
                        JSONArray jsonarray = null;
                        try {
                            jsonarray = new JSONArray(commonResponse.getRouteMaster());

                            if (!commonResponse.getRouteMaster().equals("[]")) {
                                for (int i = 0; i < jsonarray.length(); i++) {
                                    JSONObject jsonobject = jsonarray.getJSONObject(i);
                                    RouteId_StringValue = jsonobject.getString("RouteId");
                                    //Asynxfor_Route_Master_insert(RouteId_StringValue);
                                    EventBus.getDefault().post(new GetDataResponseEvent(RouteId_StringValue));
                                }
                            } else {
                                System.out.println("ROute Master Is NULL ::" + commonResponse.getStatusMessage() + "\n");
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                      break;

                    case 404:
                        pDialog.dismiss();
                        Toast.makeText(LoginPage.this, R.string.wrong_Username_or_password, Toast.LENGTH_LONG).show();
                        EventBus.getDefault().post(new GetDataResponseEvent(""));
                        break;

                    case 500:
                        pDialog.dismiss();
                        Toast.makeText(LoginPage.this, R.string.something_wrong, Toast.LENGTH_LONG).show();
                        EventBus.getDefault().post(new GetDataResponseEvent(""));
                        break;
                }
            }

            @Override
            public void onFailure(Call<CommonResponse> call, Throwable t) {
                t.printStackTrace();
                pDialog.dismiss();
                Toast.makeText(LoginPage.this, R.string.something_wrong, Toast.LENGTH_LONG).show();
                EventBus.getDefault().post(new GetDataResponseEvent(""));
            }
        });
    }
}

您需要一个事件 class 来包装您希望从 API 返回的数据,例如:

public class GetDataResponseEvent {
    private final String data;

    public GetDataResponseEvent(String data) {
        this.data = data;
    }

    public String getData() {
        return data;
    }
}

并作为从 Activity 调用服务的示例:

public class YourActivity extends Activity {
    Dialog pDialog;

    // ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // init pDialog, content view, etc.

        EventBus.getDefault().register(this);

        // ...
    }

    // ...

    public void getDataFromApi() {
        ApiService.getInstance().doGetData(pDialog, TokenType_StringValue,Access_Token_StringValue, ID_StringValue);
    }

    // ...

    public void onEvent(GetDataResponseEvent event) {
        String data = event.getData();
        Asynxfor_Route_Master_insert(data);
    }

    // ...
}

再次注意,您必须升级到 Retrofit 2 and use Otto。我猜到了这个实现,所以它可能无法按原样工作,但应该是一个很好的草图来感受它。

这是您的 RestInterface,您可以在其中定义要进行的调用

interface RestInterface {

    String BASE_URL = "https://you_have_to_specify.com/if_anything/";
    @GET("your_api")
    Call<ModelResponse> getRouteId(@Query String valueItem);
}

这是实现 class,您可以从中调用

public class RestService {

    private RestInterface restInterface;
    private OkHttpClient okHttpClient;
    public RestService() {

        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();

        if (BuildConfig.DEBUG) {
            HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
            httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            clientBuilder.addInterceptor(httpLoggingInterceptor);
        }
        okHttpClient = clientBuilder.build();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(RestInterface.BASE_URL)
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        restInterface = retrofit.create(RestInterface.class);

    }
    //this is the exposed method
    public void getRoute(String valueId, Callback<ModelResponse> callback){
            Call<ModelResponse> call = restInterface.getRouteId(valueId);
            call.enque(callback);

    }
}

此 class 应采用您收到的回复的格式

public class ModelResponse{

    String RouteId;

    public String getRouteId(){
        return RouteId; 
    }
}

现在从您的 activity(或您的首选 class)创建一个 RestService 对象,并调用方法 getRoute(),传递您的查询字符串和一个匿名回调对象(改造 2回调接口)作为参数。您将在匿名回调对象中获得结果作为 Response。 // 稍后添加 您的实施 class

public class FromWhereYourApiIsCalled{
    RestService restService = new RestService();
    public void callRouteIdMethod(String value){
      progressDialog.show();// define progress dialog before
      restService.getRoute(value, new Callback<ModelResponse>() {
                @Override
                public void onResponse(Call<ModelResponse> call, Response<ModelResponse> response) {
                    progressDialog.dismiss();
                    Log.v("route Id", response.body().getRouteId());
                }

                @Override
                public void onFailure(Call<ModelResponse> call, Throwable t) {
                    progressDialog.dismiss();
                }
            });
    }
}

冻结问题可能有两种原因。

首先:你的请求操作是运行在应用的UI线程上,不推荐。

其次:您正在尝试在具有旧处理器的一些旧设备上测试您的应用程序。

我建议您使用 Retrofit 自己的异步请求,其中描述了 here.The guy who told you that you should not call dialog.dismiss() on the main thread is right. It can freeze your app as well. Here is a good example 如何使用方法 runOnUiThread()

来避免它