Приложение для приема SMS аварийно завершает работу при поступлении нового текстового сообщения — Android

Я создал активность отправки/получения SMS, и все работает нормально. Часть получения просто загружает все мои тексты из моего приложения для обмена сообщениями Android в файл ListView. Это тоже работает. Проблема в том, что когда приходит новое текстовое сообщение, оно сразу же вылетает. Даже если я нахожусь в другом приложении или на главном экране, я получаю всплывающее окно с сообщением, что мое приложение разбилось, и повторяется сбой каждый раз, когда приходит новое сообщение. Почему происходит сбой и как это исправить? ListView, содержащий сообщения, должен обновляться при поступлении нового сообщения. Я исправил этот код вместе, так что, вероятно, я что-то упустил. Кроме того, просто сосредоточьтесь на классе SMSReceiverSMSListFragment.java) и refreshSmsInbox()SMSMain.java), так как именно в этих местах происходит загрузка в мой ListView. Кроме того, updateList() обновляет весь этот процесс, но только если в моем почтовом ящике уже есть сообщения. Было бы неплохо, если бы обновление действительно могло обновляться в режиме реального времени. Это возможно? Заранее спасибо.

В моем манифесте приведены мои соответствующие разрешения:

<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />

<receiver android:name=".SMSReceiver"
          android:exported="true"> 
      <intent-filter android:priority="999"> 
          <action android:name="android.provider.Telephony.SMS_RECEIVED" /> 
      </intent-filter> 
</receiver>

SMSMain.java

package org.azurespot.practiceapp.sms;

import java.util.ArrayList;

import org.azurespot.practiceapp.R;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.speech.RecognizerIntent;
import android.telephony.SmsManager;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;


public class SMSMain extends Activity {

    protected EditText etPhoneNumber;
    protected static EditText etMessage;
    protected View dialogView;
    private static final int CONTACT_PICKER_RESULT = 1001;
    AlertDialog dialog;
    protected static final int RESULT_SPEECH = 1;
    private static SMSMain smsMain;
    ArrayList<String> smsMessagesList = new ArrayList<String>();
    ListView smsListView;
    ArrayAdapter<String> arrayAdapter;

    public static SMSMain instance() {
        return smsMain;
    }

    @Override
    public void onStart() {
        super.onStart();
        smsMain = this;
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_smsmain);

