Download data from internet in background and conc

2019-09-23 13:15发布

I need to download elements from internet and add them to an arraylist in the background. (The download may take a few minutes.)

There is a loop in which part of overall elements are downloaded each iteration and added to the list. I need different activities be able to have access to that arraylist whenever needed, no matter if the download (the loop) is in progress or finished.

It seems a service can do this, but i don't have any idea on how. Considering the code below, how can i achieve this?

class A extends Service {
    void foo(){
    //uses a loop to get elements from internet 
    //then adds the elements to myArraylist in each loop
    }
}


class B extends Activity {
    //needs to have access to myArraylist asynchronously
}


class C extends Activity {
    //needs to have access to myArraylist asynchronously
}  

Note that i need the download process stay active when user switches between activities.

3条回答
不美不萌又怎样
2楼-- · 2019-09-23 13:25

Using the structure recommended by Nick Cardoso but with many changes to meet my case, i managed to solve the problem. here it is:

class A extends Service {
ArrayList arrayList = new ArrayList();
MyApplication app;
void foo(){
   new Thread (new Runnable (){        
    @Override
    public void run() {
       app = (MyApplication)getApplication();
       While(true){
       //get elements from network and put them in arrayList
       app.synchronisedAddCollection(arrayList);            
       LocalBroadcastManager.getInstance(this).sendBroadcast(mediaIntent);
    }
    }  
  }).start();                        
 }
}

And here is my Application class:

public class MyApplication extends Application {
List<HashMap<String, String>> myArrayList = new ArrayList<HashMap<String, String>>();

@Override
public void onCreate() {
    super.onCreate();        
}

public void synchronisedAddCollection(ArrayList<HashMap<String, String>> arrayList) {
    myArrayList.addAll(arrayList); 
}

public ArrayList<HashMap<String, String>> getArrayList(){
    return (ArrayList<HashMap<String, String>>) myArrayList;
}
} 

Here is the activity which needs to access the shared arraylist

 class B extends Activity {
  MyApplication app;
 @Override
 public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    startService(new Intent(getApplicationContext(), MyService.class); 
    LocalBroadcastManager.getInstance(this).registerReceiver(lbr,
              new IntentFilter("mediaIntent"));
 }
 private BroadcastReceiver lbr = new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
          app = (MyApplication)getApplication();
          //now i have access to the app arrayList
          System.out.println(app.myArrayList.size());     
      }
 }
 };
}

Do not forget to register MyApplication and MyService in manifest.

查看更多
Rolldiameter
3楼-- · 2019-09-23 13:26

So the problem you face with what you are asking is that your download loop may be adding to or changing the list while the active activity may also be accessing the same list. This can cause a ConcurrentModificationException. To avoid this what you need to do is synchronise all activity with the list. In order to make it available to all activities and have it accessible to your service I would suggest that the list itself is stored in your application (a class extending Application)

public class MyApplication extends Application {

    private List<MyElement> mElems;

    @Override
    public void onCreate() {
        super.onCreate();
        mElems = Collections.synchronizedList(new ArrayList<MyElement>());
        //this line will start your download service, available accross the whole app
        startService(new Intent(getApplicationContext(), A.class)); 
    }

    //You can use accessor methods and keep the list private to ensure
    //synchronisation doesn't get missed anywhere
    public void synchronisedAddElement(MyElement elem) {
        mElems.add(elem); //already synchronous in this case
    }

    //I havent tested this method, you method below may be safer
    public Iterator getElementsIteratorSynchronised() {
       synchronized(mElems) {
           return list.iterator();
       }
    }

    public Iterator iterateElementsSynchronised(OnElementListener lis) {
       synchronized(mElems) {
           Iterator<MyElement> i = list.iterator();
           if (lis != null) {
               while (l.hasNext()) {
                   lis.onElement(l.next());
               }
           }
       }
    }

    public static class OnElementListener {
         public void onElement(MyElement el);
    }

}

You would write to it as follows

class A extends Service {
     void foo(){
          MyApplication app = (MyApplication) getApplication();
          ... //do your network call loop here, adding to local list
          app.synchronisedAddElement( myNewElement );           
     }
}

And Read

class B extends Activity {

     //the async task just because your comment said async access
     new AsynTask<MyApplication, Void, Void>() {

          public Void doInBackground(MyApplication app) {
              app.iterateElementsSynchronised(new OnElementListener() { 

                  public void onElement(MyElement el) {
                      Log.d(TAG, "Did somethign appropriate with " + el);
                  }

              })
          }

     }.execute( (MyApplication) getApplication() );

}

Please just treat this as pseudo code, I've written it on the train home so the method signatures may vary, but this should get you where you need to be

查看更多
兄弟一词,经得起流年.
4楼-- · 2019-09-23 13:42

You can do it by Broadcast receiver.For send the data on other activity you can use:

                    intent = new Intent(ApplicationSetting.NEW_MESSAGE_ACTION);
                    intent.putExtra(IMMessage.IMMESSAGE_KEY, msg);
                    sendBroadcast(intent);

For receive this message for other any activity you can use this code:

private BroadcastReceiver receiver = new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {

        String action = intent.getAction();
        /*
         * For before commit
         */
        if (ApplicationSetting.NEW_MESSAGE_ACTION.equals(action)) {
            IMMessage message = intent
                    .getParcelableExtra(IMMessage.IMMESSAGE_KEY);
            Log.w("message", "are" + message);

        }

    }

};
查看更多
登录 后发表回答