enrm
enrm

Reputation: 705

Android - passing data from Service to main at unknown point in time

I've looked at some of the other questions here on SO and none seem to be answering my question. I have a main activity with a simple button. Right now it calls startService(new Intent(MainActivity.this, BluetoothScanner.class) which runs until it finds another bluetooth device. When this is done, I want the BluetoothScanner to report that it is complete and return something (a string perhaps) to the main activity, so I can create a popup or something that notifies the user that a bluetooth device has been seen in the vicinity. How do I best go about this?

The button looks like:

public void onClick(View v) {
    switch(on) {
        case 0:
            startService(new Intent(this, BluetoothScanner.class));
            on = 1;
            break;
        case 1:
            stopService(new Intent(...);
            on = 0;
            break;
       }
 } 
});

There seems to be some alternatives, like global variables (bad), resultReceivers,bundling stuff in intents. But I'm not sure how to do this. What would you suggest solves my problem?

Regards

Upvotes: 0

Views: 54

Answers (2)

ci_
ci_

Reputation: 8774

There are many ways to do this, which is best depends on your particular circumstances. One simple solution with no external dependencies is to use a BroadcastReceiver in your Activity, override onReceive() and do whatever you want to do there, e.g. pop up a Dialog displaying some String returned by your Service. In your Service then you would do what you need to do and when you're finished, use the LocalBroadcastManager to send a broadcast with sendBroadcast.

Here's a minimal example (MCVE that shows how you could do this (without the Bluetooth part, from your question it seems that's not your problem):

Example ActionBarActivity:

public class MainActivity extends ActionBarActivity {
    private Dialog dialog;

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            dialog = new AlertDialog.Builder(MainActivity.this)
                    .setTitle("Some title")
                    .setMessage(intent.getExtras().getString("payload"))
                    .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                        }
                    })
                    .create();
            dialog.show();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout layout = new LinearLayout(this);
        Button button = new Button(this);
        button.setText("Click!");
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startService(new Intent(MainActivity.this, MyService.class));
            }
        });
        layout.addView(button);
        setContentView(layout);
        LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter("MyAction"));
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
        if (dialog != null && dialog.isShowing())
            dialog.dismiss();
    }

}

Example Service:

public class MyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                LocalBroadcastManager.getInstance(MyService.this).sendBroadcast(new Intent("MyAction").putExtra("payload", "Some string"));
                stopSelf();
            }
        }, 3000);
        return START_NOT_STICKY;
    }
}

Example AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testso" >

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="TestSO"
        android:theme="@style/Theme.AppCompat" >
        <activity
            android:name=".MainActivity"
            android:label="TestSO" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name=".MyService"
            android:exported="false" />
    </application>

</manifest>

Upvotes: 1

CommonsWare
CommonsWare

Reputation: 1007369

I would recommend using an event bus. Have the service post a BluetoothScanCompletedEvent or some such. Have your activity subscribe on the bus to listen for such events and do work when the event arrives.

My personal preference is greenrobot's EventBus. LocalBroadcastManager is in the Android Support package and would also fill the role. Square's Otto is the third major event bus for Android, but its threading approach is probably not ideal for your use case.

This directory contains sample projects demonstrating all three event bus implementations.

Upvotes: 3

Related Questions