私は最初のAndroidアプリケーションを作成し、サービスとアクティビティの間のコミュニケーションを回避しようとしています。バックグラウンドで実行され、いくつかのgpsと時間ベースのロギングを行うサービスがあります。サービスを開始および停止するために使用されるアクティビティがあります。
そのため、最初に、アクティビティの開始時にサービスが実行されているかどうかを把握できる必要があります。これについては他にもいくつか質問がありますので、私はそれを理解できると思います(ただし、気軽にアドバイスしてください).
私の本当の問題:アクティビティが実行されていて、サービスが開始されている場合、サービスがアクティビティにメッセージを送信する方法が必要です。この時点での単純な文字列と整数-ほとんどステータスメッセージ。メッセージは定期的に発生しませんので、別の方法がある場合、サービスのポーリングは良い方法だとは思いません。ユーザーがアクティビティを開始したときにのみ、この通信が必要です。サービスからアクティビティを開始したくありません。つまり、アクティビティを開始し、サービスが実行されている場合、何か興味深いことが発生すると、アクティビティUIにステータスメッセージが表示されます。アクティビティを開始しない場合、これらのメッセージは表示されません(それほど興味深いものではありません)。
サービスが実行されているかどうかを判断し、実行されている場合は、アクティビティをリスナーとして追加する必要があるようです。次に、アクティビティが一時停止または停止したときに、リスナーとしてのアクティビティを削除します。それは実際に可能ですか?私がそれを理解できる唯一の方法は、アクティビティにParcelableを実装させ、AIDLファイルを作成して、サービスのリモートインターフェイスを介して渡すことです。それはやり過ぎのように思えますが、アクティビティがどのようにwriteToParcel()/ readFromParcel()を実装する必要があるのかわかりません。
より簡単な方法またはより良い方法はありますか?助けてくれてありがとう。
編集:
後でこれに興味がある人のために、サンプルディレクトリのAIDLでこれを処理するためのGoogleのサンプルコードがあります:/apis/app/RemoteService.Java
サービスと通信するには、3つの明らかな方法があります。
あなたの場合、オプション3を使用します。サービスへの静的な参照を作成し、onCreate()に設定します。
void onCreate(Intent i) {
sInstance = this;
}
静的sInstance
を返す静的関数MyService getInstance()
を作成します。
次に、Activity.onCreate()
でサービスを開始し、サービスが実際に開始されるまで非同期で待機し(アクティビティにインテントを送信することで、サービスの準備ができたことをアプリに通知させることができます)、そのインスタンスを取得します。インスタンスを取得したら、サービスリスナーオブジェクトをサービスに登録すると、設定が完了します。注:アクティビティ内のビューを編集する場合、UIスレッドでビューを変更する必要があります。サービスはおそらく独自のスレッドを実行するため、Activity.runOnUiThread()
を呼び出す必要があります。
最後に行う必要があるのは、Activity.onPause()
内のリスナーオブジェクトへの参照を削除することです。そうしないと、アクティビティコンテキストのインスタンスがリークします。
注:このメソッドは、アプリケーション/アクティビティ/タスクがサービスにアクセスする唯一のプロセスである場合にのみ役立ちます。そうでない場合は、オプション1または2を使用する必要があります。
質問者はおそらくこれを過ぎてから長い間動いていますが、誰かがこれを検索する場合は...
これを処理する別の方法がありますが、これが最も簡単だと思います。
BroadcastReceiver
をアクティビティに追加します。 onResume
でカスタムインテントを受け取るために登録し、onPause
で登録解除します。次に、ステータスの更新または何を持っているかを送信するときに、サービスからそのインテントを送信します。
Intent
を他のアプリがリッスンした場合(誰かが悪意のある操作を行う可能性がありますか?)、不幸にならないようにしてください。それ以上は大丈夫です。
コードサンプルが要求されました:
私のサービスでは、これがあります:
// Do stuff that alters the content of my local SQLite Database
sendBroadcast(new Intent(RefreshTask.REFRESH_DATA_INTENT));
(RefreshTask.REFRESH_DATA_INTENT
は単なる定数文字列です。)
リスニングアクティビティでは、BroadcastReceiver
を定義します。
private class DataUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(RefreshTask.REFRESH_DATA_INTENT)) {
// Do stuff - maybe update my view based on the changed DB contents
}
}
}
クラスのトップでレシーバーを宣言します。
private DataUpdateReceiver dataUpdateReceiver;
これを追加するにはonResume
をオーバーライドします。
if (dataUpdateReceiver == null) dataUpdateReceiver = new DataUpdateReceiver();
IntentFilter intentFilter = new IntentFilter(RefreshTask.REFRESH_DATA_INTENT);
registerReceiver(dataUpdateReceiver, intentFilter);
そして、追加するonPause
をオーバーライドします。
if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver);
今、私の活動は私のサービスを聞いて「ねえ、自分でアップデートしてください」と言っています。データベーステーブルを更新してからアクティビティ内の変更を見つけるのではなく、Intent
でデータを渡すこともできますが、とにかく変更を保持したいので、DB経由でデータを渡すことは理にかなっています。
LocalBroadcastManager
を使用して、アプリ内のローカルサービスから送信されたブロードキャストをリッスンするレシーバーを登録します。参照先は次のとおりです。
http://developer.Android.com/reference/Android/support/v4/content/LocalBroadcastManager.html
メッセンジャーを使用することは、サービスとアクティビティ間で通信するもう1つの簡単な方法です。
アクティビティで、対応するメッセンジャーを持つハンドラーを作成します。これにより、サービスからのメッセージが処理されます。
class ResponseHandler extends Handler {
@Override public void handleMessage(Message message) {
Toast.makeText(this, "message from service",
Toast.LENGTH_SHORT).show();
}
}
Messenger messenger = new Messenger(new ResponseHandler());
メッセンジャーは、メッセージに添付することでサービスに渡すことができます。
Message message = Message.obtain(null, MyService.ADD_RESPONSE_HANDLER);
message.replyTo = messenger;
try {
myService.send(message);
catch (RemoteException e) {
e.printStackTrace();
}
完全な例は、APIデモで見つけることができます: MessengerServiceおよびMessengerServiceActivity 。 MyServiceの仕組みについては、完全な例を参照してください。
他のコメントに記載されていない他の方法は、bindService()を使用してアクティビティからサービスにバインドし、ServiceConnectionコールバックでサービスのインスタンスを取得することです。ここで説明されているように http://developer.Android.com/guide/components/bound-services.html
LiveData
のように機能するEventBus
を使用することもできます。
class MyService : LifecycleService() {
companion object {
var BUS = MutableLiveData<Object>()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
val testItem : Object
// expose your data
if (BUS.hasActiveObservers()) {
BUS.postValue(testItem)
}
return START_NOT_STICKY
}
}
次に、Activity
からオブザーバーを追加します。
MyService.BUS.observe(this, Observer {
it?.let {
// Do what you need to do here
}
})
これからもっと読むことができます blog。
別の方法は、MVCパターンのバリエーションを実装して、アクティビティとサービス自体を介して偽モデルクラスでオブザーバーを使用することです。それがこれを達成するための最良の方法であるかどうかはわかりませんが、それは私のために働いた方法です。いくつかの例が必要な場合は、それを尋ねると私は何かを投稿します。
@MrSnowflakeの回答をフォローアップするには、コード例を使用します。 これはXABBERが現在オープンソースApplication
クラスです 。 Application
クラスは、Listeners
およびManagerInterfacesなどを集中化および調整しています。あらゆる種類のマネージャーが動的にロードされます。 Xabberで開始されたActivity´s
は、Listener
のタイプを報告します。また、Service
が開始すると、Application
クラスに開始済みとして報告します。ここで、Activity
にメッセージを送信するには、Activity
を必要なタイプのlistener
にするだけです。 OnStart()
OnPause()
register/unregで。 Service
は、Application
クラスに、話す必要があるlistener
を尋ねることができ、もしそこにあれば、アクティビティは受信する準備ができています。
Application
クラスを通過すると、これよりも多くの戦利品があることがわかります。
私の方法:
import Android.os.Bundle;
import Android.os.Handler;
import Android.os.IBinder;
import Android.os.Message;
import Android.os.Messenger;
import Android.os.RemoteException;
import Android.util.Log;
import Java.util.ArrayList;
import Java.util.List;
public class MessageManager {
public interface IOnHandleMessage{
// Messages
int MSG_HANDSHAKE = 0x1;
void onHandleMessage(Message msg);
}
private static final String LOGCAT = MessageManager.class.getSimpleName();
private Messenger mMsgSender;
private Messenger mMsgReceiver;
private List<Message> mMessages;
public MessageManager(IOnHandleMessage callback, IBinder target){
mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_ACTIVITY));
mMsgSender = new Messenger(target);
mMessages = new ArrayList<>();
}
public MessageManager(IOnHandleMessage callback){
mMsgReceiver = new Messenger(new MessageHandler(callback, MessageHandler.TYPE_SERVICE));
mMsgSender = null;
mMessages = new ArrayList<>();
}
/* START Getter & Setter Methods */
public Messenger getMsgSender() {
return mMsgSender;
}
public void setMsgSender(Messenger sender) {
this.mMsgSender = sender;
}
public Messenger getMsgReceiver() {
return mMsgReceiver;
}
public void setMsgReceiver(Messenger receiver) {
this.mMsgReceiver = receiver;
}
public List<Message> getLastMessages() {
return mMessages;
}
public void addMessage(Message message) {
this.mMessages.add(message);
}
/* END Getter & Setter Methods */
/* START Public Methods */
public void sendMessage(int what, int arg1, int arg2, Bundle msgData){
if(mMsgSender != null && mMsgReceiver != null) {
try {
Message msg = Message.obtain(null, what, arg1, arg2);
msg.replyTo = mMsgReceiver;
if(msgData != null){
msg.setData(msgData);
}
mMsgSender.send(msg);
} catch (RemoteException rE) {
onException(rE);
}
}
}
public void sendHandshake(){
if(mMsgSender != null && mMsgReceiver != null){
sendMessage(IOnHandleMessage.MSG_HANDSHAKE, 0, 0, null);
}
}
/* END Public Methods */
/* START Private Methods */
private void onException(Exception e){
Log.e(LOGCAT, e.getMessage());
e.printStackTrace();
}
/* END Private Methods */
/** START Private Classes **/
private class MessageHandler extends Handler {
// Types
final static int TYPE_SERVICE = 0x1;
final static int TYPE_ACTIVITY = 0x2;
private IOnHandleMessage mCallback;
private int mType;
public MessageHandler(IOnHandleMessage callback, int type){
mCallback = callback;
mType = type;
}
@Override
public void handleMessage(Message msg){
addMessage(msg);
switch(msg.what){
case IOnHandleMessage.MSG_HANDSHAKE:
switch(mType){
case TYPE_SERVICE:
setMsgSender(msg.replyTo);
sendHandshake();
break;
case TYPE_ACTIVITY:
Log.v(LOGCAT, "HERE");
break;
}
break;
default:
if(mCallback != null){
mCallback.onHandleMessage(msg);
}
break;
}
}
}
/** END Private Classes **/
}
public class activity extends AppCompatActivity
implements ServiceConnection,
MessageManager.IOnHandleMessage {
[....]
private MessageManager mMessenger;
private void initMyMessenger(IBinder iBinder){
mMessenger = new MessageManager(this, iBinder);
mMessenger.sendHandshake();
}
private void bindToService(){
Intent intent = new Intent(this, TagScanService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
/* START THE SERVICE IF NEEDED */
}
private void unbindToService(){
/* UNBIND when you want (onDestroy, after operation...)
if(mBound) {
unbindService(mServiceConnection);
mBound = false;
}
}
/* START Override MessageManager.IOnHandleMessage Methods */
@Override
public void onHandleMessage(Message msg) {
switch(msg.what){
case Constants.MSG_SYNC_PROGRESS:
Bundle data = msg.getData();
String text = data.getString(Constants.KEY_MSG_TEXT);
setMessageProgress(text);
break;
case Constants.MSG_START_SYNC:
onStartSync();
break;
case Constants.MSG_END_SYNC:
onEndSync(msg.arg1 == Constants.ARG1_SUCCESS);
mBound = false;
break;
}
}
/* END Override MessageManager.IOnHandleMessage Methods */
/** START Override ServiceConnection Methods **/
private class BLEScanServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
initMyMessenger(iBinder);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mMessenger = null;
mBound = false;
}
}
/** END Override ServiceConnection Methods **/
public class Blablabla extends Service
implements MessageManager.IOnHandleMessage {
[...]
private MessageManager mMessenger;
@Nullable
@Override
public IBinder onBind(Intent intent) {
super.onBind(intent);
initMessageManager();
return mMessenger.getMsgReceiver().getBinder();
}
private void initMessageManager(){
mMessenger = new MessageManager(this);
}
/* START Override IOnHandleMessage Methods */
@Override
public void onHandleMessage(Message msg) {
/* Do what you want when u get a message looking the "what" attribute */
}
/* END Override IOnHandleMessage Methods */
mMessenger.sendMessage(what, arg1, arg2, dataBundle);
サービスを開始またはバインドするアクティビティで。サービスの「OnBind」メソッドは、バインダーをMessageManagerに返します。アクティビティの「Service Connection」インターフェースメソッドの実装「OnServiceConnected」を通じて、このIBinderを取得し、それを使用してMessageManagerを初期化します。アクティビティがMessageManagerを初期化した後、MessageHandlerはサービスに送信してハンドシェイクし、「MessageHandler」送信者(MessageManagerの「プライベートMessenger mMsgSender;」)を設定できるようにします。これを行うと、サービスはメッセージの送信者を認識します。
MessageManagerのメッセンジャー「送信者」のリスト/キューを使用してこれを実装し、複数のメッセージを異なるアクティビティ/サービスに送信したり、MessageManagerのメッセンジャー「受信者」のリスト/キューを使用して複数のメッセージを受信したりすることもできますさまざまなアクティビティ/サービスからのメッセージ。
「MessageManager」インスタンスには、受信したすべてのメッセージのリストがあります。
この「MessageManager」インスタンスを使用した「アクティビティのメッセンジャー」と「サービスメッセンジャー」間の接続は自動であるため、「OnServiceConnected」メソッドと「ハンドシェイク」を使用して行われます。
これがあなたのお役に立てば幸いです:)ありがとうございます!さようなら:D
コールバックを作成する
public interface MyCallBack{
public void getResult(String result);
}
アクティビティ側:
サービスがアクティビティにバインドおよびバインド解除されたときにコールバックを登録および登録解除します。
public class YourActivity extends AppCompatActivity implements MyCallBack{
private Intent myIntent;
private GPSService gpsService;
private boolean bound = false;
@Override
public void getResult(String result){
// show in textView textView.setText(result);
}
@Override
protected void onStart()
{
super.onStart();
bindService();
}
@Override
protected void onStop() {
super.onStop();
unbindService();
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
GPSService.GPSBinder binder = (GPSService.GPSBinder) service;
gpsService= binder.getService();
bound = true;
gpsService.registerCallBack(YourActivity.this); // register
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
bound = false;
}
};
private void bindService() {
bindService(notifyMeIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private void unbindService(){
if (bound) {
gpsService.registerCallBack(null); // unregister
unbindService(serviceConnection);
bound = false;
}
}
// Call this method somewhere to start Your GPSService
private void startService(){
myIntent = new Intent(this, GPSService.class);
startService(myIntent );
}
}
サービス側:
必要なときにいつでもコールバックメソッドを呼び出す
public class GPSService extends Service{
private MyCallBack myCallback;
private IBinder serviceBinder = new GPSBinder();
public void registerCallBack(MyCallBack myCallback){
this.myCallback= myCallback;
}
public class GPSBinder extends Binder{
public GPSService getService(){
return GPSService.this;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent){
return serviceBinder;
}
}
Madhurが述べたように、通信にはバスを使用できます。
バスを使用する場合、いくつかのオプションがあります。
Ottoイベントバスライブラリ(RxJavaの代わりに非推奨)
グリーンロボットのEventBus
http://greenrobot.org/eventbus/
NYBus(RxBus、RxJavaを使用して実装。EventBusと非常に類似)
LocalBroadcastManager、イベントバス、メッセンジャーに加えて、この質問ですでに回答があり、Pending Intentサービスから通信します。
前述のように、私のブログ投稿で here
サービスとアクティビティ間の通信は、PendingIntentを使用して行うことができます。そのために、createPendingResult()。createPendingResult()を使用して、新しいPendingIntentオブジェクトを作成します。 onActivityResult(int、int、Intent)コールバック内で使用して結果データをアクティビティに送り返すサービス。PendingIntentはParcelableであるため、Intentエクストラに入れることができるため、アクティビティはこのPendingIntentをサービスに渡すことができます。次に、サービスはPendingIntentのsend()メソッドを呼び出して、onActivityResultを介してイベントをアクティビティに通知できます。
アクティビティ
public class PendingIntentActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); PendingIntent pendingResult = createPendingResult( 100, new Intent(), 0); Intent intent = new Intent(getApplicationContext(), PendingIntentService.class); intent.putExtra("pendingIntent", pendingResult); startService(intent); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 100 && resultCode==200) { Toast.makeText(this,data.getStringExtra("name"),Toast.LENGTH_LONG).show(); } super.onActivityResult(requestCode, resultCode, data); } }
サービス
public class PendingIntentService extends Service { private static final String[] items= { "lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus" }; private PendingIntent data; @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { data = intent.getParcelableExtra("pendingIntent"); new LoadWordsThread().start(); return START_NOT_STICKY; } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { super.onDestroy(); } class LoadWordsThread extends Thread { @Override public void run() { for (String item : items) { if (!isInterrupted()) { Intent result = new Intent(); result.putExtra("name", item); try { data.send(PendingIntentService.this,200,result); } catch (PendingIntent.CanceledException e) { e.printStackTrace(); } SystemClock.sleep(400); } } } } }