Android用の独自のMusicPlayerを作成しようとしています。私が問題になったのは、バックグラウンドでいくつかのことを実行しています。メインアクティビティはGUIを管理し、現在まですべての曲が再生されています。 GUIと音楽の演奏クラスを分離したかった。私は音楽管理の部分をサービスに入れて、他のものは今のままにしておきたいと思っています。
私の問題は、双方向のオブジェクトの移動を含む多くの通信が行われているため、アクティビティとサービス間の通信を整理できないことです。ここでStack Overflowで検索した多くのテクニックを試しましたが、毎回問題が発生しました。オブジェクトをアクティビティに送信したり、その逆を行うにはサービスが必要です。ウィジェットを追加すると、サービスと通信できるようになります。
ソースコードの場所のコメントが必要な場合は、どんなヒントも歓迎しますが、この移行では混chaとしました。
これについて、サービスから乱数を返す1つのメソッドを呼び出すことよりも高度なチュートリアルはありますか? :P
編集:可能な解決策は、RoboGuiceライブラリを使用し、オブジェクトを注入して移動することです
バインドとコールバックインターフェイスを使用して、アクティビティとサービス間の通信を実装しました。
サービスにデータを送信するために、アクティビティへのサービスインスタンスを再実行するバインダーを使用しました。その後、アクティビティはサービスのパブリックメソッドにアクセスできます。
サービスからアクティビティにデータを送り返すために、フラグメントとアクティビティ間で通信するときに使用しているように、コールバックインターフェイスを使用しました。
以下に、それぞれのコードサンプルを示します。次の例は、アクティビティとサービスの双方向関係を示しています。アクティビティには2つのボタンがあります。最初のボタンは、サービスを開始および停止します。 2番目のボタンは、サービスで実行されるタイマーを開始します。
サービスは、タイマーの進行状況とともにコールバックを通じてアクティビティを更新します。
私の活動:
//Activity implements the Callbacks interface which defined in the Service
public class MainActivity extends ActionBarActivity implements MyService.Callbacks{
ToggleButton toggleButton;
ToggleButton tbStartTask;
TextView tvServiceState;
TextView tvServiceOutput;
Intent serviceIntent;
MyService myService;
int seconds;
int minutes;
int hours;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serviceIntent = new Intent(MainActivity.this, MyService.class);
setViewsWidgets();
}
private void setViewsWidgets() {
toggleButton = (ToggleButton)findViewById(R.id.toggleButton);
toggleButton.setOnClickListener(btListener);
tbStartTask = (ToggleButton)findViewById(R.id.tbStartServiceTask);
tbStartTask.setOnClickListener(btListener);
tvServiceState = (TextView)findViewById(R.id.tvServiceState);
tvServiceOutput = (TextView)findViewById(R.id.tvServiceOutput);
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
Toast.makeText(MainActivity.this, "onServiceConnected called", Toast.LENGTH_SHORT).show();
// We've binded to LocalService, cast the IBinder and get LocalService instance
MyService.LocalBinder binder = (MyService.LocalBinder) service;
myService = binder.getServiceInstance(); //Get instance of your service!
myService.registerClient(MainActivity.this); //Activity register in the service as client for callabcks!
tvServiceState.setText("Connected to service...");
tbStartTask.setEnabled(true);
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
Toast.makeText(MainActivity.this, "onServiceDisconnected called", Toast.LENGTH_SHORT).show();
tvServiceState.setText("Service disconnected");
tbStartTask.setEnabled(false);
}
};
View.OnClickListener btListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if(v == toggleButton){
if(toggleButton.isChecked()){
startService(serviceIntent); //Starting the service
bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE); //Binding to the service!
Toast.makeText(MainActivity.this, "Button checked", Toast.LENGTH_SHORT).show();
}else{
unbindService(mConnection);
stopService(serviceIntent);
Toast.makeText(MainActivity.this, "Button unchecked", Toast.LENGTH_SHORT).show();
tvServiceState.setText("Service disconnected");
tbStartTask.setEnabled(false);
}
}
if(v == tbStartTask){
if(tbStartTask.isChecked()){
myService.startCounter();
}else{
myService.stopCounter();
}
}
}
};
@Override
public void updateClient(long millis) {
seconds = (int) (millis / 1000) % 60 ;
minutes = (int) ((millis / (1000*60)) % 60);
hours = (int) ((millis / (1000*60*60)) % 24);
tvServiceOutput.setText((hours>0 ? String.format("%d:", hours) : "") + ((this.minutes<10 && this.hours > 0)? "0" + String.format("%d:", minutes) : String.format("%d:", minutes)) + (this.seconds<10 ? "0" + this.seconds: this.seconds));
}
}
そして、ここにサービスがあります:
public class MyService extends Service {
NotificationManager notificationManager;
NotificationCompat.Builder mBuilder;
Callbacks activity;
private long startTime = 0;
private long millis = 0;
private final IBinder mBinder = new LocalBinder();
Handler handler = new Handler();
Runnable serviceRunnable = new Runnable() {
@Override
public void run() {
millis = System.currentTimeMillis() - startTime;
activity.updateClient(millis); //Update Activity (client) by the implementd callback
handler.postDelayed(this, 1000);
}
};
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//Do what you need in onStartCommand when service has been started
return START_NOT_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
//returns the instance of the service
public class LocalBinder extends Binder{
public MyService getServiceInstance(){
return MyService.this;
}
}
//Here Activity register to the service as Callbacks client
public void registerClient(Activity activity){
this.activity = (Callbacks)activity;
}
public void startCounter(){
startTime = System.currentTimeMillis();
handler.postDelayed(serviceRunnable, 0);
Toast.makeText(getApplicationContext(), "Counter started", Toast.LENGTH_SHORT).show();
}
public void stopCounter(){
handler.removeCallbacks(serviceRunnable);
}
//callbacks interface for communication with service clients!
public interface Callbacks{
public void updateClient(long data);
}
}
更新:2016年7月10日
IMOカスタムイベントにBroadcastReceiverを使用することは、前述のメッセンジャーがデバイスのローテーションやメモリリークのアクティビティレクリエーションを処理しないため、より良い方法だと思います。
アクティビティのイベント用にカスタムBroadCast Receiverを作成できます。その後、メッセンジャーも使用できます。
あなたのActivity
messageHandlerクラスを作成します
public static class MessageHandler extends Handler {
@Override
public void handleMessage(Message message) {
int state = message.arg1;
switch (state) {
case HIDE:
progressBar.setVisibility(View.GONE);
break;
case SHOW:
progressBar.setVisibility(View.VISIBLE);
break;
}
}
}
次のようにインスタンスを持つことができます
public static Handler messageHandler = new MessageHandler();
このHandlerオブジェクトを追加データとしてService
を開始します
Intent startService = new Intent(context, SERVICE.class)
startService.putExtra("MESSENGER", new Messenger(messageHandler));
context.startService(startService);
Service
で、このオブジェクトをインテントから受け取り、サービスのMessenger
変数を次のように初期化します。
private Messenger messageHandler;
Bundle extras = intent.getExtras();
messageHandler = (Messenger) extras.get("MESSENGER");
sendMessage(ProgressBarState.SHOW);
そして、アクティビティにメッセージを送信するメソッドsendMessage
を作成します。
public void sendMessage(ProgressBarState state) {
Message message = Message.obtain();
switch (state) {
case SHOW :
message.arg1 = Home.SHOW;
break;
case HIDE :
message.arg1 = Home.HIDE;
break;
}
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
上記のサンプルコードは、Serviceからメッセージを受信したときにアクティビティのProgressBarを表示および非表示にします。
インテントは、ActivitiyとService間の通信に適したソリューションです。
サービスでインテントを受信するための高速なソリューションは、サブクラス化 IntentService classです。キューとワーカースレッドを使用して、インテントとして表現される非同期リクエストを処理します。
サービスからアクティビティへの通信では、インテントをブロードキャストできますが、コンテキストから通常のsendBroadcast()を使用する代わりに、サポートライブラリから LocalBroadcastManager を使用するのがより効率的な方法です。
サンプルサービス。
public class MyIntentService extends IntentService {
private static final String ACTION_FOO = "com.myapp.action.FOO";
private static final String EXTRA_PARAM_A = "com.myapp.extra.PARAM_A";
public static final String BROADCAST_ACTION_BAZ = "com.myapp.broadcast_action.FOO";
public static final String EXTRA_PARAM_B = "com.myapp.extra.PARAM_B";
// called by activity to communicate to service
public static void startActionFoo(Context context, String param1) {
Intent intent = new Intent(context, MyIntentService.class);
intent.setAction(ACTION_FOO);
intent.putExtra(EXTRA_PARAM1, param1);
context.startService(intent);
}
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_FOO.equals(action)) {
final String param1 = intent.getStringExtra(EXTRA_PARAM_A);
// do something
}
}
}
// called to send data to Activity
public static void broadcastActionBaz(String param) {
Intent intent = new Intent(BROADCAST_ACTION_BAZ);
intent.putExtra(EXTRA_PARAM_B, param);
LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
bm.sendBroadcast(intent);
}
}
アクティビティ例
public class MainActivity extends ActionBarActivity {
// handler for received data from service
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(MyIntentService.BROADCAST_ACTION_BAZ)) {
final String param = intent.getStringExtra(EXTRA_PARAM_B);
// do something
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter filter = new IntentFilter();
filter.addAction(MyIntentService.BROADCAST_ACTION_BAZ);
LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
bm.registerReceiver(mBroadcastReceiver, filter);
}
@Override
protected void onDestroy() {
LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
bm.unregisterReceiver(mBroadcastReceiver);
super.onDestroy();
}
// send data to MyService
protected void communicateToService(String parameter) {
MyIntentService.startActionFoo(this, parameter);
}
}
正解に問題があると思います。私はそれについてコメントするのに十分な評判がありません。
答えの右側:サービスへのポインタを取得するためのbindService()アクティビティ呼び出しは問題ありません。接続が維持されるときにサービスコンテキストが維持されるためです。
答えが間違っています:コールバックするActivityクラスへのサービスポインターは悪い方法です。アクティビティコンテキストがRelease =>例外である場合、アクティビティインスタンスはnullではない可能性があります。
答えの間違った解決策:サービスがアクティビティにインテントを送信します。 BroadcastReceiverを介したアクティビティ受信者の意図。
注:この場合、同じプロセスのサービスとアクティビティ、LocalBroadcastManagerを使用してインテントを送信する必要があります。パフォーマンスとセキュリティが向上します
この場合の最善の方法は、さまざまなアクションのためにサービスからブロードキャストを実行し、アクティビティでそれを受信することによって通信することです。カスタムブロードキャストを作成し、完了、変更、準備などの特定のイベントを定義するコードを送信できます。
これは、アクティビティとサービス間の通信の簡単な例です
MyReceiver myReceiver; //my global var receiver
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layourAwesomexD);
registerReceiver();
}
//When the activity resume, the receiver is going to register...
@Override
protected void onResume() {
super.onResume();
checkStatusService(); // verficarStatusServicio(); <- name change
registerReceiver();
}
//when the activity stop, the receiver is going to unregister...
@Override
protected void onStop() {
unregisterReceiver(myReceiver); //unregister my receiver...
super.onStop();
}
//function to register receiver :3
private void registerReceiver(){
//Register BroadcastReceiver
//to receive event from our service
myReceiver = new MyReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(MyService.SENDMESAGGE);
registerReceiver(myReceiver, intentFilter);
}
// class of receiver, the magic is here...
private class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context arg0, Intent arg1) {
//verify if the extra var exist
System.out.println(arg1.hasExtra("message")); // true or false
//another example...
System.out.println(arg1.getExtras().containsKey("message")); // true or false
//if var exist only print or do some stuff
if (arg1.hasExtra("message")) {
//do what you want to
System.out.println(arg1.getStringExtra("message"));
}
}
}
public void checkStatusService(){
if(MyService.serviceStatus!=null){
if(MyService.serviceStatus == true){
//do something
//textview.text("Service is running");
}else{
//do something
//textview.text("Service is not running");
}
}
}
public class MyService extends Service {
final static String SENDMESAGGE = "passMessage";
public static Boolean serviceStatus = false;
@Override
public void onCreate() {
super.onCreate();
serviceStatus=true;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {return null;}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//you service etc...
passMessageToActivity("hello my friend this an example of send a string...");
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
passMessageToActivity("The service is finished, This is going to be more cooler than the heart of your ex...");
System.out.println("onDestroy");
serviceStatus=false;
}
private void passMessageToActivity(String message){
Intent intent = new Intent();
intent.setAction(SENDMESAGGE);
intent.putExtra("message",message);
sendBroadcast(intent);
}
}
onStartCommand
で開始されます。onStartCommand
で取得しますreturn
のonStartCommand
に関する違い: START_STICKYとSTART_REDELIVER_INTENTの違い? とGoogleの公式Webサイトを確認してください: Services最も簡単で効率的な方法は、GreenRobotの EventBus を使用することです。
簡単な3つの手順を使用します。
1イベントの定義
public static class MessageEvent { /* Additional fields if needed */ }
2サブスクライバーの準備:サブスクリプションメソッドを宣言して注釈を付け、オプションでスレッドモードを指定します:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
サブスクライバーを登録および登録解除します。たとえば、Androidでは、アクティビティとフラグメントは通常、ライフサイクルに従って登録する必要があります。
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
3イベントのポスト:
EventBus.getDefault().post(new MessageEvent());
非常に簡単で強力な方法は、EventBusを使用することです。Gradleビルドに追加して、簡単なパブリッシャー/サブスクライバーパターンを楽しむことができます。
Androidドキュメントをご覧ください
http://developer.Android.com/guide/components/bound-services.html#Binder