আমি কি ভলির সাথে একটি সুসংগত অনুরোধ করতে পারি?


133

কল্পনা করুন যে আমি ইতিমধ্যে একটি পরিষেবাতে আছি যার পটভূমি থ্রেড রয়েছে। আমি কি একই থ্রেডে ভোলি ব্যবহার করে একটি অনুরোধ করতে পারি, যাতে কলব্যাকগুলি সিঙ্ক্রোনিকভাবে ঘটে?

এর জন্য দুটি কারণ রয়েছে: - প্রথমত, আমার আর কোনও থ্রেডের প্রয়োজন নেই এবং এটি তৈরি করা এটি অপচয় হবে। - দ্বিতীয়ত, আমি যদি সার্ভিস ইন্টেন্টে আছি, কলব্যাকের আগে থ্রেডের সম্পাদন শেষ হবে এবং এর জন্য ভলির কাছ থেকে আমার কোনও প্রতিক্রিয়া থাকবে না। আমি জানি যে আমি নিজের নিজস্ব পরিষেবা তৈরি করতে পারি যার একটি চাল্লুপ আমি নিয়ন্ত্রণ করতে পারি তার কিছু থ্রেড রয়েছে তবে ভলিতে এই কার্যকারিতাটি থাকা বাঞ্ছনীয় হবে।

ধন্যবাদ!


5
@ ব্লুন্ডেলের প্রতিক্রিয়া পাশাপাশি উচ্চ উত্সাহিত (এবং খুব দরকারী) উত্তরটি অবশ্যই পড়তে ভুলবেন না।
Jedidja

উত্তর:


183

দেখে মনে হচ্ছে ভলির RequestFutureক্লাস দিয়ে এটি সম্ভব । উদাহরণস্বরূপ, একটি সিঙ্ক্রোনাস জেএসএন এইচটিটিপি জিইটি অনুরোধ তৈরি করতে, আপনি নিম্নলিখিতটি করতে পারেন:

RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(URL, new JSONObject(), future, future);
requestQueue.add(request);

try {
  JSONObject response = future.get(); // this will block
} catch (InterruptedException e) {
  // exception handling
} catch (ExecutionException e) {
  // exception handling
}

5
টুইটারে এটি JsonObjectRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener, ErrorListener errorlistener)কনস্ট্রাক্টর ব্যবহার করে । RequestFuture<JSONObject>উভয় Listener<JSONObject>এবং ErrorListenerইন্টারফেস প্রয়োগ করে , তাই এটি শেষ দুটি পরামিতি হিসাবে ব্যবহার করা যেতে পারে।
ম্যাট

21
এটি চিরতরে অবরুদ্ধ!
মোহাম্মদ সুহি শেখ কুরূশ

9
ইঙ্গিত: আপনি যদি ভবিষ্যতে কল করেন তবে এটি চিরতরে অবরুদ্ধ হয়ে থাকবে get বাজেট () আগে আপনি অনুরোধের সারিতে অনুরোধটি যুক্ত করুন।
দাতায়াহ

3
এটি চিরকালের জন্য অবরুদ্ধ থাকবে কারণ আপনার কোনও সংযোগ ত্রুটি হতে পারে ব্লুন্ডেল উত্তর
মিনা গ্যাব্রিয়েল

4
একজনের উচিত বলা উচিত যে আপনি এটি মূল থ্রেডে করবেন না। এটা আমার কাছে পরিষ্কার ছিল না। এর ফলে যদি মূল থ্রেডটি অবরুদ্ধ করা থাকে future.get()তবে অ্যাপ্লিকেশনটি স্টল হয়ে যাবে বা যদি সেট করা থাকে তা নিশ্চিত হওয়ার জন্য টাইমআউট চালু হবে।
r00 স্ট্যান্ডে

125

দ্রষ্টব্য @ ম্যাথিউজের উত্তরটি সঠিক তবে তবে আপনি যদি অন্য থ্রেডে থাকেন এবং আপনার যখন ইন্টারনেট না থাকে আপনি একটি ভলির কল করেন, তবে আপনার ত্রুটি কলব্যাকটি মূল থ্রেডে কল করা হবে, তবে আপনি যে থ্রেডটিতে রয়েছেন তা বন্ধ করে দেওয়া হবে। (অতএব যদি সেই থ্রেডটি কোনও ইনটেন্ট সার্ভিস হয় তবে আপনি এটিতে আর কোনও বার্তা প্রেরণ করতে পারবেন না এবং আপনার পরিষেবাটি মূলত মারা যাবে)।

