如何在 onNewIntent 执行之前拦截 NFC 标签

how to intercept NFC tag before onNewIntent executes

我有一个应用程序可以捕获 NFC 标签。我过去遇到的问题是用户以不稳定的方式将鼠标悬停在标签上,导致 NFC 适配器触发两次。

我做了一些事情来解决这个问题。

清单:

<activity
    android:name=".NfcActivity"
    android:screenOrientation="portrait"
    android:launchMode="singleTask" 
    android:noHistory="true"
    android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation">

    <intent-filter>


        <action android:name="android.nfc.action.NDEF_DISCOVERED" />

        <category android:name="android.intent.category.DEFAULT" />

        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>

这会将 NFC 捕获 Activity 设置为堆栈中的唯一实例,并且没有历史记录。我已经覆盖了所有可以停止和重新启动此 activity 的配置更改,后者可能导致意图数据重新传送到 activity,使其看起来像重复扫描。

在 Activity 本身中,我已经覆盖了 onNewIntent,除了显示错误的扫描屏幕外什么都不做。我也明白 onNewIntent 应该从功能的角度反映 onCreate,但是因为以前版本的应用程序已经向下一个 Activity 发送了 2 次扫描,我只希望 NFC 捕获代码在一个地方,onCreate。

在 onCreate 中,我做了进一步的测试,以防止将鼠标悬停在标签上并创建错误的扫描。

该应用程序似乎运行良好,但在一个特定的 phone(三星 Galaxy Young 2)上,如果用户将 phone 放在标签上几秒钟,NFC 适配器似乎连续开火几次。

发生这种情况时,原始扫描将被取消。这样做的原因是 oncreate 处理标签但是当后续扫描发生时(通过悬停,偶然), onPause -> onNewIntent 运行。所以 Activity 跳出 onCreate 并停止处理标签。 onNewIntent 显示失败的扫描屏幕并启动菜单屏幕。

以上不是什么大问题,因为用户必须重新扫描标签。

我希望发生的事情是:

当 onCreate 运行时,无论如何处理标签,即使 onNewintent 执行。有没有办法在到达 onNewintent 和 onPause 之前拦截意图?

也许有一个我可以使用的全局标志,可以先检查它以说明 onCreate 仍然是 运行 而 onNewIntent 不应该,或者更重要的是 onPause 没有被调用使 onCreate 停止 运行.

import java.util.List;
import java.util.concurrent.ExecutionException;

import org.ndeftools.Message;
import org.ndeftools.Record;

import android.app.Activity;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.os.Vibrator;
import android.util.Log;
import android.widget.Toast;

public class NfcActivity extends Activity {

    private static final String TAG = NfcActivity.class.getName();

    protected NfcAdapter nfcAdapter;
    protected PendingIntent nfcPendingIntent;

    Handler handler;
    Runnable runnable;

    Handler failHandler;
    Runnable failRunnable;

    Parcelable[] messages;

    Intent i;

    Tag tag;
    String tagId;

    boolean nfcConnected;

