我有一个带有按钮的片段.单击时,它告诉服务开始轮询传感器,然后将传感器数据插入后台线程的数据库中.当再次按下按钮时,服务将停止.当按下“停止”按钮时,执行者队列中仍可能有一些任务正在插入到数据库中,因此在此期间,我想显示一个进度对话框,并在清除整个队列后将其关闭.带按钮的片段如下所示:
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)); } }}
首次单击该按钮时,将启动以下服务:
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事件发送回片段以关闭进度对话框.
显示对话框很容易.我可以在调用stopService之前,在片段的onClick方法中添加进度对话框代码
我在弄清楚如何在SensorService的onDestroy中发送消息以关闭该对话框时遇到了困难
在不借助外部库的情况下实现此目的的最佳方法是什么?
是否可以使用某种方式使用我在SensorService中使用的broadcastReceiver?还是最好在片段中创建一个新的Handler,然后以某种方式将其传递给服务,以便它可以将消息发送回片段?
编辑:
我已经根据以下答案之一尝试了以下方法:
在我的片段类中添加了MessageHandler类:
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实例(试图将其放置在多个位置…结果相同):
public static Handler messageHandler = new MessageHandler();
然后使用以下命令从我的片段启动服务:
Intent startService = new Intent(mainActivity, SensorService.class);startService.putExtra("MESSENGER", new Messenger(messageHandler));getContext().startService(startService);
在我的SensorService broadcastReceiver中,创建messageHandler:
Bundle extras = intent.getExtras();messageHandler = (Messenger) extras.get("MESSENGER");
然后,在SensorService onDestroy的最开始显示对话框:
sendMessage("SHOW");
并在同一方法的最后将其关闭:
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(); } }
这样我就可以启动“服务正常”了,但是当我再次按它停止时,我得到了:
java.lang.RuntimeException:无法停止服务com.example.app.SensorService@21124f0:java.lang.NullPointerException:尝试在虚拟设备上调用虚拟方法’voID androID.os.Messenger.send(androID.os.Message)’空对象引用
及其涉及SensorService.send(message)的SensorService的第105行
关于什么可能是错的想法?
解决方法:
活动中:
protected broadcastReceiver mMessageReceiver = new broadcastReceiver() { @OverrIDe public voID onReceive(Context context, final Intent intent) { runOnUiThread(new Runnable() { @OverrIDe public voID run() { if(intent.hasExtra("someExtraMessage")){ doSomething(intent.getStringExtra("someExtraMessage")); } } }); }};@OverrIDepublic voID onCreate(Bundle savedInstanceState, PersistableBundle persistentState) { super.onCreate(savedInstanceState, persistentState); LocalbroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter("message-ID"));}protected voID onDestroy() { super.onDestroy(); LocalbroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);}public voID doSomething(){ //...}
然后从服务的某个地方:
Context context = BamBamApplication.getApplicationContext(); // Can be application or activity context.// BamBamApplicaiton extends Application ;)Intent intent = new Intent("message-ID");intent.putExtra("someExtraMessage", "Some Message :)");LocalbroadcastManager.getInstance(context).sendbroadcast(intent);
实际上,您从一开始就做错了:)所有服务都在主线程上运行,因此在这里您必须更好地启动所有硬处理以异步任务将其移至后台,否则您将卡住应用程序,否则会突然出现意外情况崩溃.
这是您的异步任务示例,该任务在后台解析Json API响应,并按参数键入结果.
class ParseJsonInBackground<T> extends AsyncTask<String, VoID, APIResponseModel<T>> { private ProcessResponse<T> func; private Type inClass; public ParseJsonInBackground(ProcessResponse<T> f, Type inClass){ this.func = f; this.inClass = inClass; } @OverrIDe protected voID onPreExecute() { super.onPreExecute(); } @OverrIDe protected APIResponseModel<T> doInBackground(String... Json) { Gson gson = new Gson(); try { APIResponseModel<T> result = (APIResponseModel<T>) gson.fromJson(Json[0], inClass); return result; }catch(Exception e){ APIResponseModel<T> result = new APIResponseModel<T>(); result.data = null; result.success = false; result.error = new ArrayList<>(); result.error.add(new ErrorModel(0, "Parsing error", "Parsing error")); return result; } } @OverrIDe protected voID onPostExecute(APIResponseModel<T> result) { Utils.hIDeLoadingProgress(mContext); if(result != null && func != null){ if(result.success){ func.onSuccess(result); }else{ func.onError(result); } } }}
并示例如何调用:
new ParseJsonInBackground<T>(responseFunc, inClass).execute(Json.toString());
注意! -不要在处理过程中使用任何视图,因为这会阻塞主线程,使数据库处理处于类似的异步任务中,不要经常写入数据库以进行事务记录.
总结以上是内存溢出为你收集整理的Android在片段和服务之间发送消息全部内容,希望文章能够帮你解决Android在片段和服务之间发送消息所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)