2015-05-04 5 views
1

Я пытаюсь сделать простое приложение для веб-представления, основанное на push-уведомлениях. Мое приложение регистрируется в фоновом режиме и делает все, но когда я отправляю push, и мой телефон должен показывать push-уведомление, приложение отключается. Я попытался отладить и установить точку останова в методе onReceive(), и я также установил точки останова, но приложение просто падает и не останавливается перед тем, как это сделать. Я новичок в андроид-студии, и я вообще не понимаю сообщения об ошибках, поэтому, если кто-то может помочь мне найти ошибку, мы будем очень благодарны.сбой приложения каждый раз, когда он получает push-уведомление

LogCat:

05-04 14:41:46.349 30089-30089/com.example.alfredo.guiasandroid E/dalvikvm﹕ Could not find class 'android.app.Notification$Builder', referenced from method com.google.android.gms.common.GooglePlayServicesUtil.zza 
05-04 14:41:46.349 30089-30089/com.example.alfredo.guiasandroid E/dalvikvm﹕ Could not find class 'android.app.AppOpsManager', referenced from method com.google.android.gms.common.GooglePlayServicesUtil.zza 
05-04 14:43:34.879 10207-10207/? E/Launcher﹕ setWindowOpaque() 
05-04 14:43:34.909 10207-10207/? E/Launcher﹕ MTP-LAUNCHER: media scanning not yet finished. 
05-04 14:43:34.959 10207-10207/? E/Launcher﹕ setWindowOpaque() 
05-04 14:43:44.879 30089-30089/com.example.alfredo.guiasandroid E/AndroidRuntime﹕ FATAL EXCEPTION: main 
    java.lang.RuntimeException: Unable to instantiate receiver com.example.alfredo.guiasandroid.MainActivity$GcmBroadcastReceiver: java.lang.InstantiationException: com.example.alfredo.guiasandroid.MainActivity$GcmBroadcastReceiver 
      at android.app.ActivityThread.handleReceiver(ActivityThread.java:1777) 
      at android.app.ActivityThread.access$2400(ActivityThread.java:117) 
      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:985) 
      at android.os.Handler.dispatchMessage(Handler.java:99) 
      at android.os.Looper.loop(Looper.java:130) 
      at android.app.ActivityThread.main(ActivityThread.java:3687) 
      at java.lang.reflect.Method.invokeNative(Native Method) 
      at java.lang.reflect.Method.invoke(Method.java:507) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625) 
      at dalvik.system.NativeStart.main(Native Method) 
    Caused by: java.lang.InstantiationException: com.example.alfredo.guiasandroid.MainActivity$GcmBroadcastReceiver 
      at java.lang.Class.newInstanceImpl(Native Method) 
      at java.lang.Class.newInstance(Class.java:1409) 
      at android.app.ActivityThread.handleReceiver(ActivityThread.java:1768) 
            at android.app.ActivityThread.access$2400(ActivityThread.java:117) 
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:985) 
            at android.os.Handler.dispatchMessage(Handler.java:99) 
            at android.os.Looper.loop(Looper.java:130) 
            at android.app.ActivityThread.main(ActivityThread.java:3687) 
            at java.lang.reflect.Method.invokeNative(Native Method) 
            at java.lang.reflect.Method.invoke(Method.java:507) 
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867) 
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625) 
            at dalvik.system.NativeStart.main(Native Method) 
05-04 14:43:44.979 10104-10118/? E/﹕ Dumpstate > /data/log/dumpstate_app_error 
05-04 14:43:50.839 10207-10207/? E/Launcher﹕ setWindowOpaque() 

MainFest:

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    package="com.example.alfredo.guiasandroid" android:versionCode="1" android:versionName="1.0"> 
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="22"/> 


<uses-permission android:name="android.permission.INTERNET" /> 
<uses-permission android:name="android.permission.GET_ACCOUNTS" /> 
<uses-permission android:name="android.permission.WAKE_LOCK" /> 

    <permission android:name="com.example.alfredo.guiasandroid.permission.C2D_MESSAGE" 
     android:protectionLevel="signature"/> 
    <uses-permission android:name="com.example.alfredo.guiasandroid.permission.C2D_MESSAGE"/> 

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> 


