【问题标题】:Intent constructor and BroadcastReceiverIntent 构造函数和 BroadcastReceiver
【发布时间】:2013-09-27 08:14:06
【问题描述】:

基于下面的示例项目。 (取自here

为什么action 字符串被传递给Intent 构造函数时会触发BroadcastReceiverContextSomeOtherActivity.class 被传递给它时不会触发?

           // this works
           Intent intent = new Intent(PROX_ALERT_INTENT);        

           //this does not
           Intent intent = new Intent(this, ProximityIntentReceiver.class);  
           intent.setAction(PROX_ALERT_INTENT);                             

ProximityAlertActivity.java:

package com.androidmyway.demo.proxymityalert;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManager;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;


public class ProximityAlertActivity extends Activity {
    private static final long POINT_RADIUS = 100; // in Meters
    private static final long PROX_ALERT_EXPIRATION = -1; // It will never expire
    private static final String PROX_ALERT_INTENT = "com.androidmyway.demo.ProximityAlert";
    private LocationManager locationManager;
    private EditText latitudeEditText;
    private EditText longitudeEditText;
    private Button addAlertButton;

    @Override
    public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_proxymity);

            locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

            latitudeEditText = (EditText) findViewById(R.id.point_latitude);
            longitudeEditText = (EditText) findViewById(R.id.point_longitude);
            addAlertButton = (Button) findViewById(R.id.add_alert_button);

            addAlertButton.setOnClickListener(new OnClickListener() {
                  public void onClick(View v) {
                         addProximityAlert();
                  }
            });

    }

    private void addProximityAlert() {
           double latitude = Double.parseDouble(latitudeEditText.getText().toString());
           double longitude = Double.parseDouble(longitudeEditText.getText().toString());
           //Intent intent = new Intent(PROX_ALERT_INTENT);           
           Intent intent = new Intent(this, ProximityIntentReceiver.class); 
           intent.setAction(PROX_ALERT_INTENT);
           PendingIntent proximityIntent = PendingIntent.getBroadcast(this, 2, intent,         PendingIntent.FLAG_UPDATE_CURRENT);
           locationManager.addProximityAlert(
                  latitude, // the latitude of the central point of the alert region
                  longitude, // the longitude of the central point of the alert region
                  POINT_RADIUS, // the radius of the central point of the alert region, in meters
                  PROX_ALERT_EXPIRATION, // time for this proximity alert, in milliseconds, or -1 to     indicate no                           expiration
                  proximityIntent // will be used to generate an Intent to fire when entry to or exit from the alert region is detected
           );

           IntentFilter filter = new IntentFilter(PROX_ALERT_INTENT);
           registerReceiver(new ProximityIntentReceiver(), filter);
           Toast.makeText(getApplicationContext(),"Alert Added",Toast.LENGTH_SHORT).show();
    }    
}

ProximityIntentReceiver:

package com.androidmyway.demo.proxymityalert;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.location.LocationManager;
import android.util.Log;

public class ProximityIntentReceiver extends BroadcastReceiver {
    private static final int NOTIFICATION_ID = 1000;

    @SuppressWarnings("deprecation")
    @Override
    public void onReceive(Context context, Intent intent) {
        String key = LocationManager.KEY_PROXIMITY_ENTERING;
        Boolean entering = intent.getBooleanExtra(key, false);
        if (entering) {
            Log.d(getClass().getSimpleName(), "entering");
        }else {
            Log.d(getClass().getSimpleName(), "exiting");
        }
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        Intent notificationIntent = new Intent(context, ProximityAlertActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
        Notification notification = createNotification();
        notification.setLatestEventInfo(context, "Proximity Alert!", "You are near your point of interest.", pendingIntent);

        notificationManager.notify(NOTIFICATION_ID, notification);
    }

    private Notification createNotification() {
        Notification notification = new Notification();
        notification.icon = R.drawable.ic_launcher;
        notification.when = System.currentTimeMillis();
        notification.flags |= Notification.FLAG_AUTO_CANCEL;
        notification.flags |= Notification.FLAG_SHOW_LIGHTS;
        notification.defaults |= Notification.DEFAULT_VIBRATE;
        notification.defaults |= Notification.DEFAULT_LIGHTS;
        notification.ledARGB = Color.WHITE;
        notification.ledOnMS = 1500;
        notification.ledOffMS = 1500;
        return notification;
    }
}

AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.androidmyway.demo.proxymityalert"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>


    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.Black.NoTitleBar" >
        <activity
            android:name=".ProximityAlertActivity"
        >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

【问题讨论】:

