システム全体にかかわる情報は「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 ); }
よくある記事は一つのアプリ内での 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(); } }