get()একটি সময়সীমা আছে এর সংস্করণটি ব্যবহার করুন future.get(30, TimeUnit.SECONDS)এবং আপনার থ্রেড থেকে প্রস্থান করতে ত্রুটিটি ধরুন।

@ ম্যাথিউজ উত্তর মেলে:

        try {
            return future.get(30, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // exception handling
        } catch (ExecutionException e) {
            // exception handling
        } catch (TimeoutException e) {
            // exception handling
        }

নীচে আমি এটি একটি পদ্ধতিতে জড়িয়ে রেখেছি এবং একটি আলাদা অনুরোধ ব্যবহার করেছি:

   /**
     * Runs a blocking Volley request
     *
     * @param method        get/put/post etc
     * @param url           endpoint
     * @param errorListener handles errors
     * @return the input stream result or exception: NOTE returns null once the onErrorResponse listener has been called
     */
    public InputStream runInputStreamRequest(int method, String url, Response.ErrorListener errorListener) {
        RequestFuture<InputStream> future = RequestFuture.newFuture();
        InputStreamRequest request = new InputStreamRequest(method, url, future, errorListener);
        getQueue().add(request);
        try {
            return future.get(REQUEST_TIMEOUT, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Log.e("Retrieve cards api call interrupted.", e);
            errorListener.onErrorResponse(new VolleyError(e));
        } catch (ExecutionException e) {
            Log.e("Retrieve cards api call failed.", e);
            errorListener.onErrorResponse(new VolleyError(e));
        } catch (TimeoutException e) {
            Log.e("Retrieve cards api call timed out.", e);
            errorListener.onErrorResponse(new VolleyError(e));
        }
        return null;
    }

এটি একটি মোটামুটি গুরুত্বপূর্ণ বিষয়! নিশ্চিত নয় কেন এই উত্তরটি আরও বেশি পরিমাণে অর্জন করেছে।
Jedidja

1
আরও লক্ষণীয় যে আপনি যদি অনুরোধটি পাস করেছেন একই শ্রোতার কাছে যদি আপনি এক্সিকিউশনএক্সেপশনটি পাস করেন তবে আপনি ব্যতিক্রমটি দু'বার প্রক্রিয়াকরণ করবেন। এই ব্যতিক্রম ঘটে যখন অনুরোধ চলাকালীন কোনও ব্যতিক্রম ঘটে যা ভলি আপনার জন্য ত্রুটিযুক্ত তালিকায় প্রবেশ করবে।
সিম্টসনি

@ ব্লুন্ডেল আমি আপনার উত্তরটি বুঝতে পারি না। শ্রোতার যদি ইউআই থ্রেডে মৃত্যুদন্ড কার্যকর করা হয় তবে আপনি অপেক্ষা করতে পটভূমি থ্রেড এবং UI থ্রেড যা কল করুন notifAll () তাই ঠিক আছে। ডেলিভারি ঘটতে পারে যদি একই থ্রেডে ডেলিভারি করা হয়ে থাকে তবে আপনি ভবিষ্যতের পান () দিয়ে অবরুদ্ধ হন। সুতরাং আপনার প্রতিক্রিয়া কোনও বোধগম্য বলে মনে হচ্ছে।
গ্রাইওয়ালফ 82

1
@ গ্রাইওল্ফ ৮২২ IntentServiceএকক থ্রেডের থ্রেড পুল নির্বাহক, অতএব ইনটেন্স সার্ভিস অবরুদ্ধ হয়ে যাবে এটি একটি লুপে বসে
ব্লুন্ডেল ১

@ ব্লুন্ডেল আমি বুঝতে পারি না। এটি বিজ্ঞপ্তি না দেওয়া পর্যন্ত এটি বসে থাকে এবং এটি ইউআই থ্রেড থেকে কল করা হবে। আপনার দুটি পৃথক থ্রেড না হওয়া পর্যন্ত আমি অচলাবস্থা দেখতে পাচ্ছি না
গ্রেওয়াল্প ৮২

9

সম্ভবত ফিউচারগুলি ব্যবহার করার পরামর্শ দেওয়া হচ্ছে, তবে যদি আপনি যে কোনও কারণে না চান তবে নিজের সিঙ্ক্রোনাইজড ব্লকিংয়ের রান্না না করে আপনার ব্যবহার করা উচিত java.util.concurrent.CountDownLatch। সুতরাং যে এই মত কাজ করবে ..

//I'm running this in an instrumentation test, in real life you'd ofc obtain the context differently...
final Context context = InstrumentationRegistry.getTargetContext();
final RequestQueue queue = Volley.newRequestQueue(context);
final CountDownLatch countDownLatch = new CountDownLatch(1);
final Object[] responseHolder = new Object[1];

final StringRequest stringRequest = new StringRequest(Request.Method.GET, "http://google.com", new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        responseHolder[0] = response;
        countDownLatch.countDown();
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        responseHolder[0] = error;
        countDownLatch.countDown();
    }
});
queue.add(stringRequest);
try {
    countDownLatch.await();
} catch (InterruptedException e) {
    throw new RuntimeException(e);
}
if (responseHolder[0] instanceof VolleyError) {
    final VolleyError volleyError = (VolleyError) responseHolder[0];
    //TODO: Handle error...
} else {
    final String response = (String) responseHolder[0];
    //TODO: Handle response...
}