    标签: android android-intent broadcastreceiver


    【解决方案1】:

    原来我在问两个问题:

    Q1:为什么BroadcastReceiver 会在Action 字符串被传递给Intent 构造函数时触发,但在ContextSomeOtherActivity.class 被传递给它时不会触发? (我认为这是一个普遍的问题,而不仅仅是针对我发布的代码)

    Q2:为什么BroadcastReceiver 会在Action 字符串传递给Intent 构造函数时触发,而在ContextNameOfReceiver.class 传递给它时不会触发? (这是特定于我发布的代码,因为它使用了以编程方式注册的接收器)

           //this works
           Intent intent = new Intent(PROX_ALERT_INTENT);        
    
           //this does not
           Intent intent = new Intent(this, ProximityIntentReceiver.class);  
           intent.setAction(PROX_ALERT_INTENT); 
    

    经过一些研究,我相信我有正确的答案:

    ANS1:即使在使用new Intent(context, SomeOtherActivity.class) 调用构造函数后手动将Action 添加到Intent 仍然不会触发BroadcastReceiver。 Android developers page on Intents给出解释:

    Android delivers an explicit intent to an instance of the designated target class. Nothing in the Intent object other than the component name matters for determining which component should get the intent.

    因此手动添加Action 无效。此外,一旦使用显式Intent,唯一可以在Intent 构造函数中设置的类就是您明确要调用的BroadcastReceiver 的类。在这种情况下,传递任何其他类都没有意义。

    这将我带到 ANS2

    CommonsWare 对以下 SO 问题提供了非常明确的答案:

    Why don't I get proximity alterts even though I've registered alerts?

    问题是我正在以编程方式注册接收器。这迫使我使用IntentFilter。如上所述,一旦使用显式的Intent重要的是组件名称。我猜在动态注册接收器时,它正在侦听IntentFilter 中定义的Action,而不是组件名称(接收器名称)。

    使用显式Intent

    Intent intent = new Intent(this, ProximityIntentReceiver.class); 
    

    以及清单中的静态注册接收器:

    <receiver android:name="ProximityIntentReceiver" />
    

    解决了问题。

    如果我有任何错误,请纠正我。

    【讨论】:

      【解决方案2】:

      我相信,如果我错了,请纠正我,构造:

      Intent intent = new Intent(this, SomeActivity.class); 
      

      旨在启动另一个活动、意图服务或类似的,而不是用于广播。

      这也是有道理的,因为其他应用程序可以接收广播,并且如果广播包含类引用,那么其他应用程序将没有机会调用那段代码。

      上述意图构造的 Javadocs:

      为特定组件创建意图。所有其他字段(操作、数据、类型、类)都是空的,尽管稍后可以通过显式调用对其进行修改。这提供了一种方便的方法来创建旨在执行硬编码类名的意图,而不是依赖系统为您找到合适的类;请参阅 setComponent(ComponentName) 以了解有关此影响的更多信息。

      我看到您确实在之后设置了操作,但我认为在 Android 引擎盖下,无论如何,该意图仍然被认为是针对特定组件的。

      【讨论】:

      • 这是有道理的。但是导致这种情况的具体原因是什么?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-12-13
      • 2015-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多