Thread Synchronization with IntentService

心不动则不痛 提交于 2019-12-11 02:38:29

问题


I'm trying to create an app that makes HTTP requests through an intentservice. I need the app to wait for the service to finish its run (aka, have the request be returned with some data) before it continues its operations, as its operations involve manipulation of the data I hope to receive from the HTTP requests. I've tried numerous means of doing so - Semaphore, CountDownLatch, but it seems that for all of them, I need some method of passing in the waiting/counting object into the intentservice so that it can tell the main thread where that object is waiting that it is done processing. How do I go about doing that? Basically, I want a synchronous, blocking call to an http server to work conveniently with an Intent Service, since an intent service makes multi threading easy.

Again to reiterate just to make sure i'm not misusing terminology: What I mean by Synchronous and blocking/what I want: I make a call to the http server by sending an intent to my intentservice that makes the request. My UI thread, or thread from which this intent was sent, now waits until the request has been processed and a result has been returned before continuing to run.

If you think that I am going about this process (making http calls in a blocking, synchronous way) all wrong, what is another way you might choose to go about it? Thanks!


回答1:


I am sorry, but I think your architecture is not right or I may understand it wrong. IntentService is built to do thing serial way on separate thread. Now you say you want it to be synchronous and blocking. You cannot block UI thread!

In order to create notification system from your IntentService to Activity/Fragment/etc. you have few choices: singleton, broadcast message (receiver, resultReceiver), others?

Based on assumption that service and other parts of the application are working in same process. Best option would be to create manager to do this job. Something like this can be built to start service as well as listen for completion event:

public class MyNetworkManager {

    static MyNetworkManager sInstance;
    Context mContext;
    LinkedList<OnCompletionListener> mListeners;

    private MyNetworkManager(Context context) {
        mContext = context;
        mListeners = new LinkedList<>();
    }

    public static MyNetworkManager getInstance(Context context) {
        if (sInstance == null) {
            synchronized (MyNetworkManager.class) {
                if (sInstance == null) {
                    sInstance = new MyNetworkManager(context.getApplicationContext());
                }
            }
        }
        return sInstance;
    }

    // add listener to listen for completion event
    public void addListener(OnCompletionListener listener) {
        synchronized (mListeners) {
            mListeners.add(listener);
        }
    }

    // remove listener to stop listening for completion event
    public void removeListener(OnCompletionListener listener) {
        synchronized (mListeners) {
            mListeners.remove(listener);
        }
    }

    // call from UI to start service operation
    public void startNetworkOperation() {
        Intent service = new Intent();
        mContext.startService(service);
    }

    // call from service to notify UI (still on different thread, can use Handler to make call on main thread)
    public void notifyCompletion() {
        synchronized (mListeners) {
            for (OnCompletionListener listener : mListeners) {
                listener.onCompleted(this);
            }
        }
    }

    public static interface OnCompletionListener {

        void onCompleted(MyNetworkManager manager);
    }
}



回答2:


Use this pattern

public interface SynchronizationListener {
    //void onStart(int id); not requered

    //void onProgress(int id, long updateTime); not requered

    void onFinish(Object data); // replace Object with your data type
}

In your service add end call this

private void startSynchronization() {
   SynchronizationManager.getInstance().startSynchronizing();
}

Your Singleton Manager

public class SynchronizationManager {

    private static SynchronizationManager instance;
    private Object synRoot = new Object();
    private boolean synchronizing = false;
    private List<SynchronizationListener> synchronizationListeners;

    public SynchronizationManager() {
        synchronizationListeners = new ArrayList<SynchronizationListener>();
    }

    static {
        instance = new SynchronizationManager();
    }

    public static SynchronizationManager getInstance() {
        return instance;
    }

    public boolean isSynchronizing() {
        synchronized (synRoot) {
            return synchronizing;
        }
    }

    public void startSynchronizing() {
        synchronized (synRoot) {
            if (synchronizing) {
                return;
            }
            synchronizing = true;
        }

        Object data; // <-- replace Object with your data type

        if (ConnectivityReceiver.hasGoodEnoughNetworkConnection()) { // check connection

            data = sync();

        }

        synchronized (synRoot) {
            synchronizing = false;
        }

        onSynchronizationFinish(data); // use listener for send data tu Observer Activity
    }

    public void stopSynchronizing() {
        synchronized (synRoot) {
            synchronizing = false;
        }
    }

    public synchronized void registerSynchronizationListener(
            SynchronizationListener listener) {
        if (!synchronizationListeners.contains(listener)) {
            synchronizationListeners.add(listener);
        }
    }

    public synchronized void unregisterSynchronizationListener(
            SynchronizationListener listener) {
        if (synchronizationListeners.contains(listener)) {
            synchronizationListeners.remove(listener);
        }
    }

    public void onSynchronizationStart(int id) {
        for (SynchronizationListener listener : synchronizationListeners) {
            listener.onStart(id);
        }
    }
    protected void onSynchronizationProgress(int id, long updateTime) {
        for (SynchronizationListener listener : synchronizationListeners) {
            listener.onProgress(id, updateTime);
        }
    }

    protected void onSynchronizationFinish(Object data) {
        for (SynchronizationListener listener : synchronizationListeners) {
            listener.onFinish(data);
        }
    }

    protected int sync) {
        // code for load your data your HttpRequest
    }
}

In your activity

private SynchronizationListener synchronizationListener = new SynchronizationListener() {
    /*public void onStart(int id) {

    }

    public void onProgress(int id, long updateTime) {

    }*/

    public void onFinish(Object data) {
        //elaborate data

    }
};

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

    SynchronizationManager.getInstance().registerSynchronizationListener(
            synchronizationListener);
}

@Override
protected void onPause() {
    super.onPause();
    SynchronizationManager.getInstance().unregisterSynchronizationListener(
            synchronizationListener);
}

See this code for example UnivrApp




回答3:


A ContentProvider would be a better choice than an IntentService in my thinking. You can trigger each network call with a query and then return a MatrixCursor with details about the results of your background work. Android already has lots of good plumbing around running queries in background tasks and waiting for the results before triggering ui updates.

in ContentProvider query() method :

    MatrixCursor cursor = new MatrixCursor(new String[]{"_id","uri", "status", "last_modified", "result"});
    String lastModified=null;
    int id =1;
    // do your work here 
    // ..
    // report your work here
    cursor.addRow(new Object[]{id++, uri.toString(), HttpStatus.SC_OK, lastModified, "" });
    // set uri for data observers to register
    cursor.setNotificationUri(getContext().getContentResolver(), uri);
    return cursor;



回答4:


What you try to do is just communication between IntentService and Activity/Fragment.

You can try send broadcast at the end of onHandleIntent and catch it in registered receiver or use ResultReceiver - read more how to implement here.

Edit:

Try this:

  1. Handle all background operations at once in onHandleIntent
  2. On every step send new data using ResultReceiver

    // operation 1
    Bundle b1 = new Bundle();
    b1.putParcelable("data", data1);
    resultReceiver.send(0, b1);
    
    // operation 2
    Bundle b2 = new Bundle();
    b2.putParcelable("data", data2);
    resultReceiver.send(1, b2);
    
  3. Handle it in ResultReceiver

    public void onReceiveResult(int resultCode, Bundle resultData) {
        if (resultCode == 0) { // handle step 1 }
        else if (resultCode == 1) { // handle step 2 }
    }
    


来源:https://stackoverflow.com/questions/25335079/thread-synchronization-with-intentservice

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!