<application 
android:icon="@mipmap/ic_launcher" 
android:label="@string/app_name"> 
<!-- Required for applications which use Google Play Services. --> 
<!-- <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> --> 
<meta-data android:name="com.google.android.gms.version" 
    android:value="@integer/google_play_services_version" /> 
<activity 
    android:name=".MainActivity" 
    android:label="@string/app_name" 
    android:configChanges="orientation|keyboardHidden|screenSize" 
    android:launchMode="singleTop"> 
    <intent-filter> 
     <action android:name="android.intent.action.MAIN" /> 

     <category android:name="android.intent.category.LAUNCHER" /> 
    </intent-filter> 
</activity> 
<receiver 
    android:name=".MainActivity$GcmBroadcastReceiver" 
    android:permission="com.google.android.c2dm.permission.SEND" > 
    <!--android:name=".MainActivity$GcmBroadcastReceiver" --> 

    <intent-filter> 
     <!-- 
     <action android:name="com.google.android.c2dm.intent.RECEIVE" /> 
     <action android:name="android.intent.action.MAIN" /> 
     <category android:name="android.intent.category.LAUNCHER" /> 
     <category android:name="com.example.alfredo.webapp" /> 
     --> 
     <action android:name="com.google.android.c2dm.intent.RECEIVE" /> 
     <category android:name="com.example.alfredo.guiasandroid" /> 
    </intent-filter> 
</receiver> 
<!-- 
<service android:name=".MainActivity$GcmIntentService" /> 
--> 
    <service android:name=".MainActivity$GcmIntentService" /> 
</application> 
</manifest> 
<!-- 

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    package="com.example.alfredo.guiasandroid" > 

    <application 
     android:allowBackup="true" 
     android:icon="@mipmap/ic_launcher" 
     android:label="@string/app_name" 
     android:theme="@style/AppTheme" > 
     <activity 
      android:name=".MainActivity" 
      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> 
--> 

build.gradle:

apply plugin: 'com.android.application' 

android { 
    compileSdkVersion 22 
    buildToolsVersion "22.0.1" 

    defaultConfig { 
     applicationId "com.example.alfredo.guiasandroid" 
     minSdkVersion 10 
     targetSdkVersion 22 
     versionCode 1 
     versionName "1.0" 
    } 
    buildTypes { 
     release { 
      minifyEnabled false 
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 
     } 
    } 
} 

dependencies { 
    compile fileTree(dir: 'libs', include: ['*.jar']) 
    compile 'com.android.support:appcompat-v7:22.1.1' 
} 
dependencies { 
    compile fileTree(dir: 'libs', include: ['*.jar']) 
    compile 'com.android.support:appcompat-v7:22.0.0' 
    compile 'com.google.android.gms:play-services:7.0.0' 
} 

mainActivity.java:

package com.example.alfredo.guiasandroid; 

import android.os.Bundle; 
import android.view.MenuItem; 
import android.support.v4.app.NotificationCompat; 
import android.app.Activity; 
import android.app.IntentService; 
import android.app.NotificationManager; 
import android.app.PendingIntent; 
import android.content.ComponentName; 
import android.content.Context; 
import android.content.Intent; 
import android.content.SharedPreferences; 
import android.content.pm.PackageInfo; 
import android.content.pm.PackageManager; 
import android.os.AsyncTask; 
import android.os.SystemClock; 
import android.support.v4.content.WakefulBroadcastReceiver; 
import android.util.Log; 
import android.view.KeyEvent; 
import android.webkit.WebSettings; 
import android.webkit.WebView; 
import android.webkit.WebViewClient; 
import android.widget.TextView; 

import com.google.android.gms.common.ConnectionResult; 
import com.google.android.gms.common.GooglePlayServicesUtil; 
import com.google.android.gms.gcm.GoogleCloudMessaging; 

import java.io.BufferedInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.net.HttpURLConnection; 
import java.net.URL; 
import java.util.concurrent.atomic.AtomicInteger; 

public class MainActivity extends Activity { 

    public static final String PROPERTY_REG_ID = "registration_id"; 
    private static final String PROPERTY_APP_VERSION = "appVersion"; 
    private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; 

    String SENDER_ID = "example_Number"; 

