Callback is a very important feature in Java. For button click listener, it uses interface callback. I used to use interface callback often, but not very clearly understand it.

Interface and Callback

It is very easy to define an interface. Here we define a EatHelperInterface interface and it has a eat function.

1
2
3
interface EatHelperInterface {
void eat();
}

If this person is a Chinese, we just implement this interface.

1
2
3
4
5
6
public class Chinese implements EatHelperInterface{
@Override
public void eat() {
System.out.println("I need chopsticks to eat");
}
}

This is how we implement an interface. Also we can define many classes for different people. However, we don’t want so many class defined. It is better to define a People class to keep this interface, so that we can use callback to help different people to eat.

1
2
3
4
5
6
7
8
9
10
public class People {
interface EatHelperInterface eatHelperInterface;
public void setEatHelperInterface(EatHelperInterface eatHelperInterface){
this.eatHelperInterface = eatHelperInterface;
}

public void eatService(){
eatHelperInterface.eat();
}
}

setEatHelperInterface is to register the interface. People.eatService() is to execute the eat function of the interface. Our test can be like this.

1
2
3
4
5
6
7
public class Test {
public static void main(String[] args) {
People chinese = new people();
chinese.setEatHelperInterface(new Chinese());
chinese.eatService();
}
}

In many cases, we would like to use an anonymous inner class to define an interface.

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
public static void main(String[] args) {
People people = new people();
people.setEatHelperInterface(new EatHelperInterface() {
@Override
public void eat() {
System.out.println("I need **** to eat");
}
});
people.eatService();
}
}

Callback can separate the class which use this function and the one implement the function. Because the they do not care about each other. For any People, we just use people.eatService() to perform eat action. Different people pass different implementation into the interface.

Callback in Android

In Android development, for button click function, we have an interface OnClickListener and Button(View) contains this interface.

1
2
3
public interface OnClickListener {   
void onClick(View v);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {  
protected OnClickListener mOnClickListener;

public void setOnClickListener(OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
mOnClickListener = l;
}

public boolean performClick() {
if (mOnClickListener != null) {
mOnClickListener.onClick(this);
return true;
}
return false;
}
}

This button that extends the view class is very similar with the People class, which contains the interface and execute its function.

For MainActivity we should ask for help how to perform this click function. We can implement the interface in MainActivity and pass this into OnClickListener, or we can use anonymous inner class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MainActivity extends Activity{  
private Button button;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
}
  • Button -> People
  • setOnClickListener -> setEatHeplerInterface
  • onClickListener -> EatHelperInterface
  • onClick -> eat

The difference of these two cases is that people.eatService() called in Test, but for button, only case to trigger the performClick is when we click the button (Not showing in code).

Observer Pattern

This is a simple Observer Pattern example. Observer A is very sensitive to some actions of observable B. A needs to do actions at the moment changes in B. Also look at the button example. When trigger button.performClick, OnClickListener need to react immediately.

Trigger: button Click -> view.performClick -> onClick

Hierarchy: (Button)View.setOnClickListener -> OnClickListener -> onClick

For Observer Patthern, there are FOUR component: Observer, subscribe, Observable, event. Observer subscribe Observable. when state of Observable changed, observer will know that(subscribe) and trigger its event based on this change.

  • Button -> observable
  • setOnClickListener -> subscribe
  • onClickListener -> Observer
  • onClick -> event

For observer pattern, it is an very useful pattern and we have many needs of it.

In Android development, we can easily to achieve infinite loading feature by observer pattern. For a recyclerview, every time it will show 10 items with data and one more item showing loading layout. In adapter onCreateViewHolder and onBindViewHolder we should implement this way.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == VIEW_TYPE_SHOT) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_shot, parent, false);
return new ShotViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_loading, parent, false);
return new RecyclerView.ViewHolder(view) {};
}
}

@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
if(viewType == VIEW_TYPE_LOADING) {
loadMoreListener.onLoadMore();
} else {
final Shot shot = data.get(position);
// ....
}
}

So everytime when we scrolling to the last item which is VIEWTYPE_LOADING, it will trigger _onLoadMore function. It is like everytime we click button, performClick will triiger onClick function. When we setup adapter, we should implement this loadMoreListenner.onLoadmore function.

1
2
3
4
5
6
7
shotListAdapter = new ShotListAdapter(new ArrayList<Shot>(), new ShotListAdapter.LoadMoreListener() {
@Override
public void onLoadMore() {
int page = shotListAdapter.getDataCount() / COUNT_PRE_PAGE + 1;
AsyncTaskCompat.executeParallel(new LoadShotTask(page));
}
});

This LoadShotTask extends AsyncTask will use RESTful API to ask for next page of shots. This is a simple use of Interface Callback. The entire source code you can check from my Dribbble app.

Another place to use it is that when we want to refresh the content of a fragment, while the refresh button is not inside the fragment. We need to fresh the fragment from its parent activity. We should implement the listener by the observer pattern. Here is the simple example.

Another widely used of observer pattern is the RxJava.