        // initialize ListView for displaying SMS messages
        smsListView = (ListView) findViewById(R.id.messages_list);
        arrayAdapter = new ArrayAdapter<String>(this, 
                android.R.layout.simple_list_item_1, smsMessagesList);
        smsListView.setAdapter(arrayAdapter);
        // long click on ListView item will delete message
        smsListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view,
                    int pos, long arg3) {

                //removes item at position you long-click on
                smsMessagesList.remove(pos);//where arg2 is position of item you click
                arrayAdapter.notifyDataSetChanged();  

                 return true;
            }
        });


    } // end onCreate


    public void readSMS(View v){

        refreshSmsInbox();
    }

    // adds text messages to the ListView via the adapter
    public void refreshSmsInbox() {
        ContentResolver contentResolver = getContentResolver();
        Cursor smsInboxCursor = contentResolver.query(Uri.parse
                    ("content://sms/inbox"), null, null, null, null);
        int indexBody = smsInboxCursor.getColumnIndex("body");
        int indexAddress = smsInboxCursor.getColumnIndex("address");
        if (indexBody < 0 || !smsInboxCursor.moveToFirst()) return;
        arrayAdapter.clear();
        do {
            String str = "SMS From: " + smsInboxCursor.getString(indexAddress) +
                    "\n" + smsInboxCursor.getString(indexBody) + "\n";
            arrayAdapter.add(str);
        } while (smsInboxCursor.moveToNext());
    }

    public void updateList(final String smsMessage) {
        arrayAdapter.insert(smsMessage, 0);
        arrayAdapter.notifyDataSetChanged();
    }


    public void composeSMS(View v){

        // build your dialog box
        AlertDialog.Builder builder = new AlertDialog.Builder(SMSMain.this);
        // Get the layout inflater & inflate the box
        final LayoutInflater inflater = SMSMain.this.getLayoutInflater();
        dialogView = inflater.inflate(R.layout.dialog_compose_sms, null);
        builder.setView(dialogView);
     // get phone number and message IDs
        etPhoneNumber = (EditText)dialogView.findViewById(R.id.sms_phone);
        etMessage = (EditText) dialogView.findViewById(R.id.sms_message);
        builder.setTitle("Send an SMS");
        builder.setPositiveButton("Send", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {

                // Turns the entered phone number and message into String
                String phoneNumber = etPhoneNumber.getText().toString().trim();
                String message = etMessage.getText().toString().trim(); 
                // error check to make sure there is input
                if (phoneNumber.length() > 0 && message.length() > 0) 
                    // sends the SMS
                    sendSMS(phoneNumber, message);                
                else
                    Toast.makeText(getBaseContext(), 
                        "Please enter a phone number and message.", 
                        Toast.LENGTH_SHORT).show();
            }
        });
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                // User cancelled the dialog
                dialog.cancel();
            }
        });
        // put all your builders in one create command
        dialog = builder.create(); 
        // show the dialog box
        dialog.show();

    }

    // clicked the microphone button to start speech to text
    public void speechToText(View v) {

        Intent intent = new Intent(
                            RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, "en-US");

        try {
            startActivityForResult(intent, RESULT_SPEECH);
            etMessage.setText("");
        } catch (ActivityNotFoundException a) {
            Toast t = Toast.makeText(getApplicationContext(),
                    "Ooopps! Your device doesn't support Speech to Text",
                    Toast.LENGTH_SHORT);
            t.show();
        }

    }

    // method for sending the SMS
    private void sendSMS(String phoneNumber, String message)
        {        
            PendingIntent pi = PendingIntent.getActivity(this, 0,
                                    new Intent(this, SMSMain.class), 0);                
            SmsManager sms = SmsManager.getDefault();
            sms.sendTextMessage(phoneNumber, null, message, pi, null);        
        }   

    public void addContact(View v) {

        // add a contact. using Android's ContactPicker
        Intent i = new Intent(Intent.ACTION_PICK,
                ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
        startActivityForResult(i, CONTACT_PICKER_RESULT);

    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == RESULT_SPEECH){
            // speech to text result
            switch (requestCode) {
                case RESULT_SPEECH: {
                    if (resultCode == RESULT_OK && null != data) {

                        ArrayList<String> text = data
                                .getStringArrayListExtra
                                (RecognizerIntent.EXTRA_RESULTS);

                        etMessage.setText(text.get(0));
                    }
                    break;
                }
            }
        } // end if

        // picks a contact, puts into EditText
        if (resultCode == RESULT_OK) {
            switch (requestCode) {
            case CONTACT_PICKER_RESULT:
                Cursor cursor = null;
                String phone = "";
                try{
                    Uri result = data.getData();
                    // get the contact id from the URI
                    String id = result.getLastPathSegment();

                    // query for phone number
                    cursor = getContentResolver().query(
                            ContactsContract.CommonDataKinds.Phone.CONTENT_URI, 
                            null,
                            ContactsContract.CommonDataKinds.Phone._ID + "=?",
                            new String[]{id}, null);

                    int phoneIdx = cursor.getColumnIndex(Phone.DATA);

                    if (cursor.moveToFirst()) {
                        phone = cursor.getString(phoneIdx);
                    } 
                 } catch (Exception e) {
                        System.out.println("Getting contact failed.");
                 } finally {

                    etPhoneNumber.setText(phone, TextView.BufferType.EDITABLE);

                    if (phone.length() == 0) {
                            Toast.makeText(this, "No phone number found for contact.",
                                                                Toast.LENGTH_LONG).show();
                    }
                     if (cursor != null){
                            cursor.close();
                     }

                 } // end finally
                 break;  
            }
        }     
    }
}

SMSListFragment.java

package org.azurespot.practiceapp.sms;

import org.azurespot.practiceapp.R;

import android.app.ListFragment;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.SmsMessage;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

public class SMSListFragment extends ListFragment{

