Skip to content

Commit

Permalink
Updating sample application
Browse files Browse the repository at this point in the history
  • Loading branch information
techyourchance committed Aug 13, 2018
1 parent 3958ef1 commit de81022
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 130 deletions.
1 change: 1 addition & 0 deletions sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:name=".SampleApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import android.support.annotation.WorkerThread;

public class SampleDataRetriever {
public class FakeDataRetriever {

@WorkerThread
public String getData() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.techyourchance.threadposters;

import android.support.annotation.UiThread;
import android.support.annotation.WorkerThread;

import com.techyourchance.threadposter.UiThreadPoster;
import com.techyourchance.threadposter.BackgroundThreadPoster;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class FetchDataUseCase {

public interface Listener {
void onDataFetched(String data);
}

private final FakeDataRetriever mFakeDataRetriever;
private final BackgroundThreadPoster mBackgroundThreadPoster;
private final UiThreadPoster mUiThreadPoster;

private final Set<Listener> mListeners = Collections.newSetFromMap(
new ConcurrentHashMap<Listener, Boolean>());

public FetchDataUseCase(FakeDataRetriever fakeDataRetriever,
BackgroundThreadPoster backgroundThreadPoster,
UiThreadPoster uiThreadPoster) {
mFakeDataRetriever = fakeDataRetriever;
mBackgroundThreadPoster = backgroundThreadPoster;
mUiThreadPoster = uiThreadPoster;
}

public void registerListener(Listener listener) {
mListeners.add(listener);
}

public void unregisterListener(Listener listener) {
mListeners.remove(listener);
}

public void fetchData() {
// offload work to background thread
mBackgroundThreadPoster.post(new Runnable() {
@Override
public void run() {
fetchDataSync();
}
});
}

@WorkerThread
private void fetchDataSync() {
final String data = mFakeDataRetriever.getData();
// notify listeners on UI thread
mUiThreadPoster.post(new Runnable() {
@Override
public void run() {
notifySuccess(data);
}
});
}

@UiThread
private void notifySuccess(String data) {
for (Listener listener : mListeners) {
listener.onDataFetched(data);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,68 +7,68 @@
import android.widget.ProgressBar;
import android.widget.TextView;

import com.techyourchance.threadposter.UiThreadPoster;
import com.techyourchance.threadposter.BackgroundThreadPoster;
public class SampleActivity extends AppCompatActivity implements FetchDataUseCase.Listener {

public class SampleActivity extends AppCompatActivity implements SampleWorker.SampleWorkerListener {

private Button mBtnStart;
private Button mBtnFetchData;
private ProgressBar mProgressWorking;
private TextView mTxtDone;

/*
IMPORTANT:
Both BackgroundThreadPoster and UiThreadPoster should be global objects. Can be easily
achieved using dependency injection framework that supports global objects ("singletons")
*/
private final BackgroundThreadPoster mBackgroundThreadPoster = new BackgroundThreadPoster();
private final UiThreadPoster mUiThreadPoster = new UiThreadPoster();

private final SampleDataRetriever mSampleDataRetriever = new SampleDataRetriever();

private TextView mTxtData;

private SampleWorker mSampleWorker;
private FetchDataUseCase mFetchDataUseCase;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);

mBtnStart = (Button) findViewById(R.id.btn_start);
mFetchDataUseCase = ((SampleApplication)getApplication()).getFetchDataUseCase();

mBtnFetchData = (Button) findViewById(R.id.btn_fetch_data);
mProgressWorking = (ProgressBar) findViewById(R.id.progress_working);
mTxtDone = (TextView) findViewById(R.id.txt_done);
mTxtData = (TextView) findViewById(R.id.txt_data);

mBtnStart.setOnClickListener(new View.OnClickListener() {
mBtnFetchData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doWork();
fetchData();
}
});

mSampleWorker = new SampleWorker(mSampleDataRetriever, mBackgroundThreadPoster, mUiThreadPoster);
}

@Override
protected void onStart() {
super.onStart();
mSampleWorker.registerListener(this);
mFetchDataUseCase.registerListener(this);
}

@Override
protected void onStop() {
super.onStop();
mSampleWorker.unregisterListener(this);
mFetchDataUseCase.unregisterListener(this);
}

private void doWork() {
mBtnStart.setVisibility(View.GONE);
mProgressWorking.setVisibility(View.VISIBLE);
mSampleWorker.doWork();
private void fetchData() {
mBtnFetchData.setVisibility(View.GONE);
showProgressIndication();
mFetchDataUseCase.fetchData();
}

@Override
public void onWorkDone(String data) {
public void onDataFetched(String data) {
hideProgressIndication();
showData(data);
}

private void showData(String data) {
mTxtData.setText(data);
mTxtData.setVisibility(View.VISIBLE);
}

private void hideProgressIndication() {
mProgressWorking.setVisibility(View.GONE);
mTxtDone.setVisibility(View.VISIBLE);
}

private void showProgressIndication() {
mProgressWorking.setVisibility(View.VISIBLE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.techyourchance.threadposters;

import android.app.Application;

import com.techyourchance.threadposter.BackgroundThreadPoster;
import com.techyourchance.threadposter.UiThreadPoster;

public class SampleApplication extends Application {

/*
IMPORTANT:
Both BackgroundThreadPoster and UiThreadPoster should be global objects (single instance).
*/
private final BackgroundThreadPoster mBackgroundThreadPoster = new BackgroundThreadPoster();
private final UiThreadPoster mUiThreadPoster = new UiThreadPoster();

private final FakeDataRetriever mFakeDataRetriever = new FakeDataRetriever();
private final FetchDataUseCase mFetchDataUseCase =
new FetchDataUseCase(mFakeDataRetriever, mBackgroundThreadPoster, mUiThreadPoster);

public FetchDataUseCase getFetchDataUseCase() {
return mFetchDataUseCase;
}
}

This file was deleted.

8 changes: 3 additions & 5 deletions sample/src/main/res/layout/activity_sample.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@
android:padding="20dp"
tools:context="com.techyourchance.threadposters.SampleActivity">


<Button
android:id="@+id/btn_start"
android:id="@+id/btn_fetch_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start!"
android:text="Fetch data!"
android:layout_centerInParent="true"/>

<ProgressBar
Expand All @@ -23,10 +22,9 @@
android:visibility="gone"/>

<TextView
android:id="@+id/txt_done"
android:id="@+id/txt_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Done!"
android:layout_centerInParent="true"
android:visibility="gone"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,38 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class SampleWorkerTest {
public class FetchDataUseCaseTest {

private static final String TEST_DATA = "TEST_DATA";

private ThreadPostersTestController mThreadPostersTestController = new ThreadPostersTestController();

private SampleDataRetriever mSampleDataRetrieverMock;
private SampleWorker.SampleWorkerListener mListener1;
private SampleWorker.SampleWorkerListener mListener2;
private FakeDataRetriever mFakeDataRetrieverMock;
private FetchDataUseCase.Listener mListener1;
private FetchDataUseCase.Listener mListener2;

private SampleWorker SUT;
private FetchDataUseCase SUT;

@Before
public void setup() throws Exception {
mSampleDataRetrieverMock = mock(SampleDataRetriever.class);
mFakeDataRetrieverMock = mock(FakeDataRetriever.class);

when(mSampleDataRetrieverMock.getData()).thenReturn(TEST_DATA);
when(mFakeDataRetrieverMock.getData()).thenReturn(TEST_DATA);

SUT = new SampleWorker(
mSampleDataRetrieverMock,
SUT = new FetchDataUseCase(
mFakeDataRetrieverMock,
mThreadPostersTestController.getBackgroundThreadPosterTestDouble(),
mThreadPostersTestController.getUiThreadPosterTestDouble());

mListener1 = mock(SampleWorker.SampleWorkerListener.class);
mListener2 = mock(SampleWorker.SampleWorkerListener.class);
mListener1 = mock(FetchDataUseCase.Listener.class);
mListener2 = mock(FetchDataUseCase.Listener.class);
}

@Test
public void doWork_noListeners_completesWithoutErrors() throws Exception {
// Arrange
// Act
SUT.doWork();
SUT.fetchData();
// Assert
assertThat(true, is(true));
}
Expand All @@ -56,15 +56,15 @@ public void doWork_multipleListeners_notifiedWithCorrectData() throws Exception
SUT.registerListener(mListener2);
ArgumentCaptor<String> ac = ArgumentCaptor.forClass(String.class);
// Act
SUT.doWork();
SUT.fetchData();
// Assert

// needs to be called before assertions in order for all threads to complete and
// all side effects to be present
mThreadPostersTestController.join();

verify(mListener1, times(1)).onWorkDone(ac.capture());
verify(mListener2, times(1)).onWorkDone(ac.capture());
verify(mListener1, times(1)).onDataFetched(ac.capture());
verify(mListener2, times(1)).onDataFetched(ac.capture());
List<String> dataList = ac.getAllValues();
assertThat(dataList.get(0), is(TEST_DATA));
assertThat(dataList.get(1), is(TEST_DATA));
Expand Down

0 comments on commit de81022

Please sign in to comment.