Skip to content
This repository was archived by the owner on Feb 26, 2023. It is now read-only.
This repository was archived by the owner on Feb 26, 2023. It is now read-only.

enhanced IntentService support #572

Description

@pstefan

I am often using IntentService objects for background activities in my apps. These IntentService have quite many different ACTIONs in it, which are distinguished by different ACTION strings and different extras.

To get a structure in the written code I always create an own methods for each action. But I have to write many code by myself that could be wrapped by annotations and would make the code better readable.

I wanna show you this in an example. For this purpose I am using the source code of the FileTransferService of WifiDirectDemo Sample App.

public class FileTransferService extends IntentService {

    private static final int SOCKET_TIMEOUT = 5000;
    public static final String ACTION_SEND_FILE = "com.example.android.wifidirect.SEND_FILE";
    public static final String EXTRAS_FILE_PATH = "file_url";
    public static final String EXTRAS_GROUP_OWNER_ADDRESS = "go_host";
    public static final String EXTRAS_GROUP_OWNER_PORT = "go_port";

    public FileTransferService(String name) {
        super(name);
    }

    public FileTransferService() {
        super("FileTransferService");
    }

    /*
     * (non-Javadoc)
     * @see android.app.IntentService#onHandleIntent(android.content.Intent)
     */
    @Override
    protected void onHandleIntent(Intent intent) {

        Context context = getApplicationContext();
        if (intent.getAction().equals(ACTION_SEND_FILE)) {
            String fileUri = intent.getExtras().getString(EXTRAS_FILE_PATH);
            String host = intent.getExtras().getString(EXTRAS_GROUP_OWNER_ADDRESS);
            Socket socket = new Socket();
            int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT);

            try {
                Log.d(WiFiDirectActivity.TAG, "Opening client socket - ");
                socket.bind(null);
                socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT);

                Log.d(WiFiDirectActivity.TAG, "Client socket - " + socket.isConnected());
                OutputStream stream = socket.getOutputStream();
                ContentResolver cr = context.getContentResolver();
                InputStream is = null;
                try {
                    is = cr.openInputStream(Uri.parse(fileUri));
                } catch (FileNotFoundException e) {
                    Log.d(WiFiDirectActivity.TAG, e.toString());
                }
                DeviceDetailFragment.copyFile(is, stream);
                Log.d(WiFiDirectActivity.TAG, "Client: Data written");
            } catch (IOException e) {
                Log.e(WiFiDirectActivity.TAG, e.getMessage());
            } finally {
                if (socket != null) {
                    if (socket.isConnected()) {
                        try {
                            socket.close();
                        } catch (IOException e) {
                            // Give up
                            e.printStackTrace();
                        }
                    }
                }
            }

        }
    }
}

In my opinion the whole onHandleIntent method could be generated automatically and the annotated class could look like the following:

@EIntentService
public class FileTransferService extends IntentService {
    private static final int SOCKET_TIMEOUT = 5000;
    public static final String ACTION_SEND_FILE = "com.example.android.wifidirect.SEND_FILE";
    public static final String EXTRAS_FILE_PATH = "file_url";
    public static final String EXTRAS_GROUP_OWNER_ADDRESS = "go_host";
    public static final String EXTRAS_GROUP_OWNER_PORT = "go_port";

    public FileTransferService() {
        super("FileTransferService");
    }