    /** 
    * Tag used on log messages. 
    */ 
    static final String TAG = "GCMDemo"; 
    private WebView myWebView = null; 

    TextView mDisplay; 
    GoogleCloudMessaging gcm; 
    AtomicInteger msgId = new AtomicInteger(); 
    SharedPreferences prefs; 
    Context context; 
    String regid; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 

     super.onCreate(savedInstanceState); 
     Log.d(TAG, "hola coño"); 
     setContentView(R.layout.activity_main); 

     //initialize variables 
     context = getApplicationContext(); 
     gcm = GoogleCloudMessaging.getInstance(this); 
     prefs = getPreferences(0); 
     mDisplay = new TextView(getApplicationContext()); 

     // web view 
     this.myWebView = (WebView) findViewById(R.id.activity_main_webview); 
     myWebView.setWebViewClient(new WebViewClient()); 
     WebSettings webSettings = myWebView.getSettings(); 
     webSettings.setJavaScriptEnabled(true); 
     myWebView.loadUrl("http://mongini.net/guiasdelsur"); 

     //remove shared prefs 
     /* 
     SharedPreferences prefs = getGCMPreferences(context); 
     SharedPreferences.Editor edit = prefs.edit(); 
     edit.clear(); 
     edit.commit(); 
     */ 
     /* 
     SharedPreferences clear_cache = getSharedPreferences("registration_id", MODE_PRIVATE); 
     SharedPreferences.Editor edit = clear_cache.edit(); 
     edit.clear(); 
     edit.commit(); 
     */ 


