0

I am creating an app which sends current location, on receiving a particular message say "Trace", using broadcast receiver and service. I have almost completed the app but I am facing two problems. First issue MessageQueue Exception

5-25 11:07:15.346 2459-2493/com.example.prakaash.knowyourlocation W/MessageQueue: Handler (android.location.LocationManager$ListenerTransport$1) {7743340} sending message to a Handler on a dead thread
java.lang.IllegalStateException: Handler (android.location.LocationManager$ListenerTransport$1) {7743340} sending message to a Handler on a dead thread
    at android.os.MessageQueue.enqueueMessage(MessageQueue.java:545)
    at android.os.Handler.enqueueMessage(Handler.java:662)
    at android.os.Handler.sendMessageAtTime(Handler.java:631)
    at android.os.Handler.sendMessageDelayed(Handler.java:601)
    at android.os.Handler.sendMessage(Handler.java:538)
    at android.location.LocationManager$ListenerTransport.onLocationChanged(LocationManager.java:255)
    at android.location.ILocationListener$Stub.onTransact(ILocationListener.java:58)
    at android.os.Binder.execTransact(Binder.java:697)

I just want to have single reply message with current location on recieving a broadcast message. Second issue is I am not able to send phone number from MainActivity to Service. I tried the solution Passing value to Service but I am getting fatal exception

java.lang.RuntimeException: Failure delivering result ResultInfo{who=@android:requestPermissions:, request=100, result=-1, data=Intent { act=android.content.pm.action.REQUEST_PERMISSIONS (has extras) }} to activity {com.example.prakaash.myapplication/com.example.prakaash.myapplication.MainActivity}: java.lang.IllegalArgumentException:        Service Intent must be explicit: Intent {       act=com.example.prakaash.myapplication.ServiceClass (has extras) }
at android.app.ActivityThread.deliverResults(ActivityThread.java:4268)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4312)
at android.app.ActivityThread.-wrap19(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1644)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.example.prakaash.myapplication.ServiceClass (has extras) }
at android.app.ContextImpl.validateServiceIntent(ContextImpl.java:1464)
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1505)
at android.app.ContextImpl.startService(ContextImpl.java:1477)
at android.content.ContextWrapper.startService(ContextWrapper.java:650)
at com.example.prakaash.myapplication.MainActivity.onRequestPermissionsResult(MainActivity.java:42)
at android.app.Activity.dispatchRequestPermissionsResult(Activity.java:7429)
at android.app.Activity.dispatchActivityResult(Activity.java:7280)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4264)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4312) 
at android.app.ActivityThread.-wrap19(Unknown Source:0) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1644) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
at android.os.Looper.loop(Looper.java:164) 
at android.app.ActivityThread.main(ActivityThread.java:6494) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 

MainActivity

String phone="9940262305";
RecieverClass recieverClass;

IntentFilter intentFilter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    runtime_permissions();
    recieverClass=new RecieverClass();

    intentFilter= new IntentFilter("android.provider.Telephony.SMS_RECEIVED");

}

private boolean runtime_permissions() {
    if(Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){

        requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.READ_PHONE_STATE, Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.SEND_SMS,Manifest.permission.RECEIVE_SMS},100);

        return true;
    }
    return false;
}


@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if(requestCode == 100){
        if( grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED){

            Intent service =new Intent(this,ServiceClass.class);
            service.putExtra("UserID", phone);
            startService(service);
        }
        else
        {
            runtime_permissions();
        }
    }
}
@Override
protected void onResume() {
    super.onResume();
    registerReceiver(recieverClass,intentFilter);
}

BroadcastReciever

   int count=1;
String message;
@Override
public void onReceive(Context context, Intent intent) {
    final Bundle bundle = intent.getExtras();

    try {

        if (bundle != null)
        {

            final Object[] pdusObj = (Object[]) bundle.get("pdus");

            for (int i = 0; i < pdusObj.length; i++)
            {

                SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
                String phoneNumber = currentMessage.getDisplayOriginatingAddress();

                String senderNum = phoneNumber;
                message = currentMessage.getDisplayMessageBody();
             }
             // end for loop
            if(message.contains("Trace"))
            {
                Toast.makeText(context, "Working", Toast.LENGTH_SHORT).show();
                Intent service =new Intent(context,ServiceClass.class);
                context.startService(service);
            }
            else if(message.isEmpty())
            {
                Toast.makeText(context, "SMS is null", Toast.LENGTH_SHORT).show();
            }
        } // bundle is null

    }
    catch (Exception e)

    {
        Log.e("SmsReceiver", "Exception smsReceiver" +e);
    }
}

ServiceClass(Getting current location service and sending SMS)

