diff --git a/app/build.gradle b/app/build.gradle
index 649471f..8be59da 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,11 +1,11 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 26
+ compileSdkVersion 28
defaultConfig {
applicationId "by.paranoidandroid.threadsexample"
minSdkVersion 14
- targetSdkVersion 26
+ targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -16,11 +16,15 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
}
dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation 'com.android.support:appcompat-v7:26.1.0'
+ implementation 'androidx.appcompat:appcompat:1.0.0-rc01'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
diff --git a/app/src/androidTest/java/by/paranoidandroid/threadsexample/utils/SimpleAsyncTaskImplTest.java b/app/src/androidTest/java/by/paranoidandroid/threadsexample/utils/SimpleAsyncTaskImplTest.java
new file mode 100644
index 0000000..573fbd8
--- /dev/null
+++ b/app/src/androidTest/java/by/paranoidandroid/threadsexample/utils/SimpleAsyncTaskImplTest.java
@@ -0,0 +1,47 @@
+package by.paranoidandroid.threadsexample.utils;
+
+import android.os.Looper;
+
+import org.junit.Test;
+
+import utils.SimpleAsyncTaskImpl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Created by rl98880 on 19/11/2017.
+ * Edited by Ksenia on 13/08/2018.
+ */
+public class SimpleAsyncTaskImplTest {
+ int counter = 0;
+
+ @Test
+ public void execute() throws Exception {
+ new SimpleAsyncTaskImpl() {
+ @Override
+ protected void onPreExecute() {
+ assertTrue(isOnUiThread());
+ assertEquals(counter++, 0);
+ }
+
+ @Override
+ protected Object doInBackground(Object[] obj) {
+ assertFalse(isOnUiThread());
+ assertEquals(counter++, 1);
+ return new Object();
+ }
+
+ @Override
+ protected void onPostExecute(Object obj) {
+ assertTrue(isOnUiThread());
+ assertEquals(counter++, 2);
+ }
+ }.execute();
+ }
+
+ public boolean isOnUiThread() {
+ return Looper.myLooper() == Looper.getMainLooper();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9dd8f3f..205d1f8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -7,5 +7,21 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
- android:theme="@style/AppTheme" />
-
+ android:theme="@style/AppTheme">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/by/paranoidandroid/threadsexample/activities/AsyncTaskActivity.java b/app/src/main/java/by/paranoidandroid/threadsexample/activities/AsyncTaskActivity.java
new file mode 100644
index 0000000..b0ff285
--- /dev/null
+++ b/app/src/main/java/by/paranoidandroid/threadsexample/activities/AsyncTaskActivity.java
@@ -0,0 +1,144 @@
+package by.paranoidandroid.threadsexample.activities;
+
+import android.app.Activity;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import by.paranoidandroid.threadsexample.R;
+import utils.IAsyncTaskEvents;
+
+import static android.os.AsyncTask.Status.FINISHED;
+import static android.os.AsyncTask.Status.PENDING;
+import static android.os.AsyncTask.Status.RUNNING;
+
+/**
+ * Activity that demonstrates work with AsyncTask.
+ */
+
+public class AsyncTaskActivity extends Activity implements IAsyncTaskEvents {
+ private final String STATE_COUNTER = "COUNTER",
+ STATE_RUNNING = "RUNNING",
+ STATE_TEXTVIEW = "TEXTVIEW";
+ private TextView textView;
+ private int counter;
+ private boolean isRunning;
+ CounterAsyncTask counterAsyncTask;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.actvity_asynctask);
+
+ Button createBtn = findViewById(R.id.btn_create);
+ Button startBtn = findViewById(R.id.btn_start);
+ Button cancelBtn = findViewById(R.id.btn_cancel);
+ textView = findViewById(R.id.text_view_counter);
+
+ if (savedInstanceState != null) {
+ textView.setText(savedInstanceState.getString(STATE_TEXTVIEW, ""));
+ isRunning = savedInstanceState.getBoolean(STATE_RUNNING);
+ if (isRunning) {
+ counter = savedInstanceState.getInt(STATE_COUNTER);
+ counterAsyncTask = new CounterAsyncTask(AsyncTaskActivity.this);
+ isRunning = true;
+ counterAsyncTask.execute(counter);
+ }
+ }
+
+ View.OnClickListener btnListener = view -> {
+ switch (view.getId()) {
+ case R.id.btn_create:
+ if (counterAsyncTask == null
+ || counterAsyncTask.isCancelled()
+ || counterAsyncTask.getStatus() == FINISHED) {
+ counterAsyncTask = new CounterAsyncTask(AsyncTaskActivity.this);
+ }
+ break;
+ case R.id.btn_start:
+ if (counterAsyncTask != null
+ && counterAsyncTask.getStatus() == PENDING) {
+ isRunning = true;
+ counterAsyncTask.execute();
+ }
+ break;
+ case R.id.btn_cancel:
+ if (counterAsyncTask != null) {
+ isRunning = false;
+ counterAsyncTask.cancel(true);
+ counterAsyncTask = null;
+ }
+ break;
+ }
+ };
+
+ createBtn.setOnClickListener(btnListener);
+ startBtn.setOnClickListener(btnListener);
+ cancelBtn.setOnClickListener(btnListener);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ if (counterAsyncTask != null && counterAsyncTask.getStatus() == RUNNING) {
+ counterAsyncTask.cancel(true);
+ counterAsyncTask = null;
+ }
+ outState.putInt(STATE_COUNTER, counter);
+ outState.putBoolean(STATE_RUNNING, isRunning);
+ outState.putString(STATE_TEXTVIEW, textView.getText().toString());
+
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onProgressUpdate(Integer counter) {
+ this.counter = counter;
+ textView.setText(String.valueOf(counter));
+ }
+
+ @Override
+ public void onPostExecute() {
+ textView.setText(getString(R.string.done));
+ isRunning = true;
+ }
+
+ static class CounterAsyncTask extends AsyncTask {
+ private final static String LOG_TAG = "CounterAsyncTask";
+ private final static int MAX_COUNT = 10, TIMEOUT = 500;
+ private int counter = 1;
+ private IAsyncTaskEvents listener;
+
+ CounterAsyncTask(IAsyncTaskEvents events) {
+ listener = events;
+ }
+
+ @Override
+ protected void onProgressUpdate(Integer... values) {
+ listener.onProgressUpdate(values[0]);
+ }
+
+ @Override
+ protected Void doInBackground(Integer... values) {
+ if (values.length > 0 && values[0] != null) {
+ counter = values[0];
+ counter++;
+ }
+ while (counter <= MAX_COUNT) {
+ publishProgress(counter++);
+ try {
+ Thread.sleep(TIMEOUT);
+ } catch (Exception ex) {
+ Log.e(LOG_TAG, "Exception: ", ex);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void s) {
+ listener.onPostExecute();
+ }
+ }
+}
diff --git a/app/src/main/java/by/paranoidandroid/threadsexample/activities/LoaderActivity.java b/app/src/main/java/by/paranoidandroid/threadsexample/activities/LoaderActivity.java
new file mode 100644
index 0000000..1e2e8d3
--- /dev/null
+++ b/app/src/main/java/by/paranoidandroid/threadsexample/activities/LoaderActivity.java
@@ -0,0 +1,75 @@
+package by.paranoidandroid.threadsexample.activities;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+import by.paranoidandroid.threadsexample.R;
+import utils.CounterLoader;
+
+/**
+ * Activity that demonstrates work with Loader.
+ */
+
+public class LoaderActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks {
+ final int COUNTER_LOADER_ID = 0, MAX_COUNT = 10;
+ Button startBtn, cancelBtn;
+ TextView textView;
+ View.OnClickListener btnListener;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_loader);
+
+ startBtn = findViewById(R.id.btn_start);
+ cancelBtn = findViewById(R.id.btn_cancel);
+ textView = findViewById(R.id.text_view_counter);
+
+ if (savedInstanceState != null) {
+ if (LoaderManager.getInstance(LoaderActivity.this).getLoader(COUNTER_LOADER_ID) != null) {
+ // Prepare the loader. Either re-connect with an existing one, or start a new one.
+ LoaderManager.getInstance(LoaderActivity.this)
+ .initLoader(COUNTER_LOADER_ID, null, LoaderActivity.this);
+ }
+ }
+
+ btnListener = view -> {
+ switch (view.getId()) {
+ case R.id.btn_start:
+ // Prepare the loader. Either re-connect with an existing one, or start a new one.
+ LoaderManager.getInstance(LoaderActivity.this)
+ .initLoader(COUNTER_LOADER_ID, null, LoaderActivity.this);
+ break;
+ case R.id.btn_cancel:
+ LoaderManager.getInstance(LoaderActivity.this)
+ .destroyLoader(COUNTER_LOADER_ID);
+ break;
+ }
+ };
+
+ startBtn.setOnClickListener(btnListener);
+ cancelBtn.setOnClickListener(btnListener);
+ }
+
+
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ textView.setText("");
+ return new CounterLoader(this);
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, Integer data) {
+ if (data > MAX_COUNT) {
+ textView.setText(getString(R.string.done));
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader) {}
+}
diff --git a/app/src/main/java/by/paranoidandroid/threadsexample/activities/MainActivity.java b/app/src/main/java/by/paranoidandroid/threadsexample/activities/MainActivity.java
new file mode 100644
index 0000000..30e846c
--- /dev/null
+++ b/app/src/main/java/by/paranoidandroid/threadsexample/activities/MainActivity.java
@@ -0,0 +1,53 @@
+package by.paranoidandroid.threadsexample.activities;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import by.paranoidandroid.threadsexample.R;
+
+/**
+ * Main activity with three buttons.
+ */
+
+public class MainActivity extends AppCompatActivity {
+ Button asynctaskBtn, loaderBtn, threadsBtn;
+ View.OnClickListener btnListener;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ asynctaskBtn = findViewById(R.id.btn_asynctask);
+ loaderBtn = findViewById(R.id.btn_loader);
+ threadsBtn = findViewById(R.id.btn_threads);
+
+ btnListener = new View.OnClickListener() {
+ Intent intent;
+ @Override
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.btn_asynctask:
+ intent = new Intent(MainActivity.this, AsyncTaskActivity.class);
+ break;
+ case R.id.btn_loader:
+ intent = new Intent(MainActivity.this, LoaderActivity.class);
+ break;
+ case R.id.btn_threads:
+ intent = new Intent(MainActivity.this, ThreadsActivity.class);
+ break;
+ }
+ startActivity(intent);
+ }
+ };
+
+ asynctaskBtn.setOnClickListener(btnListener);
+ loaderBtn.setOnClickListener(btnListener);
+ threadsBtn.setOnClickListener(btnListener);
+ }
+
+}
diff --git a/app/src/main/java/by/paranoidandroid/threadsexample/activities/ThreadsActivity.java b/app/src/main/java/by/paranoidandroid/threadsexample/activities/ThreadsActivity.java
new file mode 100644
index 0000000..0fe7aed
--- /dev/null
+++ b/app/src/main/java/by/paranoidandroid/threadsexample/activities/ThreadsActivity.java
@@ -0,0 +1,149 @@
+package by.paranoidandroid.threadsexample.activities;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import by.paranoidandroid.threadsexample.R;
+import utils.IAsyncTaskEvents;
+import utils.SimpleAsyncTaskImpl;
+
+import static utils.SimpleAsyncTask.Status.FINISHED;
+import static utils.SimpleAsyncTask.Status.PENDING;
+import static utils.SimpleAsyncTask.Status.RUNNING;
+
+/**
+ * Activity that demonstrates advanced work with threads.
+ */
+
+public class ThreadsActivity extends Activity implements IAsyncTaskEvents {
+ private final String STATE_COUNTER = "COUNTER",
+ STATE_RUNNING = "RUNNING",
+ STATE_TEXTVIEW = "TEXTVIEW";
+ private Button createBtn, startBtn, cancelBtn;
+ private TextView textView;
+ private View.OnClickListener btnListener;
+ private int counter;
+ private boolean isRunning;
+ ThreadsActivity.CounterAsyncTask counterAsyncTask;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.actvity_asynctask);
+
+ createBtn = findViewById(R.id.btn_create);
+ startBtn = findViewById(R.id.btn_start);
+ cancelBtn = findViewById(R.id.btn_cancel);
+ textView = findViewById(R.id.text_view_counter);
+
+ if (savedInstanceState != null) {
+ textView.setText(savedInstanceState.getString(STATE_TEXTVIEW, ""));
+ isRunning = savedInstanceState.getBoolean(STATE_RUNNING);
+ if (isRunning) {
+ counter = savedInstanceState.getInt(STATE_COUNTER);
+ counterAsyncTask = new ThreadsActivity.CounterAsyncTask(ThreadsActivity.this);
+ isRunning = true;
+ counterAsyncTask.execute(counter);
+ }
+ }
+
+ btnListener = view -> {
+ switch (view.getId()) {
+ case R.id.btn_create:
+ if (counterAsyncTask == null
+ || counterAsyncTask.isCancelled()
+ || counterAsyncTask.getStatus() == FINISHED) {
+ counterAsyncTask = new CounterAsyncTask(ThreadsActivity.this);
+ }
+ break;
+ case R.id.btn_start:
+ if (counterAsyncTask != null
+ && counterAsyncTask.getStatus() == PENDING) {
+ isRunning = true;
+ counterAsyncTask.execute();
+ }
+ break;
+ case R.id.btn_cancel:
+ if (counterAsyncTask != null) {
+ isRunning = false;
+ counterAsyncTask.cancel();
+ counterAsyncTask = null;
+ }
+ break;
+ }
+ };
+
+ createBtn.setOnClickListener(btnListener);
+ startBtn.setOnClickListener(btnListener);
+ cancelBtn.setOnClickListener(btnListener);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ if (counterAsyncTask != null && counterAsyncTask.getStatus() == RUNNING) {
+ counterAsyncTask.cancel();
+ counterAsyncTask = null;
+ }
+ outState.putInt(STATE_COUNTER, counter);
+ outState.putBoolean(STATE_RUNNING, isRunning);
+ outState.putString(STATE_TEXTVIEW, textView.getText().toString());
+
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onProgressUpdate(Integer counter) {
+ this.counter = counter;
+ textView.setText(String.valueOf(counter));
+ }
+
+ @Override
+ public void onPostExecute() {
+ textView.setText(getString(R.string.done));
+ isRunning = true;
+ }
+
+ static class CounterAsyncTask extends SimpleAsyncTaskImpl {
+ private final static String LOG_TAG = "Own AsyncTask";
+ private final static int MAX_COUNT = 10, TIMEOUT = 500;
+ private int counter = 1;
+ private IAsyncTaskEvents listener;
+
+ CounterAsyncTask(IAsyncTaskEvents events) {
+ listener = events;
+ }
+
+ @Override
+ protected void onProgressUpdate(Integer... values) {
+ listener.onProgressUpdate(values[0]);
+ }
+
+ @Override
+ protected Void doInBackground(Integer... values) {
+ if (values != null && values[0] != null) {
+ counter = values[0];
+ counter++;
+ }
+ while (counter <= MAX_COUNT) {
+ if (!isCancelled()) {
+ publishProgress(counter++);
+ try {
+ Thread.sleep(TIMEOUT);
+ } catch (Exception ex) {
+ Log.e(LOG_TAG, "Exception: ", ex);
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void s) {
+ listener.onPostExecute();
+ }
+ }
+}
diff --git a/app/src/main/java/utils/CounterLoader.java b/app/src/main/java/utils/CounterLoader.java
new file mode 100644
index 0000000..39e75b4
--- /dev/null
+++ b/app/src/main/java/utils/CounterLoader.java
@@ -0,0 +1,54 @@
+package utils;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.loader.content.AsyncTaskLoader;
+
+public class CounterLoader extends AsyncTaskLoader {
+ private final static String LOG_TAG = "CounterLoader";
+ private final static int MAX_COUNT = 10, TIMEOUT = 500;
+ private int counter = 0;
+
+ public CounterLoader(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onStartLoading() {
+ super.onStartLoading();
+ if (counter != 0) {
+ deliverResult(counter);
+ } else if (isStarted()) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ public Integer loadInBackground() {
+ counter++;
+ while (counter <= MAX_COUNT) {
+ if (!isLoadInBackgroundCanceled()) {
+ Log.e(LOG_TAG, "Counter: " + counter);
+ counter++;
+ try {
+ Thread.sleep(TIMEOUT);
+ } catch (Exception ex) {
+ Log.e(LOG_TAG, "Exception: ", ex);
+ }
+ }
+ }
+ return counter;
+ }
+
+ @Override
+ public void deliverResult(Integer data) {
+ counter = data;
+ super.deliverResult(data);
+ }
+
+ @Override
+ protected void onStopLoading() {
+ super.onStopLoading();
+ }
+}
diff --git a/app/src/main/java/utils/IAsyncTaskEvents.java b/app/src/main/java/utils/IAsyncTaskEvents.java
new file mode 100644
index 0000000..94a7892
--- /dev/null
+++ b/app/src/main/java/utils/IAsyncTaskEvents.java
@@ -0,0 +1,11 @@
+package utils;
+
+/**
+ * Interface that helps to communicate between the task (the worker thread)
+ * and the Activity (UI thread).
+ */
+
+public interface IAsyncTaskEvents {
+ void onProgressUpdate(Integer counter);
+ void onPostExecute();
+}
diff --git a/app/src/main/java/utils/SimpleAsyncTask.java b/app/src/main/java/utils/SimpleAsyncTask.java
new file mode 100644
index 0000000..837d2eb
--- /dev/null
+++ b/app/src/main/java/utils/SimpleAsyncTask.java
@@ -0,0 +1,43 @@
+package utils;
+
+/**
+ * Abstract class that is interface for own implementation of AsyncTask with java threads
+ * and communication to the UI thread's’ Handler.
+ */
+
+public abstract class SimpleAsyncTask {
+ public enum Status {
+ FINISHED,
+ PENDING,
+ RUNNING
+ }
+
+ volatile Status status;
+ volatile boolean isCancelled = false;
+
+ public final Status getStatus() {
+ return status;
+ }
+
+ protected abstract void onPreExecute();
+
+ protected abstract Result doInBackground(Params[] params);
+
+ protected abstract void onPostExecute(Result result);
+
+ protected abstract void execute();
+
+ protected final void publishProgress(Progress... values) {
+ onProgressUpdate(values);
+ }
+
+ protected abstract void onProgressUpdate(Progress... values);
+
+ protected abstract void cancel();
+
+ protected abstract void onCancelled();
+
+ public final boolean isCancelled() {
+ return isCancelled;
+ }
+}
diff --git a/app/src/main/java/utils/SimpleAsyncTaskImpl.java b/app/src/main/java/utils/SimpleAsyncTaskImpl.java
new file mode 100644
index 0000000..ca07ea0
--- /dev/null
+++ b/app/src/main/java/utils/SimpleAsyncTaskImpl.java
@@ -0,0 +1,79 @@
+package utils;
+
+import android.os.Handler;
+import android.os.Looper;
+
+/**
+ * Own implementation of AsyncTask with java threads
+ * and communication to the UI thread's’ Handler.
+ */
+
+public class SimpleAsyncTaskImpl extends SimpleAsyncTask {
+ private Handler handler;
+ private Params[] params;
+
+ protected SimpleAsyncTaskImpl() {
+ status = Status.PENDING;
+ handler = new Handler(Looper.getMainLooper());
+ }
+
+ @Override
+ protected void onPreExecute() {}
+
+ @Override
+ protected Result doInBackground(Params[] params) {
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Result o) {}
+
+ @Override
+ public void execute() {
+ status = Status.RUNNING;
+
+ handler.post(this::onPreExecute);
+
+ new Thread(() -> {
+ if (isCancelled) {
+ handler.post(this::onCancelled);
+ } else {
+ Result result = doInBackground(params);
+ handler.post(() -> {
+ if (isCancelled) {
+ onCancelled();
+ } else {
+ onPostExecute(result);
+ }
+ });
+ }
+ status = Status.FINISHED;
+ }).start();
+ }
+
+ public void execute(Params... values) {
+ params = values;
+ execute();
+ }
+
+ @Override
+ protected void onProgressUpdate(Progress... values) { }
+
+ /**
+ * Invoking this method will cause subsequent calls to {@link #isCancelled()} to return true.
+ * After invoking this method, {@link #onCancelled()}, instead of
+ * {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])}
+ * returns.
+ * IMPORTANT TO NOTE:
+ * To ensure that a task is cancelled as quickly as possible, you should always
+ * check the return value of {@link #isCancelled()} periodically from
+ * {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)
+ *
+ */
+ @Override
+ public void cancel() {
+ isCancelled = true;
+ }
+
+ protected void onCancelled() {}
+}
diff --git a/app/src/main/res/layout/activity_loader.xml b/app/src/main/res/layout/activity_loader.xml
new file mode 100644
index 0000000..d0313cb
--- /dev/null
+++ b/app/src/main/res/layout/activity_loader.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..8356c86
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/actvity_asynctask.xml b/app/src/main/res/layout/actvity_asynctask.xml
new file mode 100644
index 0000000..cc07d5e
--- /dev/null
+++ b/app/src/main/res/layout/actvity_asynctask.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 3ab3e9c..4ff6a83 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -3,4 +3,8 @@
#3F51B5
#303F9F
#FF4081
+
+ #8AD1D5
+ #159588
+ #A7549C
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..3b6535e
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 4dp
+ 16dp
+ 32dp
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e123e73..821da52 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,13 @@
ThreadsExample
+
+ ASYNCTASK ACTIVITY
+ LOADER ACTIVITY
+ THREADS ACTIVITY
+
+ Create
+ Start
+ Cancel
+
+ Done!