Search code examples
androidbroadcastreceiveractivitynotfoundexception

Android Activity Not Found Exception and BroadcastReceiver


I have some functionality in my program I want to expose but I didn't seem to get the receiver working.

I tried the Manifest/Receiver:

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

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme"
        android:resizeableActivity = "true">
        <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>
        <receiver android:name=".IntStringReceiver" android:exported="true"  android:enabled="true">
            <intent-filter>
                <action android:name="com.nohkumado.intstringsynchro.EDIT_STRINGXML"/>
            </intent-filter>
            <intent-filter>
                <action android:name="com.nohkumado.intstringsynchro.ADD_STRINGXML"/>
                <action android:name="com.nohkumado.intstringsynchro.DEL_STRINGXML"/>
                <data android:mimeType="text/plain"/>
            </intent-filter>
        </receiver>
    </application>
</manifest>

package com.nohkumado.intstringsynchro;
import android.content.*;
import android.widget.*;
import android.util.*;

public class IntStringReceiver extends BroadcastReceiver
{
  public static final String TAG = "Receiver";
  @Override
  public void onReceive(Context context, Intent intent)
  {
    Toast.makeText(context, "Intent Detected:"+intent.getAction(), Toast.LENGTH_LONG).show();

    switch (intent.getAction())
    {
      case "com.nohkumado.intstringsynchro.EDIT_STRINGXML":
        {
          Intent intentStartMainActivity = new Intent(context, MainActivity.class);
          intentStartMainActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          context.startActivity(intentStartMainActivity);
          break;
        }
      case("com.nohkumado.intstringsynchro.ADD_STRINGXML"):
        {
          Toast.makeText(context, "add token "+intent.getExtras(), Toast.LENGTH_LONG).show();
          break;
        }
      case("com.nohkumado.intstringsynchro.DEL_STRINGXML"):
        {
          Toast.makeText(context, "del token "+intent.getExtras(), Toast.LENGTH_LONG).show();
          break;
        }
      default:
        {
          Toast.makeText(context, "no idea what to do with  "+intent, Toast.LENGTH_LONG).show();
          Log.d(TAG,"no idea what to do with  "+intent);
        }//default
    }//    switch (intent.getAction())
  }//  public void onReceive(Context context, Intent intent)
}//class

as pointed out, I had, erroneously, in the receiver part put a

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

which meant that in the best case only the first intent filter got fired, and not the others.... removed that

now as an other app, I created a small Tester, it has only 3 buttons to trigger the 3 actions I want to pass on as intents, since it was only a small test, I bound the onClick event in the layout file:

package com.nohkumado.istester;

import android.app.*;
import android.content.*;
import android.net.*;
import android.os.*;
import android.view.*;
import android.widget.*;

public class MainActivity extends Activity 
{
  @Override
  protected void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
  }//onCreate
  public void callIntString()
  {
    callIntString(null);
  }
  public void callIntString(View but)
  {
    Toast.makeText(this, "call int string", Toast.LENGTH_SHORT).show(); 

    String name="com.nohkumado.intstringsynchro.EDIT_STRINGXML";
    Intent callIt = new Intent(name);
    try
    {
      startActivity(callIt);
    }
    catch (ActivityNotFoundException e)
    {
      Toast.makeText(this, "no available activity"+callIt, Toast.LENGTH_SHORT).show();
  
      //callGooglePlayStore();
    }
  }

  private void callGooglePlayStore()
  {
    Intent launchIntent = getPackageManager().getLaunchIntentForPackage("com.android.vending");
    ComponentName comp = new ComponentName("com.android.vending", "com.google.android.finsky.activities.LaunchUrlHandlerActivity"); // package name and activity
    launchIntent.setComponent(comp);
    launchIntent.setData(Uri.parse("market://details?id=com.nohkumado.intstringsynchro"));

    startActivity(launchIntent);
  }//callIntString
}

and here was my understanding problem, instead of using startActivity, I should have tried sendBroadcast(launchIntent);

Ok, to close this up... first, I was not completely aware, that the activity contract in the manifest, opens a way for anyone to call on this activity.

Next, I have a specific application, that I want to open up to others, meaning editing the strings.xml files of Android projects, and I wanted to propose the equivalent of a REST API, with LIST/EDIT, ADD, DEL.

Now, if we work with activities, the easiest way to get a handle on my activity from outside is, I think, like this:

Intent call = pm.getLaunchIntentForPackage("com.nohkumado.intstringsynchro");

followed by some putExtra calls to insert a token identifying a specific action, and an eventual value to perform on.... finished by a startActivity

this way, the default activity is started, regardless of its name, and the intent passed on, that can be read in the onCreate method of the MainActivity.

To implement my REST API, I tried to make 3 entry points to my app, one for each type of access, only the LIST/EDIT firing up a UI, the 2 others spawning a series of Asynctasks doing the work in background. But this meant, that an eventual user has to know which activities to address.

So I reverted to using the putExtra of the intent with a token/value pair to implement my REST-like API.....

For educations sake I tried out the way over the broadcast mechanism, the advantage being that on the client side there seems no risk of crash, no need to catch a ActivityNotFound Exception, and, from a typo in my code, I noticed that the intent activity doesn't need to be bound to my actual app, I can choose any name I want.

For this, on the client side I needed to:

String name="com.nohkumado.intstringsynchro.EDIT_STRINGXML";
Intent callIt = new Intent(name);
sendBroadcast(callIt);

but on my app side I need to implement a complete BroadCastreceiver....

On the other side, this mechanism was extremely slow, giving the whole operation a very sluggish feel

Please correct me if I got it right this time, and I am open to suggestions if there a better ways to achieve my goal to propose to others those LIST/EDIT, ADD and REMOVE functionalities?


Solution

  • the reports back an activity not found exception

    Apparently, you do not have an activity with an <intent-filter> for an action string of com.nohkumado.intstringsynchro.EDIT_STRINGXML. Your manifest in the question certainly does not have such an activity. Your manifest in the question has a <receiver> element with an odd <intent-filter> that, among other things includes that action. However, <receiver> and <activity> are not the same thing.

    Either change your code to send a broadcast, or change your code to have an activity with that action string.