     // Check device for Play Services APK. If check succeeds, proceed with 
     // GCM registration. 
     if (checkPlayServices()) { 
      gcm = GoogleCloudMessaging.getInstance(this); 
      regid = getRegistrationId(context); 

      if (regid.isEmpty()) { 
       registerInBackground(); 
       Log.i(TAG,"ok"); 
      } 
     } else { 
      Log.i(TAG, "No valid Google Play Services APK found."); 

     } 
    } 



    // You need to do the Play Services APK check here too. 
    @Override 
    protected void onResume() { 
     super.onResume(); 
     checkPlayServices(); 
    } 

    /** 
    * Check the device to make sure it has the Google Play Services APK. If 
    * it doesn't, display a dialog that allows users to download the APK from 
    * the Google Play Store or enable it in the device's system settings. 
    */ 
    private boolean checkPlayServices() { 

     int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this); 
     if (resultCode != ConnectionResult.SUCCESS) { 
      if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) { 
       GooglePlayServicesUtil.getErrorDialog(resultCode, this, 
         PLAY_SERVICES_RESOLUTION_REQUEST).show(); 
      } else { 
       Log.d(TAG, "This device is not supported."); 

       finish(); 
      } 
      return false; 
     } 
     return true; 
    } 
    /** 
    * Gets the current registration ID for application on GCM service. 
    * <p> 
    * If result is empty, the app needs to register. 
    * 
    * @return registration ID, or empty string if there is no existing 
    *   registration ID. 
    */ 
    private String getRegistrationId(Context context) { 
     final SharedPreferences prefs = getGCMPreferences(context); 
     String registrationId = prefs.getString(PROPERTY_REG_ID, ""); 
     if (registrationId.isEmpty()) { 
      Log.i(TAG, "Registration not found."); 
      return ""; 
     } 
     // Check if app was updated; if so, it must clear the registration ID 
     // since the existing registration ID is not guaranteed to work with 
     // the new app version. 
     int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE); 
     int currentVersion = getAppVersion(context); 
     if (registeredVersion != currentVersion) { 
      Log.i(TAG, "App version changed."); 
      return ""; 
     } 
     return registrationId; 
    } 

    /** 
    * @return Application's {@code SharedPreferences}. 
    */ 
    private SharedPreferences getGCMPreferences(Context context) { 
     // This sample app persists the registration ID in shared preferences, but 
     // how you store the registration ID in your app is up to you. 
     return getSharedPreferences(MainActivity.class.getSimpleName(), 
       Context.MODE_PRIVATE); 
    } 


    /** 
    * @return Application's version code from the {@code PackageManager}. 
    */ 
    private static int getAppVersion(Context context) { 
     try { 
      PackageInfo packageInfo = context.getPackageManager() 
        .getPackageInfo(context.getPackageName(), 0); 
      return packageInfo.versionCode; 
     } catch (PackageManager.NameNotFoundException e) { 
      // should never happen 
      throw new RuntimeException("Could not get package name: " + e); 
     } 
    } 

    /** 
    * Registers the application with GCM servers asynchronously. 
    * <p> 
    * Stores the registration ID and app versionCode in the application's 
    * shared preferences. com.example.alfredo.webapp.MainActivity 
    */ 
    private void registerInBackground() { 

     new AsyncTask<Void,Void,String>() { 
      @Override 
      protected String doInBackground(Void... params) { 
       String msg = ""; 
       try { 

        regid = gcm.register(SENDER_ID); 


        msg = "Device registered, registration id=" + regid; 

        // You should send the registration ID to your server over HTTP, 
        // so it can use GCM/HTTP or CCS to send messages to your app. 
        sendRegistrationIdToBackend(); 


        // Save the regid for future use - no need to register again. 
        SharedPreferences.Editor editor = prefs.edit(); 
        editor.putString(PROPERTY_REG_ID, regid); 
        editor.commit(); 
       } catch (IOException ex) { 
        msg = "Error :" + ex.getMessage(); 
       } 
       return msg; 
      } 
      // Once registration is done, display the registration status 
      // string in the Activity's UI. 
      @Override 
      protected void onPostExecute(String msg) { 
       mDisplay.append(msg + "\n"); 
      } 
     }.execute(); 
    } 



    private String readStream(InputStream is) { 
     try { 
      ByteArrayOutputStream bo = new ByteArrayOutputStream(); 
      int i = is.read(); 
      while(i != -1) { 
       bo.write(i); 
       i = is.read(); 
      } 
      return bo.toString(); 
     } catch (IOException e) { 
      return ""; 
     } 
    } 

    /** 
    * Sends the registration ID to your server over HTTP, so it can use GCM/HTTP 
    * or CCS to send messages to your app. Not needed for this demo since the 
    * device sends upstream messages to a server that echoes back the message 
    * using the 'from' address in the message. 
    */ 
    private void sendRegistrationIdToBackend() { 
     // Your implementation here. 
     HttpURLConnection urlConnection = null; 
     try { 

      URL url = new URL("http://www.example.com/example.php?host=localhost&dbname=db_name&user=user&pass=pass&idPush="+regid); 
      urlConnection = (HttpURLConnection) url.openConnection(); 

      /** Connecting to url */ 
      urlConnection.connect(); 

      InputStream in = new BufferedInputStream(urlConnection.getInputStream()); 
      readStream(in); 
     }catch(Exception e){ 
      Log.d("Exception url ", e.toString()); 
     }finally { 
      urlConnection.disconnect(); 
     } 

    } 


    /** 
    * Stores the registration ID and app versionCode in the application's 
    * {@code SharedPreferences}. 
    * 
    * @param context application's context. 
    * @param regId registration ID 
    */ 
    //aqui peta 
    private void storeRegistrationId(Context context, String regId) { 
     final SharedPreferences prefs = getGCMPreferences(context); 
     int appVersion = getAppVersion(context); 
     Log.i(TAG, "Saving regId on app version " + appVersion); 
     SharedPreferences.Editor editor = prefs.edit(); 
     editor.putString(PROPERTY_REG_ID, regId); 
     editor.putInt(PROPERTY_APP_VERSION, appVersion); 
     editor.commit(); 
    } 



    public class GcmBroadcastReceiver extends WakefulBroadcastReceiver { 
     @Override 
     public void onReceive(Context context, Intent intent) { 
      // Explicitly specify that GcmIntentService will handle the intent. 
      ComponentName comp = new ComponentName(context.getPackageName(), 
        GcmIntentService.class.getName()); 
      // Start the service, keeping the device awake while it is launching. 
      startWakefulService(context, (intent.setComponent(comp))); 
      setResultCode(Activity.RESULT_OK); 
     } 
    } 


    public class GcmIntentService extends IntentService { 
     public static final int NOTIFICATION_ID = 1; 
     private NotificationManager mNotificationManager; 
     NotificationCompat.Builder builder; 

     public GcmIntentService() { 
      super("GcmIntentService"); 

     } 

     @Override 
     protected void onHandleIntent(Intent intent) { 
      Bundle extras = intent.getExtras(); 
      GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this); 
      // The getMessageType() intent parameter must be the intent you received 
      // in your BroadcastReceiver. 
      String messageType = gcm.getMessageType(intent); 

      if (!extras.isEmpty()) { // has effect of unparcelling Bundle 
      /* 
      * Filter messages based on message type. Since it is likely that GCM 
      * will be extended in the future with new message types, just ignore 
      * any message types you're not interested in, or that you don't 
      * recognize. 
      */ 
       if (GoogleCloudMessaging. 
         MESSAGE_TYPE_SEND_ERROR.equals(messageType)) { 
        sendNotification("Send error: " + extras.toString()); 
       } else if (GoogleCloudMessaging. 
         MESSAGE_TYPE_DELETED.equals(messageType)) { 
        sendNotification("Deleted messages on server: " + 
          extras.toString()); 
        // If it's a regular GCM message, do some work. 
       } else if (GoogleCloudMessaging. 
         MESSAGE_TYPE_MESSAGE.equals(messageType)) { 
        // This loop represents the service doing some work. 
        for (int i=0; i<5; i++) { 
         Log.i(TAG, "Working... " + (i+1) 
           + "/5 @ " + SystemClock.elapsedRealtime()); 
         try { 
          Thread.sleep(5000); 
         } catch (InterruptedException e) { 
         } 
        } 
        Log.i(TAG, "Completed work @ " + SystemClock.elapsedRealtime()); 
        // Post notification of received message. 
        sendNotification("Received: " + extras.toString()); 
        Log.i(TAG, "Received: " + extras.toString()); 
       } 
      } 
      // Release the wake lock provided by the WakefulBroadcastReceiver. 
      GcmBroadcastReceiver.completeWakefulIntent(intent); 
     } 

     // Put the message into a notification and post it. 
     // This is just one simple example of what you might choose to do with 
     // a GCM message. 
     private void sendNotification(String msg) { 
      mNotificationManager = (NotificationManager) 
        this.getSystemService(Context.NOTIFICATION_SERVICE); 

      PendingIntent contentIntent = PendingIntent.getActivity(this, 0, 
        new Intent(this, MainActivity.class), 0); 

      NotificationCompat.Builder mBuilder = 
        new NotificationCompat.Builder(this) 
          //.setSmallIcon(R.drawable.ic_stat_gcm) 
          .setContentTitle("GCM Notification") 
          .setStyle(new NotificationCompat.BigTextStyle() 
            .bigText(msg)) 
          .setContentText(msg); 

      mBuilder.setContentIntent(contentIntent); 
      mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build()); 
     } 
    } 



    //back device button 
    @Override 
    public boolean onKeyDown(int keyCode, KeyEvent event) { 
     if ((keyCode == KeyEvent.KEYCODE_BACK) && this.myWebView.canGoBack()) { 
      this.myWebView.goBack(); 
      return true; 
     } 

     return super.onKeyDown(keyCode, event); 
    } 


    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
     // Handle action bar item clicks here. The action bar will 
     // automatically handle clicks on the Home/Up button, so long 
     // as you specify a parent activity in AndroidManifest.xml. 
     int id = item.getItemId(); 

     //noinspection SimplifiableIfStatement 
     if (id == R.id.action_settings) { 
      return true; 
     } 

     return super.onOptionsItemSelected(item); 
    } 

} 
+0

Смотрите этот вопрос: http://stackoverflow.com/questions/3608955/receiver-as-inner-class-in-android – Natix

ответ

0

AFAIK внутренний класс BroadcastReceiver s работает только в том случае, если они static.

Фактически $ обозначает статический внутренний класс.

См https://stackoverflow.com/a/4392499/603270

+0

жаль, что это AFAIK? – user3033437

+0

это означает «Насколько я знаю». @shkschneider не должен использовать нетехнические аббревиатуры в ответах ... – eduyayo

+0

@eduyayo действительно? Я вижу их повсюду. Но все равно. – shkschneider

Смежные вопросы