যেহেতু লোকেরা আসলে এটি করার চেষ্টা করেছিল এবং কিছুটা সমস্যার মধ্যে পড়েছিল আমি সিদ্ধান্ত নিয়েছি আমি বাস্তবে এটির ব্যবহারের একটি "বাস্তব জীবন" কাজের নমুনা সরবরাহ করব। এটি এখানে https://github.com/timolehto/SynchronousVolleySample

এখন যদিও সমাধানটি কাজ করে তবে এর কিছু সীমাবদ্ধতা রয়েছে। সর্বাধিক গুরুত্বপূর্ণ, আপনি এটিকে মূল ইউআই থ্রেডে কল করতে পারবেন না। ভোলি ব্যাকগ্রাউন্ডে অনুরোধগুলি কার্যকর করে, কিন্তু ডিফল্টরূপে ভল্লি Looperপ্রতিক্রিয়াগুলি প্রেরণের জন্য অ্যাপ্লিকেশনটির মূলটি ব্যবহার করে । মূল ইউআই থ্রেড প্রতিক্রিয়ার জন্য অপেক্ষা করায় এটি অচলাবস্থার কারণ, তবে ডেলিভারি প্রক্রিয়াজাতকরণের আগে এটি শেষ হওয়ার Looperঅপেক্ষায় রয়েছে onCreate। আপনি যদি সত্যিই এটি করতে চান তবে স্থির সহায়ক পদ্ধতির পরিবর্তে, নিজের ইউএস থ্রেড থেকে পৃথক থ্রেডের সাথে আবদ্ধ একটি ব্যবহার করে RequestQueueএটি আপনার নিজের দ্বারা ExecutorDeliveryআবদ্ধ আপনার নিজের পাসিং ইনস্ট্যান্ট করুন ।HandlerLooper


এই সমাধানটি আমার থ্রেডকে চিরতরে অবরুদ্ধ করে রাখে, কাউন্টডাউনচ্যাচ পরিবর্তে থ্রেড পরিবর্তন করে ঘুমান এবং সমস্যার সমাধান হয়
স্যানারসায়ান

আপনি যদি এই পদ্ধতিতে ব্যর্থ হওয়া কোনও কোডের একটি সম্পূর্ণ নমুনা সরবরাহ করতে পারেন তবে আমরা সমস্যাটি কী তা বুঝতে পারি। কাউন্টডাউন ল্যাচগুলির সাথে একত্রে ঘুমানো কীভাবে বোঝায় তা আমি দেখতে পাচ্ছি না।
টিমো

ঠিক আছে @ ভিনোজভেথা পরিস্থিতি স্পষ্ট করার জন্য আমি উত্তরটি কিছুটা আপডেট করেছি এবং একটি গিটহাব রেপো সরবরাহ করেছি যা আপনি সহজেই ক্লোন করতে পারেন এবং কোডটিকে কার্যত চেষ্টা করতে পারেন। আপনার যদি আরও সমস্যা থাকে তবে অনুগ্রহ করে আপনার সমস্যাটিকে রেফারেন্স হিসাবে প্রদর্শন করে এমন নমুনা রেপোর একটি কাঁটা সরবরাহ করুন।
টিমো