    ProgressDialog progressDialog;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.nfcactivitylayout);
        Log.e(TAG, "oncreate");
        nfcConnected = false;

        // initialize NFC
        nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        nfcPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, this.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

        tag = null;
        tagId = null;

        i = getIntent();

        if ((i.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {

            //check to see if Android has previously killed the app and relaunched it from History
            //and delivered the original intent.
            //if it has do not process and launch the menu screen
                Intent processPayloadIntent = new Intent(NfcActivity.this, NfcscannerActivity.class);
                processPayloadIntent.setAction("QRCODE_ACTION"); 
                processPayloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(processPayloadIntent);

            }else{



        tag = i.getParcelableExtra(NfcAdapter.EXTRA_TAG);

        tagId = bytesToHexString(tag.getId());

        Log.e(TAG, "tagID = " + tagId);

        Log.e(TAG, "oncreate intent action = " + i.getAction());




        //The activity has captured tag data, prove the user is not hovering over the tag and is doing a good scan
        //hovering can trigger the adapter twice

        AsyncNfcConnect asnc = new AsyncNfcConnect();
        try {
            asnc.execute().get();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }




        Log.e(TAG, "nfcConnected!!!!!!!!!!!!!!!!!!!!!!!!! = " + nfcConnected);

        if(nfcConnected == true){


            int buildVersionSdk = Build.VERSION.SDK_INT;
            int buildVersionCodes = Build.VERSION_CODES.GINGERBREAD;

            Log.e(TAG, "buildVersionSdk = " + buildVersionSdk
                    + "buildVersionCodes = " + buildVersionCodes);

            int themeVersion;
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD) {

                themeVersion = 2;

            } else {

                themeVersion = 1;
            }

            try{

            progressDialog = new ProgressDialog(this, themeVersion);
            progressDialog.setTitle("NFC Tag Scanned");
            progressDialog.setMessage("Processing tag...");
            progressDialog.setIndeterminate(true);
            progressDialog.show();

            }catch(Exception e){        }




        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(i.getAction()) || NfcAdapter.ACTION_TAG_DISCOVERED.equals(i.getAction())) {            

            if(NfcScannerApplication.isCanScanNfcTag()){

            messages = i.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            if (messages != null) {


                //setContentView(R.layout.successfulnfc);

                NfcScannerApplication.startNfcTimer();
                //Toast.makeText(this, "NFC timer set", Toast.LENGTH_LONG).show();

                Log.e(TAG, "Found " + messages.length + " NDEF messages"); // is almost always just one

                vibrate(); // signal found messages :-)



                initHandler();
                handler.postDelayed(runnable,  2000);

      }else{

          Toast.makeText(this, "Data on tag was not correct", Toast.LENGTH_LONG).show();

              try{

                handler.removeCallbacks(runnable);
                Log.e(TAG, "just removed callback to runnable that reads nfc tag data");

                }catch(Exception e){

                }




                initFailHandler();
                failHandler.postDelayed(failRunnable,  1);



      }

            }else{


                try{

                    handler.removeCallbacks(runnable);
                    Log.e(TAG, "just removed callback to runnable that reads nfc tag data");

                    }catch(Exception e){

                    }




                    initFailHandler();
                    failHandler.postDelayed(failRunnable,  1);


            }



        } else {

            Toast.makeText(this, "Tag not recognized correctly", Toast.LENGTH_LONG).show();

                try{

                handler.removeCallbacks(runnable);
                Log.e(TAG, "just removed callback to runnable that reads nfc tag data");

                }catch(Exception e){

                }




                initFailHandler();
                failHandler.postDelayed(failRunnable,  1);

        }


        }else{


            try{

                Toast.makeText(this, "Phone wasn't connected to Tag", Toast.LENGTH_LONG).show();

                handler.removeCallbacks(runnable);
                Log.e(TAG, "just removed callback to runnable that reads nfc tag data");

                }catch(Exception e){

                }




                initFailHandler();
                failHandler.postDelayed(failRunnable,  1);



        }//end of NFC connect test


        }//end of launched from history check


    }//end of onCreate





    @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG, "onStart");
    }





    @Override
    protected void onStop() {
        super.onStop();
        Log.e(TAG, "onStop");
    }





    @Override
    public void onNewIntent(Intent intent) {
        Log.e(TAG, "onNewIntent");

                            Toast.makeText(this, "Bad scan!!!", Toast.LENGTH_LONG).show();



                            initFailHandler();
                            failHandler.postDelayed(failRunnable, 1);



    }//end of onNewIntent






    public void enableForegroundMode() {
        Log.e(TAG, "enableForegroundMode");

        IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED); // filter for all
        IntentFilter[] writeTagFilters = new IntentFilter[] {tagDetected};
        nfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, writeTagFilters, null);
    }

    public void disableForegroundMode() {
        Log.e(TAG, "disableForegroundMode");

        nfcAdapter.disableForegroundDispatch(this);
    }





    @Override
    protected void onResume() {
     super.onResume();
     Log.e(TAG, "onResume");

        enableForegroundMode();
    }

    @Override
    protected void onPause() {
        Log.e(TAG, "onPause");

        super.onPause();

        disableForegroundMode();

        if(handler != null){
            handler.removeCallbacks(runnable);
        }


    }

    private void vibrate() {
        Log.e(TAG, "vibrate");

        Vibrator vibe = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE) ;
        vibe.vibrate(500);
    }


    public void initHandler(){

          handler = new Handler();
          runnable = new Runnable() {
                public void run() {
                    processTag();

                }

                private void processTag() {
                    Log.e(TAG, "about to process tag");

                    try{
                        progressDialog.dismiss();

                    }catch(Exception e){

                        //do nothing
                    }

                    // parse to records
                    for (int i = 0; i < messages.length; i++) {
                        try {
                            List<Record> records = new Message((NdefMessage)messages[i]);

                            Log.e(TAG, "Found " + records.size() + " records in message " + i);

                            for(int k = 0; k < records.size(); k++) {
                                Log.e(TAG, " Record #" + k + " is of class " + records.get(k).getClass().getSimpleName());

                                Record record = records.get(k);

                                NdefRecord ndefRecord = record.getNdefRecord();

                                byte[] arr = ndefRecord.getPayload();

                                String payload = new String(arr);


                                if(payload.length() > 0){

                                payload = payload.substring(3, payload.length());

                                Log.e(TAG, "payload = " + payload);

                                String[] splitPayload = payload.split(",");

                                String tagType = splitPayload[0];
                                String tagCompany = splitPayload[1];
                                String tagClientID = splitPayload[2];
                                String tagClientName = splitPayload[3];

                                if(! tagClientID.equalsIgnoreCase("0") && tagClientID.length() > 0){

                                    handler.post(new Runnable(){
                                        public void run() {

                                            setContentView(R.layout.successfulnfc);

                                          }
                                        });






                                Intent processPayloadIntent = new Intent(NfcActivity.this, NfcscannerActivity.class);
                                processPayloadIntent.putExtra("payload", payload);
                                processPayloadIntent.putExtra("tagid", tagId);
                                processPayloadIntent.setAction("NFC"); 
                                processPayloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                                //processPayloadIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK);
                                startActivity(processPayloadIntent);
                                finish();
                                overridePendingTransition(0, R.anim.activity_animation_zoom_in);





                                    }else{
                                        Toast.makeText(NfcActivity.this, "Tag data problem/Scan problem.", Toast.LENGTH_LONG).show();

                                        initFailHandler();
                                        failHandler.postDelayed(failRunnable, 1);


                                    }

                                }else{
                                    Toast.makeText(NfcActivity.this, "Tag data problem/Scan problem.", Toast.LENGTH_LONG).show();

                                    initFailHandler();
                                    failHandler.postDelayed(failRunnable, 1);

                                }



                            }
                        } catch (Exception e) {
                            Log.e(TAG, "Problem parsing message", e);
                        }

                    }

                }
            };

        }





    public void initFailHandler(){

          failHandler = new Handler();
          failRunnable = new Runnable() {
                public void run() {

                    returnToMainMenu();

                }

                private void returnToMainMenu() {
                    //Log.e(TAG, "about to return to main menu");

                    try{
                        progressDialog.dismiss();

                    }catch(Exception e){

                        //do nothing
                    }

                    Toast.makeText(NfcActivity.this, "Please check your scanning technique.\nPlease do not hover over tag or swipe...", Toast.LENGTH_LONG).show();

                    failHandler.post(new Runnable(){
                        public void run() {

                            setContentView(R.layout.nfcfail);

                          }
                        });






                    //onBackPressed();
                    Intent processPayloadIntent = new Intent(NfcActivity.this, NfcscannerActivity.class);
                    processPayloadIntent.setAction("QRCODE_ACTION"); 
                    processPayloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                    startActivity(processPayloadIntent);
                    finish();
                    //overridePendingTransition(0, R.anim.activity_animation_zoom_in);

                }
            };

        }




    private String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("0x");
        if (src == null || src.length <= 0) {
            return null;
        }

        char[] buffer = new char[2];
        for (int i = 0; i < src.length; i++) {
            buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);
            buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
            System.out.println(buffer);
            stringBuilder.append(buffer);
        }

        return stringBuilder.toString();
    }