    public static View viewList;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {

      // Defines the XML file for the fragment
      viewList = inflater.inflate(R.layout.sms_messages_fragment, 
                                                        container, false);

      return viewList;
    }

    public class SMSReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {

            //---get the SMS message passed in---
            Bundle bundle = intent.getExtras();        
            SmsMessage[] messages = null;
            String msgString = ""; 

            if (bundle != null)
            {
                //---retrieve the SMS message received---
                Object[] pdus = (Object[]) bundle.get("pdus");
                messages = new SmsMessage[pdus.length];            
                for (int i = 0; i < messages.length; i++){
                    messages[i] = SmsMessage.createFromPdu((byte[])pdus[i]);                
                    msgString += "SMS From: " + messages[i].getOriginatingAddress();                     
                    msgString += " :";
                    msgString += messages[i].getMessageBody().toString();
                    msgString += "\n"; 



                }

                //---display the new SMS message---
                SMSMain smsMain = SMSMain.instance();
                smsMain.updateList(msgString);
            }
        }

    }

}

Логкат

01-01 18:24:09.444: E/AndroidRuntime(13141): FATAL EXCEPTION: main
01-01 18:24:09.444: E/AndroidRuntime(13141): Process: org.azurespot.practiceapp, PID: 13141
01-01 18:24:09.444: E/AndroidRuntime(13141): java.lang.RuntimeException: Unable to instantiate receiver org.azurespot.practiceapp.SMSReceiver: java.lang.ClassNotFoundException: Didn't find class "org.azurespot.practiceapp.SMSReceiver" on path: DexPathList[[zip file "/data/app/org.azurespot.practiceapp-2.apk"],nativeLibraryDirectories=[/data/app-lib/org.azurespot.practiceapp-2, /vendor/lib, /system/lib]]
01-01 18:24:09.444: E/AndroidRuntime(13141):    at android.app.ActivityThread.handleReceiver(ActivityThread.java:2608)
01-01 18:24:09.444: E/AndroidRuntime(13141):    at android.app.ActivityThread.access$1800(ActivityThread.java:173)
01-01 18:24:09.444: E/AndroidRuntime(13141):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1379)
01-01 18:24:09.444: E/AndroidRuntime(13141):    at android.os.Handler.dispatchMessage(Handler.java:102)
01-01 18:24:09.444: E/AndroidRuntime(13141):    at android.os.Looper.loop(Looper.java:136)
01-01 18:24:09.444: E/AndroidRuntime(13141):    at android.app.ActivityThread.main(ActivityThread.java:5579)
01-01 18:24:09.444: E/AndroidRuntime(13141):    at java.lang.reflect.Method.invokeNative(Native Method)
01-01 18:24:09.444: E/AndroidRuntime(13141):    at java.lang.reflect.Method.invoke(Method.java:515)
01-01 18:24:09.444: E/AndroidRuntime(13141):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268)
01-01 18:24:09.444: E/AndroidRuntime(13141):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084)
01-01 18:24:09.444: E/AndroidRuntime(13141):    at dalvik.system.NativeStart.main(Native Method)
01-01 18:24:09.444: E/AndroidRuntime(13141): Caused by: java.lang.ClassNotFoundException: Didn't find class "org.azurespot.practiceapp.SMSReceiver" on path: DexPathList[[zip file "/data/app/org.azurespot.practiceapp-2.apk"],nativeLibraryDirectories=[/data/app-lib/org.azurespot.practiceapp-2, /vendor/lib, /system/lib]]
01-01 18:24:09.444: E/AndroidRuntime(13141):    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:67)
01-01 18:24:09.444: E/AndroidRuntime(13141):    at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
01-01 18:24:09.444: E/AndroidRuntime(13141):    at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
01-01 18:24:09.444: E/AndroidRuntime(13141):    at android.app.ActivityThread.handleReceiver(ActivityThread.java:2603)
01-01 18:24:09.444: E/AndroidRuntime(13141):    ... 10 more

person Azurespot    schedule 02.01.2015    source источник


Ответы (1)


Когда вы объявляете свой приемник в манифесте:

