2011-09-20 17 views
12

Tengo la siguiente implementación ContentObserver para recibir y escribir SMS, pero se llama varias veces.¿Por qué se llama al ContentObserver varias veces?

Código:

public class SMSObserverActivity extends Activity { 
    protected MyContentObserver observer = null; 

    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState){ 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
     String url = "content://mms-sms/"; 
     Uri uri = Uri.parse(url); 
     observer = new MyContentObserver(new Handler()); 
     getContentResolver().registerContentObserver(uri, true, observer); 
    } 

    @Override 
    protected void onDestroy(){ 
     super.onDestroy(); 

     getContentResolver().unregisterContentObserver(observer); 
    } 

    class MyContentObserver extends ContentObserver { 
     ContentValues values = new ContentValues(); 
     Handler handler; 

     public MyContentObserver(Handler handler){ 
      super(handler); 
      this.handler = handler; 
     } 

     @Override 
     public boolean deliverSelfNotifications(){ 
      return false; 
     } 


     @Override 
     public void onChange(boolean arg0){ 
      super.onChange(arg0); 

      Log.v("SMS", "Notification on SMS observer"); 
      values.put("status", 5); 
      Message msg = new Message(); 
      msg.obj = "xxxxxxxxxx"; 
      int threadId = 0; 
      handler.sendMessage(msg); 

      Uri uriSMSURI = Uri.parse("content://sms/"); 
      Cursor cur = 
        getContentResolver().query(uriSMSURI, null, null, null, 
          null); 
      cur.moveToNext(); 
      Log.e("sms", cur.getString(4)+" "+cur.getString(11)); 
     } 
    } 
} 

Manifiesto:

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    package="com.test" 
    android:versionCode="1" 
    android:versionName="1.0"> 
    <uses-sdk android:minSdkVersion="8" /> 
    <uses-permission android:name="android.permission.READ_SMS"></uses-permission> 
    <uses-permission android:name="android.permission.WRITE_SMS"></uses-permission> 

    <application android:icon="@drawable/icon" android:label="@string/app_name"> 
     <activity android:name=".SMSObserverActivity" 
        android:label="@string/app_name"> 
      <intent-filter> 
       <action android:name="android.intent.action.MAIN" /> 
       <category android:name="android.intent.category.LAUNCHER" /> 
      </intent-filter> 
     </activity> 

    </application> 
</manifest> 

Por qué se llama varias veces?

EDIT:
Hubo la idea de que el problema se debe a la falta de unregisterContentObserver, pero no hace ninguna diferencia.

+0

¿alguna vez resolver esto? –

+0

Todavía no, todavía no tengo tiempo para eso, pero lea los comentarios sobre la respuesta y pruébelo con un servicio. Quizás lo pondrás en funcionamiento. ;) – CSchulz

+0

El problema es el mismo si se ejecuta en un servicio me temo –

Respuesta

11

Esto ocurre porque está registrando su observador de contenido para toda la base de datos de SMS. Por lo tanto, su observador de contenido recibe una notificación cada vez que se actualiza una entrada de tabla en la base de datos.

En este caso, cuando se envía un mensaje, por ejemplo, alrededor de 7 tablas se actualizan las entradas para que su observador de contenido reciba notificaciones 7 veces.

Dado que solo me interesa si se envía un mensaje, he cambiado para observar solo los mensajes en cola y esto significa que mi observador siempre recibe notificaciones exactamente tres veces, así que implementé el código para protegerlo.

Es probable que existan otros problemas, como mensajes de múltiples destinatarios o mensajes de varias partes, pero los conceptos básicos funcionan hasta el momento.

+0

Bien, pensé que el observador solo se llama una vez para todas las actualizaciones relacionadas por objeto. Creo que hay una identificación única para ver, qué notificaciones pertenecen a una actualización, ¿verdad? – CSchulz

0

Si usted quiere tener su observador activa sólo cuando la actividad está en estado activo, le aconsejo que se mueva registerContentObserver() y unregisterContentObserver() a métodos onResume() y onPause() respectivamente. onDestroy() no se puede llamar si su aplicación se cierra, pero se garantiza que es onPause().

+0

El observador debe estar siempre activo, cuando la * Actividad * se está ejecutando. Es solo una prueba de contenido de observación. – CSchulz

+0

La actividad se ejecuta entre 'onResume()' y 'onPause()', en el otro momento está en segundo plano o no está visible. Incluso puede acabar sin llamar a 'onDestroy()', y cuando vuelva a crear la aplicación en 'onCreate()', registrará un observador por segunda vez. – Malcolm

+0

¿Por qué se registrará dos veces? Tengo una declaración de anulación de registro. – CSchulz

2

Para evitar el envío de SMS múltiples por el observador contenido de probar este

public class SmsObserver extends ContentObserver { 
    SharedPreferences trackMeData; 
    private Context context; 
    private static int initialPos; 
    private static final String TAG = "SMSContentObserver"; 
    private static final Uri uriSMS = Uri.parse("content://sms/sent"); 

    public SmsObserver(Handler handler, Context ctx) { 
     super(handler); 
     context = ctx; 
     trackMeData = context.getSharedPreferences("LockedSIM", 0); 
     initialPos = getLastMsgId(); 

    } 

    @Override 
    public void onChange(boolean selfChange) { 
     super.onChange(selfChange); 
     queryLastSentSMS(); 
    } 

    public int getLastMsgId() { 

     Cursor cur = context.getContentResolver().query(uriSMS, null, null, null, null); 
     cur.moveToFirst(); 
     int lastMsgId = cur.getInt(cur.getColumnIndex("_id")); 
     Log.i(TAG, "Last sent message id: " + String.valueOf(lastMsgId)); 
     return lastMsgId; 
    } 

    protected void queryLastSentSMS() { 

     new Thread(new Runnable() { 

      @Override 
      public void run() { 
       Cursor cur = 
        context.getContentResolver().query(uriSMS, null, null, null, null); 

       if (cur.moveToNext()) { 



        try { 

         String body = cur.getString(cur.getColumnIndex("body")); 

         if (initialPos != getLastMsgId()) { 

          String receiver = cur.getString(cur.getColumnIndex("address")); 
          Log.i("account", myDeviceId); 
          Log.i("date", day + "-" + month + "-" + year + " " 
           + hour + ":" + minute + ":" + seconde); 
          Log.i("sender", myTelephoneNumber); 
          Log.i("receiver", receiver); 


          // Then, set initialPos to the current position. 
          initialPos = getLastMsgId(); 

          sendsmstoph(receiver, body); 
         } 
        } catch (Exception e) { 
         // Treat exception here 
        } 
       } 
       cur.close(); 
      } 
     }).start(); 

    } 
+0

Llamando initialPos = getLastMsgId(); from contructor throws null pointer exception –

+0

He notado que esto es bueno pero no siempre a prueba de tontos. Por lo tanto, creo que el uso de las Preferencias Compartidas puede resolver este problema. Así es como lo he hecho: si (sharedPreferences.getString ("lastMessageTime", "0") .equals (String.valueOf (timeInMillis)) || sharedPreferences.getString ("lastMessageNumber", "0")! .equals (número)) – Usman

Cuestiones relacionadas