private class AsyncNfcConnect extends AsyncTask<String, Void, String> {






        @Override
        protected String doInBackground(String... params) {

            NfcActivity.this.nfcConnected = false;
            String result;
            Ndef ndefTag = Ndef.get(tag);

            try {
                Log.e(TAG, "about to test connect()********************************************");
                ndefTag.connect();  // this should already perform an IO operation and should therefore fail if there is no tag
                Log.e(TAG, "Ndef.connect() connected!********************************************");
                NdefMessage ndefMsg = ndefTag.getNdefMessage();  // this reads the current NDEF message from the tag and consequently causes an IO operation

                NfcActivity.this.nfcConnected = true;
                result = "OK";
                return result;

            } catch (Exception e) {
                // there is no tag or communication with tag dropped
                Log.e(TAG, "There a problem with connecting to the tag using Ndef.connect(");
                NfcActivity.this.nfcConnected = false;
                result = "NOTOK";
                return result;
            } finally {
                try {
                    ndefTag.close();
                } catch (Exception e) {
                }
            }


        }



    }//end of Async


}

您似乎坚持不在 onNewIntent() 中处理 NFC 意图。我建议 onCreate()onNewIntent() 都调用扫描标签的通用过程。这样,两个入口点将遵循相同的代码路径。

您声称应用程序 "jumps out of onCreate" 可能只是一种修辞手法?发生的情况是您的标签扫描 AsyncNfcConnect 运行s 在单独的线程上作为后台任务(应该如此)。该任务在 onCreate() 中创建,并在 onCreate() 完成后继续 运行ning(您可以在 onCreate() 末尾添加一个 Log 语句来检查) .当与标签的连接以某种方式丢失并重新发现标签时,如您所见,将调用 onNewIntent()

无论如何都无法阻止这种情况的发生,因此您的应用程序必须能够处理它。要检测并处理它,您可以在 activity 中设置一些标志,以指示您的后台任务正在 运行ning 或有 运行。您还可以存储是否发生异常的信息,并在重新发现时再次尝试扫描标签(这可能需要您实施我上面提出的建议)。如果您想让您的应用程序更加防故障,您还可以存储最后一次扫描标签的 ID,以便在您成功扫描(或未扫描)后重新发现它时再次肯定地识别它。当同一个标签不断出现异常时,您可以在一定次数后向用户指示他(例如,建议将设备放置在不同的位置 w.r.t。标签)。