トップAndroid Java > broadcast入門

broadcast入門

システムからのbroadcast(BoradcastIntent)を受ける

システム全体にかかわる情報は「BroadcastIntent」という特殊なIntentを使って、 全てのアプリケーションに向けて発信される[7]。

BroadcastIntentには以下のようなものがある。

 android.intent.action.TIME_TICK                  時刻が変化した(分単位)
 android.intent.action.ACTION_POWER_CONNECTED     電源接続、
 android.intent.action.ACTION_POWER_DISCONNECTED  電源切断、
 android.intent.action.ACTION_SHUTDOWN            シャットダウン、
 android.net.wifi.WIFI_STATE_CHANGED              ネットワーク状態が変わった

まず、記事[7]に倣って次のコードを試した。

        // BroadcastReceiver
        private BroadcastReceiver mReceiver;
 
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
 
            // 受信する情報の種類を設定
            // 電源接続・切断の通知を受け取る
            IntentFilter filter = new IntentFilter();
            filter.addAction( Intent.ACTION_POWER_CONNECTED );
            filter.addAction( Intent.ACTION_POWER_DISCONNECTED );
 
            // 受信した場合の処理の記述
            mReceiver = new BroadcastReceiver(){
                @Override
                public void onReceive( Context context, Intent intent ){
                    // Toast で受信内容を確認
                    Toast.makeText(context, "Receive broadcast:" + intent.getAction(),
                                   Toast.LENGTH_LONG ).show();
                }
            };
 
            // BroadcastReceiverを登録する
            this.registerReceiver( mReceiver, filter );
        }
 
        @Override
        public void onDestroy(){
            super.onDestroy();
 
            // 終了時に、BroadcastReceiverを登録解除する
            this.unregisterReceiver( mReceiver );
        }

独立した Activity と Service間の broadcast

よくある記事は一つのアプリ内での Service から Activity へ broadcast である[1]。 このレベルは難しくない。今回は、アプリAとアプリBがあって、 アプリBは Service でそのスレッドからアプリAの Activity に broadcast したい。

似たことを試みたが制約にかかりうまくいかなかった。

まず、記事[1]を二つのアプリに分けてテストする。

以下のプログラムにより、アプリ B の Service からアプリ A の Activity に情報(この場合は文字列)を送れる ことを確認した。

[アプリA]
public class MainActivity extends AppCompatActivity {
    TextView my_view;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        my_view = findViewById(R.id.my_view);
        UpdateReceiver receiver = new UpdateReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("DO_ACTION");
        registerReceiver(receiver, filter);
    }

    class UpdateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent){
            Bundle extras = intent.getExtras();
            String msg = extras.getString("message");
            my_view.setText(msg);
            Log.i("Received", msg);
        }
    }
}
[アプリB]
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout ll = new LinearLayout(this);
        Button btn = new Button(this);
        btn.setText("start");
        ll.addView(btn);
        setContentView(ll);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent service = new Intent(getApplicationContext(), MyService.class);
                startService(service);
            }
        });
    }
}

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

    public int onStartCommand(Intent intent, int flags, int startId){
        sendMessage("こんにちは");
        stopSelf();
        return START_NOT_STICKY;
    }

    protected void sendMessage(String msg){
        Intent broadcast = new Intent();
        broadcast.putExtra("message", msg);
        broadcast.setAction("DO_ACTION");
        getBaseContext().sendBroadcast(broadcast);
    }

}

マニフェストによる方法

マニフェストによる方法を見かけたので、やってみたが、ダメだった。 android:exported="true" でもダメだった。 どこかに誤まりがあるのか、それとも仕様が変わったのかは分からない。

        <receiver android:name=".MainActivity$UpdateReceiver" android:exported="false">
            <intent-filter>
                <action android:name="DO_ACTION"/>
            </intent-filter>
        </receiver>

事例:位置情報取得

自作地図アプリで位置情報取得を行っているが、電池消費量を抑えるために、別アプリとしてコントロールしたい。

位置情報を文字列で送る

少なくとも同一アプリでは、Location オブジェクトをそのまま broadcast できるが、ここでは、文字列として送る。

    static final String ACTION_BROADCAST = "com.example.gps.broadcast";
    static final String EXTRA_LOCATION   = "com.example.gps.location";

    protected void sendLocation(Location location) {
        Intent broadcast = new Intent();
        broadcast.putExtra(EXTRA_LOCATION, Utils.getLocationText(location));
        broadcast.setAction(ACTION_BROADCAST);
        getBaseContext().sendBroadcast(broadcast);
    }

    static String getLocationText(Location location) {
        return location == null ? "Unknown location" :
                "(" + location.getLatitude() + ", " + location.getLongitude() + ")";
    }

同一アプリで受信する

以下のプログラムにより、同一アプリでの受信を確認した。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final String ACTION_BROADCAST = "com.example.gps.broadcast";
        MyReceiver myReceiver = new MyReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_BROADCAST);
        registerReceiver(myReceiver, filter);

        startService(new Intent(MainActivity.this, LocationUpdatesService.class));
    }

    // Receiver for broadcasts sent by LocationUpdatesService.
    private class MyReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle extras = intent.getExtras();
            String msg = extras.getString("com.example.gps.location");
            Log.d("GPS", msg);
        }
    }
}

別アプリで受信する

以下のプログラムにより、別アプリ(テスト用でこの受信だけのアプリ AppA)での受信を確認した。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        UpdateReceiver receiver = new UpdateReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.example.gps.broadcast");
        registerReceiver(receiver, filter);
    }

    class UpdateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent){
            Bundle extras = intent.getExtras();
            String msg = extras.getString("com.example.gps.location");
            Log.i("Received", msg);
        }
    }
}

自作地図アプリで受信する

自作地図アプリはすでにかなり大きいプログラムになっている。 現在は内部サービスからの broacast を MainActivity ではなく、GPSLogger extends AppCompatActivity で受けている。

まず、この GPSLogger で受けることを考え、次のようにしたが、受信できなかった。

public class GPSLogger extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        final String ACTION_BROADCAST = "com.example.gps.broadcast";
        MyReceiver myReceiver = new MyReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_BROADCAST);
        registerReceiver(myReceiver, filter);
     }

    class MyReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle extras = intent.getExtras();
            String msg = extras.getString("com.example.gps.location");
            Log.d("GPS", msg);
        }
    }

}

そこで MainActivity に移動させた。今度は受信するようになった。

public class MainActivity extends AppCompatActivity

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // 省略

        MyReceiver myReceiver = new MyReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.example.gps.broadcast");
        registerReceiver(myReceiver, filter);
    }


    class MyReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle extras = intent.getExtras();
            String msg = extras.getString("com.example.gps.location");
            Log.d("GPS", msg);
            Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
        }
    }

A.リファレンス

[1] ServiceからActivityへ情報を渡す
[2] [ Android ] ブロードキャストでIntentを送受信する
[3] ブロードキャストの概要
[4] Intentでオブジェクトを渡す方法
[5] BroadcastReceiverを使って位置情報を習得
[6] 位置情報の取得
[7] BroadcastIntentを受け取る
[8] Wifiが繋がったらサービスを呼び出すレシーバー