Android在片段和服务之间发送消息 [英] Android sending messages between fragment and service
问题描述
我有一个带有按钮的片段.当单击时,它告诉服务开始轮询传感器,然后将传感器数据插入后台线程的数据库中.当再次按下按钮时,服务将停止.当按下停止"按钮时,执行者队列中仍可能有一些任务正在插入到数据库中,因此在此期间,我想显示一个进度对话框,并在清除整个队列后将其关闭.带有按钮的片段如下所示:
I have a fragment with a button. When clicked it tells a service to start polling sensors and then insert the sensor data into a database on a background thread. When the button is pushed again, the service will stop. When the Stop button is pushed, there may still be tasks in the executor queue that is inserting into the DB, so during this time I want to display a progress dialog, and dismiss it once the entire queue is clear. The fragment with the button looks like this:
public class StartFragment extends Fragment implements View.OnClickListener {
Button startButton;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_start, container, false);
startButton = (Button) view.findViewById(R.id.startButton);
startButton.setOnClickListener(this);
return view;
}
@Override
public void onClick(View v) {
if (recording has not yet started){
mainActivity.startService(new Intent(mainActivity, SensorService.class));
} else {
//I want to display a progress dialog here when the service is told to stop
//Once all executor task queue is clear, I want to dismiss this dialog
mainActivity.stopService(new Intent(mainActivity, SensorService.class));
}
}
}
首次单击该按钮时,将启动以下服务:
When the button is clicked the first time, the following service will start:
public class SensorService extends Service implements SensorEventListener {
public static final int SCREEN_OFF_RECEIVER_DELAY = 100;
private SensorManager sensorManager = null;
private WakeLock wakeLock = null;
ExecutorService executor;
Runnable insertHandler;
private void registerListener() {
//register 4 sensor listeners (acceleration, gyro, magnetic, gravity)
}
private void unregisterListener() {
sensorManager.unregisterListener(this);
}
public BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "onReceive("+intent+")");
if (!intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
return;
}
Runnable runnable = new Runnable() {
public void run() {
Log.i(TAG, "Runnable executing...");
unregisterListener();
registerListener();
}
};
new Handler().postDelayed(runnable, SCREEN_OFF_RECEIVER_DELAY);
}
};
public void onSensorChanged(SensorEvent event) {
//get sensor values and store into 4 different arrays here
//insert into database in background thread
executor.execute(insertHandler);
}
@Override
public void onCreate() {
super.onCreate();
//get sensor manager and sensors here
PowerManager manager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
//Executor service and runnable for DB inserts
executor = Executors.newSingleThreadExecutor();
insertHandler = new InsertHandler();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startForeground(Process.myPid(), new Notification());
registerListener();
wakeLock.acquire();
return START_STICKY;
}
@Override
public void onDestroy() {
//Prevent new tasks from being added to thread
executor.shutdown();
try {
//Wait for all tasks to finish before we proceed
while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
Log.i(TAG, "Waiting for current tasks to finish");
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
if (executor.isTerminated()){
//Stop everything else once the task queue is clear
unregisterReceiver(receiver);
unregisterListener();
wakeLock.release();
dbHelper.close();
stopForeground(true);
//Once the queue is clear, I want to send a message back to the fragment to dismiss the progress dialog here
}
}
class InsertHandler implements Runnable {
public void run() {
//get sensor values from 4 arrays, and insert into db here
}
}
所以我想在按下第二个按钮时显示对话框.然后再次按下该按钮,服务将停止,我要等到队列清除后再将dismiss事件发送回该片段,以关闭进度对话框.
So I want to display the dialog on the 2nd button press. Then once it is pressed again, service will stop, and I want to wait until the queue is clear and then send a dismiss event back to the fragment to dismiss the progress dialog.
显示对话框很容易.我可以在调用stopService
之前在片段的onClick
方法中添加进度对话框代码
Showing the dialog is easy. I can just add progress dialog code in the onClick
method of the fragment, before stopService
is called
我很难弄清楚如何在SensorService
的onDestroy
中发回消息以消除该对话框
I'm having difficulty with figuring out how to send a message back in onDestroy
of the SensorService
to dismiss that dialog
在不借助外部库的情况下实现此目的的最佳方法是什么?
Whats the best way of doing this without resorting to external libraries?
是否可以使用我在SensorService
中使用的BroadcastReceiver
的某种方式?还是最好在片段中创建一个新的Handler
,然后以某种方式将其传递给服务,以便它可以将消息发送回该片段?
Is there some way that the BroadcastReceiver
I'm using in SensorService
can be used? Or maybe it's better to create a new Handler
in the fragment and somehow pass it through to the service so it can send a message back to the fragment?
我已根据以下答案之一尝试了以下方法:
I have tried the following based on one of the answers below:
在我的片段类中添加了MessageHandler
类:
Added a MessageHandler
class to my fragment class:
public static class MessageHandler extends Handler {
@Override
public void handleMessage(Message message) {
int state = message.arg1;
switch (state) {
case 0:
stopDialog.dismiss();
break;
case 1:
stopDialog = new ProgressDialog(mainActivity);
stopDialog.setMessage("Stopping...");
stopDialog.setTitle("Saving data");
stopDialog.setProgressNumberFormat(null);
stopDialog.setCancelable(false);
stopDialog.setMax(100);
stopDialog.show();
break;
}
}
}
在我的片段中创建了MessageHandler
的新实例(试图将其放置在各个地方...结果相同):
Created a new instance of MessageHandler
in my fragment (tried placing this in a variety of places...same results):
public static Handler messageHandler = new MessageHandler();
然后使用以下命令从我的片段启动服务:
The service is then started from my fragment using:
Intent startService = new Intent(mainActivity, SensorService.class);
startService.putExtra("MESSENGER", new Messenger(messageHandler));
getContext().startService(startService);
在我的SensorService
BroadcastReceiver
中,我创建了messageHandler:
In my SensorService
BroadcastReceiver
I create the messageHandler:
Bundle extras = intent.getExtras();
messageHandler = (Messenger) extras.get("MESSENGER");
然后我在SensorService
onDestroy
的开头显示对话框:
Then I show the dialog at the very beginning of SensorService
onDestroy
:
sendMessage("SHOW");
并在同一方法结束时将其关闭:
and dismiss it at the very end of that same method:
sendMessage("HIDE");
我的sendMessage
方法如下:
public void sendMessage(String state) {
Message message = Message.obtain();
switch (state) {
case "SHOW":
message.arg1 = 1;
break;
case "HIDE" :
message.arg1 = 0;
break;
}
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
这样我就可以启动服务正常"了,但是当我再次按它停止时,我得到了:
So I can start the Service OK, but when I press it again to stop, I get this:
java.lang.RuntimeException:无法停止服务com.example.app.SensorService@21124f0:java.lang.NullPointerException:尝试调用虚拟方法'void android.os.Messenger.send(android.os.Message) '在空对象引用上
java.lang.RuntimeException: Unable to stop service com.example.app.SensorService@21124f0: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.os.Messenger.send(android.os.Message)' on a null object reference
及其对SensorService
的行105的引用,其中我有messageHandler.send(message)
and its referring to Line 105 of SensorService
where I have messageHandler.send(message)
关于可能出什么问题的想法?
Thoughts on what might be wrong?
推荐答案
原来,我的原始问题"的编辑"中的代码有效,但是我必须重新整理一下一些代码:
It turns out that the code in the Edit of my original question works, but I have to shuffle around some of my code:
Bundle extras = intent.getExtras();
messageHandler = (Messenger) extras.get("MESSENGER");
以上内容需要移到SensorService
的onStartCommand
中,而不是放在BroadcastReceiver
The above needs to be moved to onStartCommand
of SensorService
instead of being in the BroadcastReceiver
这篇关于Android在片段和服务之间发送消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!