    @Action(ACTION_SEND_FILE)
    public void sendFile( @Extra(EXTRAS_FILE_PATH) String fileUri, @Extra(EXTRAS_GROUP_OWNER_ADDRESS) String host, @Extra(EXTRAS_GROUP_OWNER_PORT) int port ) {
            Socket socket = new Socket();
         try {
                Log.d(WiFiDirectActivity.TAG, "Opening client socket - ");
                socket.bind(null);
                socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT);

                Log.d(WiFiDirectActivity.TAG, "Client socket - " + socket.isConnected());
                OutputStream stream = socket.getOutputStream();
                ContentResolver cr = context.getContentResolver();
                InputStream is = null;
                try {
                    is = cr.openInputStream(Uri.parse(fileUri));
                } catch (FileNotFoundException e) {
                    Log.d(WiFiDirectActivity.TAG, e.toString());
                }
                DeviceDetailFragment.copyFile(is, stream);
                Log.d(WiFiDirectActivity.TAG, "Client: Data written");
            } catch (IOException e) {
                Log.e(WiFiDirectActivity.TAG, e.getMessage());
            } finally {
                if (socket != null) {
                    if (socket.isConnected()) {
                        try {
                            socket.close();
                        } catch (IOException e) {
                            // Give up
                            e.printStackTrace();
                        }
                    }
                }
            }
   }
}

The code reduction in this example isn't much, but it has already improved the readability of the code.
With more ACTIONs defined the effect would be much greater!

The generated onHandleIntent method could look like this:

    @Override
    protected void onHandleIntent(Intent intent) {
        if ( ACTION_SEND_FILE.equals( intent.getAction() ) {
            String fileUri = intent.getExtras().getString(EXTRAS_FILE_PATH);
            String host = intent.getExtras().getString(EXTRAS_GROUP_OWNER_ADDRESS);
            int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT);
            sendFile( fileUri, host, port );
        } else if ( ACTION_OTHER.equals( intent.getAction() ) {
            // etc.
        }
    }

But it would not only be possible to simplify the IntentService class itself, it's also possible to simplify the calling actions of the IntentService.

Intent intent = new Intent(context, FileTransferService.class);
intent.setAction(FileTransferService.ACTION_SEND_FILE);
intent.putExtra(FileTransferService.EXTRAS_FILE_PATH, "/image.png");
intent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_ADDRESS, "192.168.0.3");
intent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_PORT, 8080);
context.startService(intent);

could be converted to

FileTransferService_.intent(context).sendFile("/image.png", "192.168.0.3", 8080).start();

I think this would be a great enhancement for every android programmer. But I do not know if annotations can be used that way and if the annotations names are the best choice for it.

What do you think? Do you think that this is a good enhancement for androidannotations? And could it be implemented that way?

Activity

  1. a-thomas commented on May 4, 2013

    @a-thomas
    Contributor

    Hi @pstefan, your idea sounds good. Indeed it could simplify the creation and usage of an IntentService with multiple actions.

    I quickly took a look at your sample code. I've modified some lines, here is the code:

    @EIntentService
    // the class must be abstract
    public abstract class MyIntentService extends IntentService {
    
        public MyIntentService() {
            super(MyIntentService.class.getSimpleName());
        }
    
        @Action
        public void actionOne(String extra1, String extra2, String extra3){
            // we use the parameters name as the extra name
        }
    
    }

    and the generated class:

    public class MyIntentService_ extends MyIntentService {
    
      // use the method name for the constant name
        public static final String ACTION_ONE= "action_one";
    
        @Override
        protected void onHandleIntent(Intent intent) {
            String action = intent.getAction();
            if (action.equals(ACTION_ONE)) {
                            // use the method parameters name as extra names
                String extra1 = intent.getStringExtra("extra1");
                String extra2 = intent.getStringExtra("extra2");
                String extra3 = intent.getStringExtra("extra3");
                super.actionOne(extra1, extra2, extra3);
            }
        }
    
    }

    I don't spend time to think about how we can call a specific action IntentService through the Intent builder.
    Your syntax seems to be possible to do:
    FileTransferService_.intent(context).sendFile("/image.png", "192.168.0.3", 8080).start();

    @DayS , @mathieuboniface , @JoanZapata any feedbacks/comments ?

  2. DayS commented on May 4, 2013

    @DayS
    Contributor

    It's good for me. That's what I had in my mind too :)

  3. ghost assigned on Jul 19, 2013
  4. DayS commented on Sep 20, 2013

    @DayS
    Contributor

    Merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions