I am listening to network activity in an android application. When events come over the network, I would like to update some TextViews that reside inside of different Fragments in my application. Right now I have a single screen application where one Fragment is on the screen at a time.
How can I call a method on an existing Fragment inside of a ViewPager, managed by a FragmentStatePagerAdapter?
I am able to "paint" the UI correctly, it gets onscreen as expected. That all works. What I am having a tough time doing is calling a method when a background thread sends a message to my containing Activity. The thread is long running and will updated each TextView frequently (think of a countdown timer).
{thread} -> Actvity.handleMessage() -> {current}Fragment.update();
I am attempting to discern what the "current", onscreen Fragment is so that I can call a method on it to update its TextViews. I am trying to use the findFragmentByTag approach I have read about, but it is always returning a null Fragment object.
Specifically I am doing this:
private String getFragmentTag(int viewPagerId, int fragmentPosition){
     return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
}
//Take the data from the thread (network) and react to it.
@Override
public boolean handleMessage(Message msg) {
    //tell the fragment to update itself        
    //Log.v(TAG, "Handler: " + msg.what);
    int currentFragmentIndex = pager.getCurrentItem();
    Log.v(TAG, "Current Fragment Index: " + Integer.toString(currentFragmentIndex));
    String fTag = getFragmentTag(pager.getId(), currentFragmentIndex );
    Log.v(TAG, "Current Fragment Tag: " + fTag);
    ////THIS IS WHERE I AM HAVING TROUBLE!////
    Log.v(TAG, "fragment: " + getSupportFragmentManager().findFragmentByTag(fTag));
    ////THIS ALWAYS OUTPUTS:
    ////fragment: null
    //now do the update
    //currentFragment.update(sensorDataMap);
    return true;
}
Based on reading a ton and specifically this post - How to get existing fragments when using FragmentPagerAdapter
Here is the relevant code...
MainActivity.java
public class MainActivity extends FragmentActivity implements Callback {
    //receive messages from the other thread
    private  Handler handler = new Handler(this);
    //the runnable listening to the network
    private UDPListener udpl;
    //we will parse the handler data into this map and store the values by int keys
    private SparseArray<String> sensorDataMap = new SparseArray<String>();
    //for logging and convenience
    private static final String TAG = "BRRT";   
    //set up the fragment manager
    private ViewPager pager;
    private ScreenFragmenStatePagerAdapter fragmentManager;
    private ScreenFragment currentFragment;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //remove the titlebar
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        //keep the screen on while we are running
        this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        //set up the pager to move through the fragments
        pager = (ViewPager) findViewById(R.id.viewPager);
        fragmentManager = new ScreenFragmenStatePagerAdapter(getSupportFragmentManager());
        pager.setAdapter(fragmentManager);
        //start up the thread
        Log.v(TAG, "start thread");
        startUDPListener(handler);
        Log.v(TAG, "past start thread");    
    }
    //start up the thread to listen to the network, pass in the handle to this thread for messages
    private void startUDPListener(Handler handler){
        //spawn the listener thread, pass in the context and the handler object
        try{
            udpl = new UDPListener(this.getApplicationContext(), handler, sensorDataMap);
            //TODO remove this debug flag
            //DEBUG
            udpl.setDebug(true);
            //DEBUG 
            //set up the threaded reading, event throwing
            Thread reader = new Thread(udpl);
            reader.start();
        } catch(Exception e){
            //TODO real logging
            Log.v(TAG, "caught after trying to start thread");
            Log.v(TAG, e.getMessage());
        }
    }
    //fragile, as this depends on the current naming convention for these fragment IDs in support/v4.
    private String getFragmentTag(int viewPagerId, int fragmentPosition){
         return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
    }
    //Take the data from the thread (network) and react to it. This will get back unparsed strings
    //and have to parse them in this thread, with the UI
    @Override
    public boolean handleMessage(Message msg) {
        //tell the fragment to update itself        
        //Log.v(TAG, "Handler: " + msg.what);
        int currentFragmentIndex = pager.getCurrentItem();
        Log.v(TAG, "Current Fragment Index: " + Integer.toString(currentFragmentIndex));
        String fTag = getFragmentTag(pager.getId(), currentFragmentIndex );
        Log.v(TAG, "Current Fragment Tag: " + fTag);
        Log.v(TAG, "fragment: " + getSupportFragmentManager().findFragmentByTag(fTag));
        //Log.v(TAG, "Current Fragment Null? " + (currentFragment == null));
        //now do the update
        //currentFragment.update(sensorDataMap);
        return true;
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}
ScreenFragmenStatePagerAdapter .java
public class ScreenFragmenStatePagerAdapter extends FragmentStatePagerAdapter {
    //the set of screens we are interested in.
    private ArrayList<ScreenConfig> screens = new ArrayList<ScreenConfig>(6);
    //for logging and convenience
    private static final String TAG = "CC";
    //convenience variable to let us know what screen we are pointing at, logically
    private ScreenConfig currentScreenConfig;
    public ScreenFragmenStatePagerAdapter(FragmentManager fm) {
        super(fm);
        //set up the data.
        setupScreenData();
    }
    @Override
    public Fragment getItem(int pos) {
        //Log.v(TAG, "fragment pos: " + pos);   
        //hopefully this will always work
        currentScreenConfig = screens.get(pos);
        ScreenFragment sf;
        if (currentScreenConfig.eventCount() == 1){
            sf = OneUpScreen.newInstance();
        } else {
            sf = FourUpScreen.newInstance();
        }
        sf.setConfig(currentScreenConfig);
        return sf;
    }
    @Override
    public int getCount() {
        return screens.size();
    }
    //class that maps the various events to their descriptions in a screen by screen way
    //do all the grunt work of mapping screens to events
    private void setupScreenData(){
        //TODO convert this stuff to JSON so we can pass them around in Bundles
        //a 1up screen
        ScreenConfig configGun = new ScreenConfig("Gun");
        configGun.addEvent(204, "TimeToGun","Ttg", "TTG");
        screens.add(configGun);
        //the rest are 4up screens
        ScreenConfig configBowman = new ScreenConfig("Bowman");
        configBowman.addEvent(5, "ExTws","Tws", "TWS");
        configBowman.addEvent(34, "ExLayTimeOnStrb", "LayTimeOnStrb", "Time Stb");
        configBowman.addEvent(37, "ExLayTimeOnPort", "LayTimeOnPort", "Time Prt");
        configBowman.addEvent(113, "ExNextMarkTwa", "NextMarkTwa", "TWA");
        screens.add(configBowman);
        ScreenConfig configPit = new ScreenConfig("Pit");
        configPit.addEvent(88, "ExMarkTime","MarkTime", "Time Mark");
        configPit.addEvent(113, "ExNextMarkTwa", "NextMarkTwa", "Next TWA");
        configPit.addEvent(111, "ExNextMarkRng", "NextMarkRng", "Next Range");
        configPit.addEvent(112, "ExNextMarkBrg", "NextMarkBrg", "Next Brg");        
        screens.add(configPit);
        ScreenConfig configUpwindTrimmer = new ScreenConfig("Upwind Trimmer");
        configUpwindTrimmer.addEvent(5, "ExTws","Tws", "TWS");
        configUpwindTrimmer.addEvent(1, "ExBsp", "Bsp", "BSP");
        configUpwindTrimmer.addEvent(54, "ExTargBspN", "TargBspN", "Tgt BSP");
        configUpwindTrimmer.addEvent(4, "ExTwa", "Twa", "TWA");     
        screens.add(configUpwindTrimmer);   
        ScreenConfig configDownwindTrimmer = new ScreenConfig("Downwind Trimmer");
        configDownwindTrimmer.addEvent(5, "ExTws","Tws", "TWS");
        configDownwindTrimmer.addEvent(4, "ExTwa", "Twa", "TWA");
        configDownwindTrimmer.addEvent(53, "ExTargTwaN", "TargTwaN", "Tgt BSP");
        configDownwindTrimmer.addEvent(58, "ExPolarBspPercent", "PolarBspPercent", "P Bsp %");      
        screens.add(configDownwindTrimmer);
        ScreenConfig configHelmsman = new ScreenConfig("Helmsman");
        configHelmsman.addEvent(5, "ExTws","Tws", "TWS");
        configHelmsman.addEvent(1, "ExBsp", "Bsp", "BSP");
        configHelmsman.addEvent(6, "ExTwd", "Twd", "TWD");
        configHelmsman.addEvent(4, "ExTwa", "Twa", "TWA");      
        screens.add(configHelmsman);
    }
}
activty_main.xml
<android.support.v4.view.ViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/viewPager"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />
 
    