এটি এখন পর্যন্ত সিঙ্ক্রোনাস অনুরোধের জন্য দুর্দান্ত সমাধান।
বিক্রম

2

উভয় @Blundells এবং @Mathews উত্তর করার জন্য একটি পরিপূরক পর্যবেক্ষণ হিসেবে আমি নিশ্চিত নই কোন কল কিছু বিতরণ করা হয় কিন্তু ভলি দ্বারা মূল থ্রেড।

উৎস

কটাক্ষপাত রয়ে RequestQueueবাস্তবায়ন মনে হয় RequestQueueএকটি ব্যবহার করছে NetworkDispatcherঅনুরোধ চালানো এবং ResponseDeliveryফলাফলের বিলি ( ResponseDeliveryমধ্যে ইনজেকশনের হয় NetworkDispatcher)। ResponseDeliveryএকটি সঙ্গে তৈরি ঘুরে হয় Handlerমূল থ্রেড থেকে ডিম (কোথাও প্রায় মধ্যে 112 লাইন RequestQueueবাস্তবায়ন)।

NetworkDispatcherবাস্তবায়নে 135 লাইন সম্পর্কে কোথাও কোথাও মনে হচ্ছে সফল ফলাফলগুলি ResponseDeliveryকোনও ত্রুটির মতোই সরবরাহ করা হবে। আবার; একটি ResponseDeliveryএকটি উপর ভিত্তি করে Handlerমূল থ্রেড থেকে ডিম।

যুক্তিসহ ব্যাখ্যা

ব্যবহারের ক্ষেত্রে যেখানে IntentServiceভলির কাছ থেকে আমাদের কোনও প্রতিক্রিয়া না আসা অবধি পরিষেবাটির থ্রেডটি ব্লক করা উচিত এটি ধরে নেওয়া ন্যায্য পক্ষ থেকে অনুরোধ করা উচিত (ফলাফলটি পরিচালনা করার জন্য কোনও জীবন্ত রানটাইম সুযোগের নিশ্চয়তা দিতে)।

প্রস্তাবিত সমাধান

একটি পদ্ধতি RequestQueueহ'ল একটি তৈরির ডিফল্ট উপায়ে ওভাররাইড করা হবে , যেখানে পরিবর্তে বিকল্প কন্সট্রাক্টর ব্যবহৃত হবে, ইনজেকশনের মাধ্যমে ResponseDeliveryযা বর্তমান থ্রেডের পরিবর্তে বর্তমান থ্রেড থেকে উদ্ভূত হয় । আমি অবশ্য এর নিদর্শনগুলি তদন্ত করি নি।


1
একটি কাস্টম রেসপন্সডেলিভারী বাস্তবায়ন কার্যকর করা ক্লাসে এবং ক্লাসে finish()পদ্ধতিটি প্যাকেজটি ব্যক্তিগত বলে জটিল , একটি প্রতিবিম্ব হ্যাক ব্যবহার করা ছাড়াও আমি নিশ্চিত নই যে এর আশপাশে কোনও উপায় আছে। মূল (ইউআই) থ্রেডে চলমান কিছু রোধ করতে আমি যা করেছিলাম তা হ'ল বিকল্প লুপার থ্রেড স্থাপন (ব্যবহার করা ) এবং সেই লুপারটির জন্য হ্যান্ডলারের সাহায্যে কনস্ট্রাক্টরের কাছে একটি দৃষ্টান্ত পাঠানো । আপনার কাছে অন্য লুপের ওভারহেড রয়েছে তবে মূল থ্রেড থেকে দূরে থাকুনRequestRequestQueueLooper.prepareLooper(); Looper.loop()ExecutorDeliveryRequestQueue
স্টিফেন জেমস হ্যান্ড

1

আমি এই প্রভাবটি অর্জনের জন্য একটি লক ব্যবহার করি এখন ভাবছি আমি যদি এটির মতো করে আমার মতামত মন্তব্য করতে চায় তবে?

// as a field of the class where i wan't to do the synchronous `volley` call   
Object mLock = new Object();


