为什么使用Retrofit将大量数据从服务器同步到本地数据库时,Android圆形进度对话框会出现冻结现象?

10

1. 我正在从服务器获取大量数据并在Android登录时插入本地数据库。

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

我的问题是:在使用Retrofit从服务器同步大量数据时,圆形进度对话框会冻结

请帮忙解决这个问题。

编辑:1

在异步任务中调用Retrofit方法,但圆形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();
                                }


                            }

                        });

            }

3
这是否只是因为代码在UI线程上执行?将其包装在异步任务中应该会立即告诉您是否存在问题。据我所知,Retrofit始终将解析放在后台线程中,但您似乎也在主线程中执行它... - Fred
但我已经在异步任务中调用了这个方法,但是当从服务器批量获取数据到本地数据库时仍然卡住了:( - Kumar
你是如何调用asyncTask的execute方法的?你能展示一下代码的部分吗? - Opiatefuchs
@Opiatefuchs,请检查我的asyncTask执行方法。 - Kumar
4个回答

3

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

您必须在能够访问UI线程的方法中执行,例如onProgressUpdate()onPostExecute()


在 doInBackground 中调用 Dismiss 是导致此问题的主要原因吗? - Kumar
原因是 'pDialog.dismiss();' 和 'Toast.makeText'。你应该将 'doinbackground' 中的处理结果传递给 'onPostExecute()'。请查看此链接:https://dev59.com/0Gkw5IYBdhLWcg3wwNN5#9671602。 - Hadi

3
在这种情况下,没有好的理由使用同步的Retrofit调用,应该使用Retrofit提供的async call。它非常简单,您可以使用回调管理进度条状态。
我认为你根本不应该使用AsyncTask。如果您想探索这个问题,请查看this, thisthis
示例代码转换从Retrofit v1同步调用与AsyncTask到Retrofit 2异步调用,在api类中进行管理,并使用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(""));
            }
        });
    }
}

您需要一个事件类来封装您希望从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并使用Otto。我猜测了这个实现,所以它可能不能直接使用,但应该是一个很好的草图,可以让您感受到它的用法。

在Retrofit 1.9中,我们不能添加进度条吗?我的代码运行良好,只有进度条会冻结!! - Kumar
使用ProgressBar与Retrofit无关,因为ProgressBar是UI(即表示层),而Retrofit是数据访问层。如果您坚持使用AsyncTask,正如其他人所说,您无法直接在doInBackground方法中访问pDialog,您必须显式地在UI线程上运行它,例如通过使用runOnUiThread方法,如此处所述:https://dev59.com/rnDXa4cB1Zd3GeqP_2Z6#15757788 - r3flss ExlUtr

1
有两个可能导致冻结问题的原因。
第一个:您的请求操作正在应用程序的UI线程上运行,这是不推荐的。
第二个:您正在尝试在某些旧设备上测试您的应用程序,这些设备具有旧的处理器。
我建议您使用Retrofit自己的异步请求,其中描述了此处。告诉您不应该在主线程上调用dialog.dismiss()的人是正确的。它也可能会使您的应用程序冻结。这里是一个很好的例子如何使用runOnUiThread()方法避免它。

我只是在Doinbackground中运行请求方法,对吧?那它怎么在UI线程上运行呢? - Kumar
dialog.dismiss() 应该在 runOnUiThread() 方法内部调用。它是 Activity 类的一个方法。 - RexSplode

1
这是您的Rest接口,您在此定义要进行的调用。
interface RestInterface {

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

这是实现类,你可以从中调用。
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);

    }
}

这个类应该符合你收到的响应的格式。
public class ModelResponse{

    String RouteId;

    public String getRouteId(){
        return RouteId; 
    }
}

现在从您的活动(或首选类)中创建一个RestService对象,并调用方法getRoute(),将您的查询字符串和匿名回调对象(retrofit 2 Callback接口)作为参数传递。您将在匿名回调对象中以Response形式获得结果。 // 后面添加 您的实现类

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();
                }
            });
    }
}

我的进度条在哪里? - Kumar
我已经在底部添加了一个实现类,请检查它。 - Debanjan

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