LocationListener listener;
LocationManager locationManager;
int count=1;
@Nullable
@Override
public IBinder onBind(Intent intent) {

    return null;
}
@SuppressLint("MissingPermission")
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(this, "Service Started", Toast.LENGTH_SHORT).show();
    final String number = intent.getStringExtra("UserID");
    listener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {

            String locat=String.valueOf(location.getLatitude() + "," + location.getLongitude());
            if(count==1 )
            {
                SmsManager sms = SmsManager.getDefault();
                if(number !=null)
                {
                    sms.sendTextMessage(number, null, "Know where they are " + "https://www.google.com/maps/?q=" + locat, null, null);
                    count++;
                }
                else
                {

                    Toast.makeText(ServiceClass.this, "number is null "+count, Toast.LENGTH_SHORT).show();
                }
            }
        }

        @Override
        public void onStatusChanged(String s, int i, Bundle bundle) {

        }

        @Override
        public void onProviderEnabled(String s) {

        }

        @Override
        public void onProviderDisabled(String s) {
            Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(i);
        }
    };

    locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
    //noinspection MissingPermission
    locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER,listener,null);
    return Service.START_STICKY;
}
  • 1
    1.) Use a regular `Service` instead of an `IntentService`. An `IntentService` stops itself after it's done handling its tasks; i.e., after `onHandleIntent()` finishes. 2.) You're not constructing the `Intent` for your `Service` correctly in `onRequestPermissionsResult()`. Do it like you are in the `BroadcastReceiver`. – Mike M. May 26 '18 at 06:33
  • I have updated my code. When I run the service, Toast message is showing number is null and it has appeared about 5 times for a single broadcast message. – Prakaash M May 26 '18 at 07:34
  • "Toast message is showing number is null" – You never attach the `"UserID"` extra in the `BroadcastReceiver`. "it has appeared about 5 times for a single broadcast message" – I'm not sure what you mean. You're requesting updates every 3 seconds. Also, every time `onStartCommand()` runs, you create and register a new `LocationListener`, and never unregister any of them. Probably want to do that once, in `onCreate()`. Also, in the Receiver, you've got the `startService()` call inside the `for` loop, which you don't want. A multipart message will call that once for each part of a long message. – Mike M. May 26 '18 at 07:49
  • I need a single reply message with current location, on receiving a message which contains **Trace**. I get you that I am updating locationManager every 3 seconds, but i don't know how to get location only once without updating every 3 seconds. Previously I tried unregistering reciever on onPause in mainActivity but still Service is called multiple times for a single broadcast Messge. – Prakaash M May 26 '18 at 08:53
  • Did you try one of the `requestSingleUpdate()` methods? Beyond that, I mentioned two other things in your code that could cause extraneous `Toast`s; creating and registering multiple listeners, and starting the `Service` for each part of a multipart SMS message. If by "Service is called multiple times", you mean that you're getting the `"Service Started"` `Toast` multiple times for a single SMS message, then I would suspect the latter (a combination of both, actually). You need to move the `startService()` call in the Receiver out of the loop. And register the listener in the `Service` once. – Mike M. May 26 '18 at 09:07
  • Thanks mike. requestSingleUpdate() did the trick. Now I am getting only one single reply message when I send a message "Trace". But one problem still persists. I can't value pass the phone number to ther ServiceClass. Kindly help me with it. – Prakaash M May 27 '18 at 16:47
  • In the `BroadcastReceiver`, you forgot to attach the number to the `Service` `Intent`; e.g., `service.putExtra("UserID", phoneNumber);`. Also, to get the complete message for a multipart message, you need to concatenate the message bodies in the `for` loop; e.g., `message += currentMessage.getDisplayMessageBody();`, notice the `+=`. – Mike M. May 27 '18 at 17:15
  • Actually I have declared String phone= "9940262305". In the global declaration and I have that service.putExtra("User_Id", phone); Is their anything to change to make it work? thanks in advance – Prakaash M May 27 '18 at 18:20
  • You did that in `MainActivity`, not in the `BroadcastReceiver`. The `BroadcastReceiver` is where the incoming SMS message goes. I don't really know why you're starting that `Service` from `MainActivity`. It won't be involved when a message comes in, and starting the `Service` there does not seem like what you want. Do you really want to get a location and send a message right when the user grants the permissions, without having received the "Trace" message? – Mike M. May 27 '18 at 18:25
  • Phone number is not fixed, it will change from user to user. I will create a EditText to get phone number from user and send that value to the SMSManager in ServiceClass. I don't how to pass that value to the Service class. kindly help me – Prakaash M May 28 '18 at 05:23
  • You mean that no matter which number the "Trace" message comes from, you're sending the message to one the user enters? I guess that's not what I thought you were doing. If you want a number entered in `MainActivity` to be available later in your `Service`, then save it to storage somehow, and read it when needed. `SharedPreferences` seems appropriate here. – Mike M. May 28 '18 at 08:45
  • Yes, you are right. No matters who sends the message "Trace", app should send the current location to the phone number which user has already saved in the app. Thanks a lot shared preference and requestSingleUpdate helped me a lot and did the trick. Thank you so much – Prakaash M May 28 '18 at 12:38

0 Answers0