// need to have the error and success listeners notifyin
final boolean[] finished = {false};
            Response.Listener<ArrayList<Integer>> responseListener = new Response.Listener<ArrayList<Integer>>() {
                @Override
                public void onResponse(ArrayList<Integer> response) {
                    synchronized (mLock) {
                        System.out.println();
                        finished[0] = true;
                        mLock.notify();

                    }


                }
            };

            Response.ErrorListener errorListener = new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    synchronized (mLock) {
                        System.out.println();
                        finished[0] = true;
                        System.out.println();
                        mLock.notify();
                    }
                }
            };

// after adding the Request to the volley queue
synchronized (mLock) {
            try {
                while(!finished[0]) {
                    mLock.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

আমি মনে করি আপনি "ফিউচার" ব্যবহার করার সময় ভল্লি ইতিমধ্যে কী সরবরাহ করে তা আপনি মূলত প্রয়োগ করছেন।
spaaarky21

1
আমি catch (InterruptedException e)ভিতরে লুপটি ভিতরে রাখার পরামর্শ দেব । অন্যথায় থ্রেড কোনও কারণে বাধা থাকলে অপেক্ষা করতে ব্যর্থ হবে
জায়েফকায়

@ জেজেফকেয়ে আমি ইতিমধ্যে ব্যতিক্রমটি ধরছি যদি কোনও ** বাধাপ্রাপ্তি ** ঘটে তবে লুপটি যখন ল্যাপটি পরিচালনা করে।
জোর করে বলুন

1

আমি ম্যাথিউর গৃহীত উত্তরে কিছু যুক্ত করতে চাই। যদিও RequestFutureথ্রেড আপনি এটি তৈরি করা থেকে সমলয় কল করার জন্য মনে হতে পারে, এটা না। পরিবর্তে, কলটি একটি পটভূমির থ্রেডে কার্যকর করা হয়।

পাঠাগারটি দিয়ে যাবার পরে আমি যা বুঝি তার থেকে অনুরোধগুলি RequestQueueতার start()পদ্ধতিতে প্রেরণ করা হয়েছে :

    public void start() {
        ....
        mCacheDispatcher = new CacheDispatcher(...);
        mCacheDispatcher.start();
        ....
           NetworkDispatcher networkDispatcher = new NetworkDispatcher(...);
           networkDispatcher.start();
        ....
    }

এখন উভয় CacheDispatcherএবং NetworkDispatcherশ্রেণি থ্রেড প্রসারিত করে। সুতরাং কার্যকরভাবে একটি নতুন কর্মী থ্রেড অনুরোধের সারিটি শনাক্ত করার জন্য তৈরি করা হয়েছে এবং অভ্যন্তরীণভাবে কার্যকর হওয়া ত্রুটি শ্রোতাদের কাছে প্রতিক্রিয়া ফিরিয়ে দেওয়া হয়েছে RequestFuture

যদিও আপনার দ্বিতীয় উদ্দেশ্যটি অর্জন করা হয়েছে তবে আপনি প্রথম উদ্দেশ্যটি হ'ল যেহেতু কোনও নতুন থ্রেড সর্বদা প্রসারিত হয় না আপনি কোন থ্রেড থেকে কার্যকর করেন তা বিবেচ্য নয় RequestFuture

সংক্ষেপে, সত্য সিঙ্ক্রোনাস অনুরোধ ডিফল্ট ভলির লাইব্রেরির সাথে সম্ভব নয়। আমি ভুল হলে আমাকে সংশোধন করুন।


0

আপনি ভলির সাথে সিঙ্ক অনুরোধ করতে পারেন তবে আপনাকে অবশ্যই বিভিন্ন থ্রেডে পদ্ধতিটি কল করতে হবে বা আপনার চলমান অ্যাপ্লিকেশনটি ব্লক হয়ে যাবে, এটি এমন হওয়া উচিত:

public String syncCall(){

    String URL = "http://192.168.1.35:8092/rest";
    String response = new String();



    RequestQueue requestQueue = Volley.newRequestQueue(this.getContext());

    RequestFuture<JSONObject> future = RequestFuture.newFuture();
    JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, URL, new JSONObject(), future, future);
    requestQueue.add(request);

    try {
        response = future.get().toString();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    } catch (JSONException e) {
        e.printStackTrace();
    }

    return response;


}

এর পরে আপনি পদ্ধতিটি থ্রেডে কল করতে পারেন:

 Thread thread = new Thread(new Runnable() {
                                    @Override
                                    public void run() {

                                        String response = syncCall();

                                    }
                                });
                                thread.start();
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.