<receiver android:name=".SMSReceiver"
      android:exported="true"> 
  <intent-filter android:priority="999"> 
      <action android:name="android.provider.Telephony.SMS_RECEIVED" /> 
  </intent-filter> 
</receiver>

. в android:name=".SMSReceiver" будет заменено на имя вашего пакета. Таким образом, то, что вы написали в манифесте, эквивалентно

<receiver android:name="org.azurespot.practiceapp.SMSReceiver"
      android:exported="true"> 
  <intent-filter android:priority="999"> 
      <action android:name="android.provider.Telephony.SMS_RECEIVED" /> 
  </intent-filter> 
</receiver>

Обратите внимание, что этот путь не соответствует пути класса SMSReceiver, который вы определяете внутри своего SMSListFragment, на что указывает ошибка logcat.

Вместо этого вам, вероятно, следует использовать android:name=".sms.SMSListFragment.SMSReceiver" в манифесте. Вложенный класс SMSReceiver также может потребоваться объявить статическим, чтобы к нему можно было получить доступ таким образом.

person stkent    schedule 02.01.2015
comment
Спасибо stkent, я понятия не имел, что это необходимо. Хотя я сделал это изменение, и оно все еще дает мне эту ошибку с новым именем. Я тоже сделал SMSReceiver статическим. java.lang.RuntimeException: Unable to instantiate receiver org.azurespot.practiceapp.sms.SMSListFragment.SMSReceiver: java.lang.ClassNotFoundException: Didn't find class "org.azurespot.practiceapp.sms.SMSListFragment.SMSReceiver" ... - person Azurespot; 02.01.2015
comment
Ну, это странно, потому что org.azurespot.practiceapp.sms.SMSListFragment.SMSReceiver теперь выглядит как правильный путь. Ваш SMSListFragment все еще в упаковке sms, верно? - person stkent; 02.01.2015
comment
С другой стороны, мой код обновления находится в SMSMain... хотя список обновляется очень хорошо, но это просто обновление в реальном времени, с которым он не может справиться, а затем происходит сбой. Не знаю связаны ли они, но SMSMain в манифесте нет, хотя это и не ресивер, так что может и не надо. - person Azurespot; 02.01.2015
comment
Да, SMSListFragment.java находится в упаковке .sms. :/ - person Azurespot; 02.01.2015
comment
SMSMain должен быть объявлен как действие в манифесте. Однако logcat по-прежнему показывает ClassNotFoundException, поэтому проблема, вероятно, связана с SMSReceiver. Боюсь, других идей в голову не приходит. - person stkent; 02.01.2015
comment
Знаешь, я думаю, это может быть как-то связано с тем, что refreshSmsInbox() заключен в кнопку. Так что это работает, когда я сначала нажимаю кнопку, то есть Read SMS, но затем, когда приходит текст, обработчик не может его получить, так как он работает только на кнопке. Как вы думаете, это может быть? Не уверен, куда еще его поместить, так как я получил код откуда-то еще. Они просто помещают его в свою основную деятельность, а не в кнопку. Может быть, мне стоит попробовать onCreate в действии? - person Azurespot; 02.01.2015
comment
Основываясь на сообщенном исключении, я все еще думаю, что более вероятно, что объявление/путь манифеста каким-то образом запутаны. Я оставлю ответ пока в качестве ссылки для других. - person stkent; 02.01.2015
comment
Это странно, потому что весь код в классе приемника работает нормально, запрашивает мой почтовый ящик и показывает текстовые сообщения в виде списка, как и должно быть, поэтому система находит приемник в порядке. Только когда поступает сигнал от SMS_RECEIVED, класс каким-то образом теряется. Но понятия не имею, почему. - person Azurespot; 02.01.2015
comment
В итоге я зарегистрировал свой приемник программно в onResume() и onPause() из SMSMain, и хотя он не обновляется автоматически (мне нужно снова нажать кнопку Read SMS), он будет обновляться до последнего текстового сообщения и публиковать его в моем ListView. Вот я и думаю, мне этого достаточно! ;) По крайней мере больше ошибок нет. Спасибо за вашу помощь. :) - person Azurespot; 02.01.2015