简体   繁体   中英

Android: How do I allow access to only part of an objects members using an interface

How do I "pass an interface", ie what CommonsWare suggested to the asker in the question below ?>

The other is to make DownloadFileTask public and pass something into the constructor. In this case, to minimize coupling, it may be that you don't want to pass an Activity, but some other sort of interface that limits what the AsyncTask can do. That way, you can choose to implement the interface on an Activity, or as a separate object, or whatever. – CommonsWare Apr 27 '10 at 17:44

How do I allow a thread to access to only some (or just one) of the objects public methods?

Answer> See the answers below.

The real problem was my misunderstanding of what an interface does. If the input type of a method F (or parameterization of a class) is specified as an interface [ie F(my_interface i)], and an object X is passed to that method which implements my_interface [F(X)], then only the members of X which implement my_interface will be accessible to the method F even if other members exist.

I thought an interface put a constraint only on the implementing class. I didn't understand that when an interface was used as a type it would also constrict access to the members of the implementing class. In retrospect, given that Java is statically typed this is obvious. See the Java tutorial for more info.

You pass an interface to an AsyncTask just like you would any function. For example:

interface MyInterface { ... }

class MyClass implements MyInterface { ... }

AsyncTask<MyInterface, Integer, Void> myAsyncTask = new ...;
myAsyncTask.execute(myClassInstance);

AsyncTask is a Generic that allows you to specify the parameter type, progress return type, and result type (MyInterface, Integer, Void respectively). You can specify any interface, class, enum, or primitive type you want.

From here, its just your standard coding. Create a class that extends an interface, create an async task with the parameter set to the interface, pass in the class and the task can only access the interface functions. You'll want to be careful about thread issues (data races, etc..)

Suppose that you need to be able to report the steps of some background task. You could choose to do it by changing the text of a label, or by changing the text of a notification bubble.

One way to do it would be to pass the label or notification to the background task:

public class BackgroundTask {
    private Label labelToUpdate;
    private Notification notificationToUpdate;

    public BackgroundTask(Label label) {
        this.labelToUpdate = label;
    }

    public BackgroundTask(Notification notification) {
        this.notificationToUpdate = notification;
    }

    //...
    private void reportNewStep(String step) {
        if (labelToUpdate != null) {
            labelToUpdate.setText(step);
        }
        if (notificationToUpdate != null) {
            notificationToUpdate.alert(step);
        }
    }
}

This couples the BackgroundTask with the UI, and with the two possible ways of reporting progress. If you add a third one, you must change the code of BackGroundTask.

Now extract an interface:

public interface ProgressListener {
    void report(String step);
}

public class BackgroundTask {
    private ProgressListener listener;

    public BackgroundTask(ProgressListener listener) {
        this.listener = listener;
    }

    //...
    private void reportNewStep(String step) {
        listener.report(step);
    }
}

The BackgroundTask is now simpler, and isn't dependent anymore on allthe possible ways to report progress. The caller just has to give it an implementation of the ProgressListener interface. For a label-based progress, it would do the following:

BackgroundTask task = new BackgroundTask(new ProgressListener() {
    @Override
    public void report(String step) {
        label.setText(step);
    }
});

Or your activity could implement the ProgressListener directly:

public class MyActivity implements ProgressListener {
    private Notification notification;

    // ...
    @Override
    public void report(String step) {
        notification.alert(step);
    }
    // ...
    BackgroundTask backgroundTask = new BackgroundTask(this);
}

No more coupling between Label and BackgroundTask, or between MyActivity and BackgroundTask. The BackgroundTask can't call any method of Label, Notification or MyActivity anymore. It can only call what it's supposed to call: the method of a dedicated ProgressListener interface.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM