Dej*_*jan 51 android message-passing android-service android-activity
我正在尝试为Android创建自己的MusicPlayer.我遇到问题的地方是在后台运行一些东西.主要活动管理GUI,到目前为止所有的歌曲都在播放.我想分开GUI和音乐播放课程.我想把音乐管理部分放在服务中,并留下现在的其他东西.
我的问题是我无法组织活动和服务之间的沟通,因为它们之间正在进行大量的沟通,包括双向移动物体.我尝试了很多技术,我在这里搜索Stack Overflow但每次遇到问题时都会这样做.我需要Service才能将对象发送到Activity,反之亦然.当我添加小部件时,我也希望它能够与服务进行通信.
任何提示都很受欢迎,如果你需要源代码发表评论,但现在在这个转变中它变得混乱.
是否有更高级的教程,而不是调用一个从服务返回随机数的方法?:P
编辑:可能的解决方案是使用RoboGuice库并注入移动对象
Mot*_*tov 70
我已经使用Bind和Callbacks接口实现了Activity和Service之间的通信.
为了将数据发送到服务,我使用了Binder,它将Service instace重新转换为Activity,然后Activity可以访问Service中的公共方法.
为了从服务中将数据发送回Activity,我使用了Callbacks界面,就像你想要在Fragment和Activity之间进行通信一样.
以下是每个代码示例:以下示例显示了Activity和Service双向关系:Activity有2个按钮:第一个按钮将启动和停止服务.第二个按钮将启动在服务中运行的计时器.
该服务将通过回调更新Activity以及计时器进度.
我的活动:
//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));
}
}
Run Code Online (Sandbox Code Playgroud)
这是服务:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
Rac*_*hra 55
更新:2016年7月10日
IMO我认为使用BroadcastReceiver进行自定义事件是更好的方法,因为所提到的Messengers不会处理设备轮换时的活动重新创建以及可能的内存泄漏.
您可以为活动中的事件创建自定义BroadCast Receiver,然后您也可以使用Messenger.
在你的 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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在你可以将它的实例作为
public static Handler messageHandler = new MessageHandler();
Run Code Online (Sandbox Code Playgroud)
启动Service
与该Handler对象作为一个额外的数据,
Intent startService = new Intent(context, SERVICE.class)
startService.putExtra("MESSENGER", new Messenger(messageHandler));
context.startService(startService);
Run Code Online (Sandbox Code Playgroud)在你Service
的意图中,你从意图中接收这个对象,并Messenger
在Service as中初始化变量
private Messenger messageHandler;
Bundle extras = intent.getExtras();
messageHandler = (Messenger) extras.get("MESSENGER");
sendMessage(ProgressBarState.SHOW);
Run Code Online (Sandbox Code Playgroud)
然后编写一个方法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();
}
}
Run Code Online (Sandbox Code Playgroud)上面的示例代码显示并隐藏了Activity中的ProgressBar,因为从服务接收消息.
mpo*_*lci 15
意图是Activitiy和Service之间通信的良好解决方案.
在服务中接收意图的快速解决方案是继承IntentService类.它使用队列和工作线程处理表示为Intents的异步请求.
对于从服务到Activity的通信,您可以广播意图,但不是使用Context中的普通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);
}
}
Run Code Online (Sandbox Code Playgroud)
示例活动
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);
}
}
Run Code Online (Sandbox Code Playgroud)
我认为正确答案存在问题.我没有足够的声誉来评论它.
在答案中: Activity调用bindService()来获取指向Service的指针是正常的.因为在维护连接时会维护服务上下文.
错误的答案: 服务指针调用Activity类是不好的方法.在Activity上下文中,Activity实例可能不为null,这里是Release => exception.
解答错误的答案: 服务发送意图到Activity.和BroadcastReceiver的Activity接收器意图.
注意: 在这种情况下,Service和Activity在同一个Process中,你应该使用LocalBroadcastManager来发送intent.它使性能和安全性更好
归档时间: |
|
查看次数: |
82328 次 |
最近记录: |