Showing posts with label Android code sample: thread. Show all posts
Showing posts with label Android code sample: thread. Show all posts

Load animated GIF from Internet

Previous examples show how to diaply animated GIF using decodeStream(InputStream) and decodeByteArray(InputStream) loaded from  /res/drawable/ folder. This example show how to load from Internet.



  • We cannot access Internet in main thread, such that we have to implement a background thread to load the gif from Internet.
  • Once loaded, we have to ask Android system to re-layout with updated graph.
  • In the example code, dummy delay is added to simulate network delay.
  • Both decodeStream(InputStream) and decodeByteArray(InputStream) are implemented, you can choice it by setting DECODE_STREAM true or false.
  • It can be noted that textViewInfo is filled with 0, 0 x 0; because the GIF is not loaded when onCreate() called.
  • uses-permission android:name="android.permission.INTERNET" is needed in AndroidManifest.xml
Modify activity_main.xml to added a LinearLayout to show how the system layout at run-time.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.androidgif.MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/android_er" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ff000090"
android:orientation="vertical"
android:padding="5dp" >

<com.example.androidgif.GifView
android:id="@+id/gifview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>

<TextView
android:id="@+id/textinfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="info..." />

</LinearLayout>

GifView.java
package com.example.androidgif;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Toast;

public class GifView extends View {

//Set true to use decodeStream
//Set false to use decodeByteArray
private static final boolean DECODE_STREAM = true;

private InputStream gifInputStream;
private Movie gifMovie;
private int movieWidth, movieHeight;
private long movieDuration;
private long mMovieStart;

final static String gifAddr = "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3_nQgi9UVrThC6nVpZvnd0GbCPiqA5WcS3e-nztI_ztbzaYVlI2J_7CunvtZAaS3sz86HgusyyMEKYKsMGTtidvs2tinEo97MXRXIHtmdMwfeBpiWhXRa3FLbP_vKdLLPZyS8v7I4ZYg/s1600/android_er.gif";

public GifView(Context context) {
super(context);
init(context);
}

public GifView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

public GifView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}

private void init(final Context context){
setFocusable(true);

gifMovie = null;
movieWidth = 0;
movieHeight = 0;
movieDuration = 0;

Thread threadLoadGif = new Thread(new Runnable(){

@Override
public void run() {
try {
URL gifURL = new URL(gifAddr);

HttpURLConnection connection = (HttpURLConnection)gifURL.openConnection();
gifInputStream = connection.getInputStream();

//Insert dummy sleep
//to simulate network delay
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

if(DECODE_STREAM){
gifMovie = Movie.decodeStream(gifInputStream);
}else{
byte[] array = streamToBytes(gifInputStream);
gifMovie = Movie.decodeByteArray(array, 0, array.length);
}

movieWidth = gifMovie.width();
movieHeight = gifMovie.height();
movieDuration = gifMovie.duration();

((MainActivity)context).runOnUiThread(new Runnable(){

@Override
public void run() {
//request re-draw layout
invalidate();
requestLayout();
Toast.makeText(context,
movieWidth + " x " + movieHeight + "\n"
+ movieDuration,
Toast.LENGTH_LONG).show();
}});

} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}});

threadLoadGif.start();

}

private static byte[] streamToBytes(InputStream is) {
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
byte[] buffer = new byte[1024];
int len;
try {
while ((len = is.read(buffer)) >= 0) {
os.write(buffer, 0, len);
}
} catch (java.io.IOException e) {
}
return os.toByteArray();
}

@Override
protected void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
setMeasuredDimension(movieWidth, movieHeight);
}

public int getMovieWidth(){
return movieWidth;
}

public int getMovieHeight(){
return movieHeight;
}

public long getMovieDuration(){
return movieDuration;
}

@Override
protected void onDraw(Canvas canvas) {

long now = android.os.SystemClock.uptimeMillis();
if (mMovieStart == 0) { // first time
mMovieStart = now;
}

if (gifMovie != null) {

int dur = gifMovie.duration();
if (dur == 0) {
dur = 1000;
}

int relTime = (int)((now - mMovieStart) % dur);

gifMovie.setTime(relTime);

gifMovie.draw(canvas, 0, 0);
invalidate();

}

}

}

Other files, MainActivity.java and AndroidManifest.xml to turn OFF hardwareAccelerated, refer to the post "Play animated GIF using android.graphics.Movie, with Movie.decodeStream(InputStream)".

download filesDownload the files.


Try Motion aftereffect on Android

The motion aftereffect (MAE) is a visual illusion experienced after viewing a moving visual stimulus for a time (tens of milliseconds to minutes) with stationary eyes, and then fixating a stationary stimulus. The stationary stimulus appears to move in the opposite direction to the original (physically moving) stimulus. The motion aftereffect is believed to be the result of motion adaptation. ~ Wikipedia.

This exercise TRY to simulate the effect on Android. The code for motion aftereffect generation is modified from http://en.wikipedia.org/wiki/File:Illusion_movie.ogg (c source code), with my optimization. But the effect seem not good! I test it on Nexus 7 tablet.

To see the illusions: Run the app on Android Tablet, and stare to the center of the image. After around one minute, look away (for example look to a face or to your hands). For few seconds everything you see will appear to distort. Or touch the motion picture on screen, it will stop and change to another ImageView.

motion aftereffect (MAE)
Motion AfterEffect (MAE)

The main part is in MySurfaceView.java.
package com.example.androidsurfaceview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView {

private SurfaceHolder surfaceHolder;
private MyThread myThread;

MainActivity mainActivity;

int T;
static final float freq = 80;

public MySurfaceView(Context context) {
super(context);
init(context);
}

public MySurfaceView(Context context,
AttributeSet attrs) {
super(context, attrs);
init(context);
}

public MySurfaceView(Context context,
AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}

private void init(Context c){
mainActivity = (MainActivity)c;
T = 0;
myThread = new MyThread(this);

surfaceHolder = getHolder();


surfaceHolder.addCallback(new SurfaceHolder.Callback(){

@Override
public void surfaceCreated(SurfaceHolder holder) {
myThread.setRunning(true);
myThread.start();
}

@Override
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height) {
// TODO Auto-generated method stub

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
myThread.setRunning(false);
while (retry) {
try {
myThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}});
}

protected void drawSomething(Canvas canvas) {

int sizex = getWidth();
int sizey = getHeight();

float divby_sizex_sq = 1.0f/((float)sizex * (float)sizex);

int[] data = new int[sizex * sizey];
int m;

T++;
if(T >= 1200){
T = 0;
}
float halfT = T * 0.5f;

for (int j=0;j<sizey;j++){

float y0 = j*2-sizey;
float y2_2 = y0 * y0 * divby_sizex_sq;

float absY = Math.abs(y0/(float)sizey);
float halfT_plus_absY = absY + halfT;
float halfT_neg_plus_absY = absY - halfT;

int j_multi_sizex = j*sizex;

for (int i=0;i<sizex;i++){
float x=(i*2-sizex)/(float)sizex;

//0.2 instead of 0.1 to have a bigger circle
if ((x*x + y2_2)<0.2){
m = (int) (Math.sin((halfT_plus_absY+Math.abs(x))*freq)*127.0+128) & 0xFF;
}else {
m = (int) (Math.sin((halfT_neg_plus_absY+Math.abs(x))*freq)*127.0+128) & 0xFF;
}

data[i+j_multi_sizex] = 0xFF000000
+ (m << 16)
+ (m << 8)
+ m;
}
}

Bitmap bm = Bitmap.createBitmap(data, sizex, sizey, Bitmap.Config.ARGB_8888);

canvas.drawBitmap(bm, 0, 0, null);

}
}

MyThread.java
package com.example.androidsurfaceview;

import android.graphics.Canvas;

public class MyThread extends Thread {

MySurfaceView myView;
private boolean running = false;

public MyThread(MySurfaceView view) {
myView = view;
}

public void setRunning(boolean run) {
running = run;
}

@Override
public void run() {
while(running){

Canvas canvas = myView.getHolder().lockCanvas();

if(canvas != null){
synchronized (myView.getHolder()) {
myView.drawSomething(canvas);
}
myView.getHolder().unlockCanvasAndPost(canvas);
}

/*
try {
sleep(30);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
*/

}
}

}

/res/layout/activity_main.xml, where @drawable/android_er is a 640x480 .png in /res/drawable/ folder.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@android:color/background_dark"
tools:context="com.example.androidsurfaceview.MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<FrameLayout
android:layout_width="640dp"
android:layout_height="480dp"
android:layout_gravity="center">
<com.example.androidsurfaceview.MySurfaceView
android:id="@+id/myview"
android:layout_width="640dp"
android:layout_height="480dp"
android:layout_gravity="center" />
<ImageView
android:id="@+id/imageicon"
android:layout_width="640dp"
android:layout_height="480dp"
android:layout_gravity="center"
android:src="@drawable/android_er"
android:visibility="invisible"/>
</FrameLayout>

</LinearLayout>

MainActivity.java. The Motion graph will run when the app start. When user touch on it, it will show the ImageView of "@drawable/android_er".
package com.example.androidsurfaceview;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;

public class MainActivity extends Activity {

ImageView imageIcon;
MySurfaceView mySurfaceView;

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

mySurfaceView = (MySurfaceView)findViewById(R.id.myview);
imageIcon = (ImageView)findViewById(R.id.imageicon);

mySurfaceView.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View v) {
imageIcon.setVisibility(View.VISIBLE);
mySurfaceView.setVisibility(View.INVISIBLE);
}});

}

}

Modify AndroidManifest.xml to force the app run in landscape mode.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.androidsurfaceview"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.androidsurfaceview.MainActivity"
android:label="@string/app_name"
android:screenOrientation="landscape" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>



download filesDownload the files.

Download and try the APK.

Create animation on SurfaceView in background Thread

Last post "Simple SurfaceView example" draw bitmap in surfaceCreated() callback. In this step, a customized Thread, MyThread, is implemented to draw the bitmap running across screen in background thread.


MyThread.java
package com.example.androidsurfaceview;

import android.graphics.Canvas;

public class MyThread extends Thread {

MySurfaceView myView;
private boolean running = false;

public MyThread(MySurfaceView view) {
myView = view;
}

public void setRunning(boolean run) {
running = run;
}

@Override
public void run() {
while(running){

Canvas canvas = myView.getHolder().lockCanvas();

if(canvas != null){
synchronized (myView.getHolder()) {
myView.drawSomething(canvas);
}
myView.getHolder().unlockCanvasAndPost(canvas);
}

try {
sleep(30);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}

}

Modify MySurfaceView.java in last post.
package com.example.androidsurfaceview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView {

private SurfaceHolder surfaceHolder;
private Bitmap bmpIcon;
private MyThread myThread;
int xPos = 0;
int yPos = 0;
int deltaX = 5;
int deltaY = 5;
int iconWidth;
int iconHeight;

public MySurfaceView(Context context) {
super(context);
init();
}

public MySurfaceView(Context context,
AttributeSet attrs) {
super(context, attrs);
init();
}

public MySurfaceView(Context context,
AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}

private void init(){

myThread = new MyThread(this);

surfaceHolder = getHolder();
bmpIcon = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);

iconWidth = bmpIcon.getWidth();
iconHeight = bmpIcon.getHeight();

surfaceHolder.addCallback(new SurfaceHolder.Callback(){

@Override
public void surfaceCreated(SurfaceHolder holder) {
myThread.setRunning(true);
myThread.start();
}

@Override
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height) {
// TODO Auto-generated method stub

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
myThread.setRunning(false);
while (retry) {
try {
myThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}});
}

protected void drawSomething(Canvas canvas) {
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(bmpIcon,
getWidth()/2, getHeight()/2, null);

xPos += deltaX;
if(deltaX > 0){
if(xPos >= getWidth() - iconWidth){
deltaX *= -1;
}
}else{
if(xPos <= 0){
deltaX *= -1;
}
}

yPos += deltaY;
if(deltaY > 0){
if(yPos >= getHeight() - iconHeight){
deltaY *= -1;
}
}else{
if(yPos <= 0){
deltaY *= -1;
}
}

canvas.drawColor(Color.BLACK);
canvas.drawBitmap(bmpIcon,
xPos, yPos, null);

}

}

Other files, /res/layout/activity_main.xml and MainActivity.java, refer to last post.

download filesDownload the files.

Next:
Draw bitmap programmatically for SurfaceView

Solve Producer–consumer problem with wait() and notifyAll()

It's a example to solve the Producer–consumer problem with wait() and notifyAll().

In computing, the producer–consumer problem (also known as the bounded-buffer problem) is a classic example of a multi-process synchronization problem. The problem describes two processes, the producer and the consumer, who share a common, fixed-size buffer used as a queue. The producer's job is to generate a piece of data, put it into the buffer and start again. At the same time, the consumer is consuming the data (i.e., removing it from the buffer) one piece at a time. The problem is to make sure that the producer won't try to add data into the buffer if it's full and that the consumer won't try to remove data from an empty buffer. ~ http://en.wikipedia.org/wiki/Producer-consumer

Solve Producer–consumer problem
Solve Producer–consumer problem

package com.example.androidthread;

import java.util.LinkedList;
import java.util.Random;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

Button buttonStart;
TextView textInfoProducer, textInfoConsumer;

String infoMsgProducer;
String infoMsgConsumer;

ShareClass shareObj = new ShareClass();
long startingTime;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonStart = (Button) findViewById(R.id.buttonstart);
textInfoProducer = (TextView) findViewById(R.id.infoproducer);
textInfoConsumer = (TextView) findViewById(R.id.infoconsumer);

buttonStart.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {

infoMsgProducer = "Producer\n";
infoMsgConsumer = "Consumer\n";

Thread threadProducer = new Thread(new Runnable() {

@Override
public void run() {

int ele = 0;
Random random = new Random();

while (true) {
String strEle = String.valueOf(ele);
infoMsgProducer += strEle + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoProducer.setText(infoMsgProducer);
}
});

shareObj.produce(String.valueOf(ele));
long randomDelay = 500 + random.nextInt(1000);

try {
Thread.sleep(randomDelay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ele++;
}
}
});

Thread threadConsumer = new Thread(new Runnable() {

@Override
public void run() {

while (true) {
Random random = new Random();

while (true) {
infoMsgConsumer += shareObj.consume() + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoConsumer.setText(infoMsgConsumer);
}});

long randomDelay = 500 + random.nextInt(1000);

try {
Thread.sleep(randomDelay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
});

startingTime = System.currentTimeMillis();
threadProducer.start();
threadConsumer.start();
}
});

}

public class ShareClass {

final int BUFFER_MAX = 5;
LinkedList<String> buffer;

ShareClass(){
buffer = new LinkedList<String>();
}

public synchronized void produce(String element){

while(buffer.size() == BUFFER_MAX){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
buffer.offer(element);

notifyAll();
}

public synchronized String consume(){

while(buffer.size() == 0){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

String element = buffer.poll();
notifyAll();
return element;
}
}

}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<Button
android:id="@+id/buttonstart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="start()" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >

<TextView
android:id="@+id/infoproducer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />

<TextView
android:id="@+id/infoconsumer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />

</LinearLayout>

</LinearLayout>




- More example about Thread

Example of using Lock/ReentrantLock

java.util.concurrent.locks.Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements.
java.util.concurrent.locks.ReentrantLock is a reentrant mutual exclusion Lock with the same basic behavior and semantics as the implicit monitor lock accessed using synchronized methods and statements, but with extended capabilities.

This example have the same function of the More example of Synchronized Statements with separate objects for locking (the 2nd example), implement with Lock.


package com.example.androidthread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

Button buttonStart;
TextView textInfoA, textInfoB, textInfoC, textInfoD;
TextView textDuration1, textDuration2;
TextView textDuration3, textDuration4;

String infoMsgA;
String infoMsgB;
String infoMsgC;
String infoMsgD;

ShareClass shareObj = new ShareClass(10);
long startingTime;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonStart = (Button) findViewById(R.id.buttonstart);
textInfoA = (TextView) findViewById(R.id.infoa);
textInfoB = (TextView) findViewById(R.id.infob);
textInfoC = (TextView) findViewById(R.id.infoc);
textInfoD = (TextView) findViewById(R.id.infod);
textDuration1 = (TextView) findViewById(R.id.duration1);
textDuration2 = (TextView) findViewById(R.id.duration2);
textDuration3 = (TextView) findViewById(R.id.duration3);
textDuration4 = (TextView) findViewById(R.id.duration4);

buttonStart.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {

infoMsgA = "Thread A\n";
infoMsgB = "Thread B\n";
infoMsgC = "Thread C\n";
infoMsgD = "Thread D\n";
textInfoA.setText(infoMsgA);
textInfoB.setText(infoMsgB);
textInfoC.setText(infoMsgC);
textInfoD.setText(infoMsgD);

Thread thread1 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter1() > 0) {

//caution:
//shareObj.counter1 may change here

infoMsgA += "A 1: "
+ shareObj.delayDecCounter1(500) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoA.setText(infoMsgA);
}

});

} else {
stop = true;
final long endTime1 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration1.setText("Duration 1 (reference only): "
+ (endTime1 - startingTime));
}

});
}
}
}
});

Thread thread2 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter2() > 0) {

//caution:
//shareObj.counter2 may change here

infoMsgB += "B 2: "
+ shareObj.delayDecCounter2(500) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoB.setText(infoMsgB);
}

});

} else {
stop = true;
final long endTime2 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration2.setText("Duration 2 (reference only): "
+ (endTime2 - startingTime));
}

});
}
}
}
});

//
Thread thread3 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter1() > 0) {

//caution:
//shareObj.counter1 may change here

infoMsgC += "C 1: "
+ shareObj.delayDecCounter1(200) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoC.setText(infoMsgC);
}

});

} else {
stop = true;
final long endTime3 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration3.setText("Duration 3 (reference only): "
+ (endTime3 - startingTime));
}

});
}
}
}
});

Thread thread4 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter2() > 0) {

//caution:
//shareObj.counter2 may change here

infoMsgD += "D 2: "
+ shareObj.delayDecCounter2(1100) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoD.setText(infoMsgD);
}

});

} else {
stop = true;
final long endTime4 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration4.setText("Duration 4 (reference only): "
+ (endTime4 - startingTime));
}

});
}
}
}
});
//

startingTime = System.currentTimeMillis();
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
});

}

public class ShareClass {

int counter1;
int counter2;

//Object lock1;
//Object lock2;
Lock lock1;
Lock lock2;

ShareClass(int c) {
counter1 = c;
counter2 = c;
lock1 = new ReentrantLock();
lock2 = new ReentrantLock();
}

public int getCounter1() {
return counter1;
}

public int getCounter2() {
return counter2;
}

public int delayDecCounter1(int delay) {

//do something not access the share obj
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

int tmpCounter;
lock1.lock();
try{
tmpCounter = counter1;

try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

tmpCounter--;
counter1 = tmpCounter;
}finally{
lock1.unlock();
}

return tmpCounter;

}

public int delayDecCounter2(int delay) {

//do something not access the share obj
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

int tmpCounter;
lock2.lock();
try{
tmpCounter = counter2;

try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

tmpCounter--;
counter2 = tmpCounter;
}finally{
lock2.unlock();
}

return counter2;

}
}

}

The layout XML, refer to the former exercise of More example of Synchronized Statements with separate objects for locking.


- More example about Thread

More example of Synchronized Statements with separate objects for locking

Last example compare between Synchronization with single lock object and separate lock objects, one-on-one; one thread access one object and another object access another object. This example demonstrate a more complicated case, two threads access one object, and other two thread access another object.

Synchronized Statements with single object, this.
package com.example.androidthread;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

Button buttonStart;
TextView textInfoA, textInfoB, textInfoC, textInfoD;
TextView textDuration1, textDuration2;
TextView textDuration3, textDuration4;

String infoMsgA;
String infoMsgB;
String infoMsgC;
String infoMsgD;

ShareClass shareObj = new ShareClass(10);
long startingTime;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonStart = (Button) findViewById(R.id.buttonstart);
textInfoA = (TextView) findViewById(R.id.infoa);
textInfoB = (TextView) findViewById(R.id.infob);
textInfoC = (TextView) findViewById(R.id.infoc);
textInfoD = (TextView) findViewById(R.id.infod);
textDuration1 = (TextView) findViewById(R.id.duration1);
textDuration2 = (TextView) findViewById(R.id.duration2);
textDuration3 = (TextView) findViewById(R.id.duration3);
textDuration4 = (TextView) findViewById(R.id.duration4);

buttonStart.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {

infoMsgA = "Thread A\n";
infoMsgB = "Thread B\n";
infoMsgC = "Thread C\n";
infoMsgD = "Thread D\n";
textInfoA.setText(infoMsgA);
textInfoB.setText(infoMsgB);
textInfoC.setText(infoMsgC);
textInfoD.setText(infoMsgD);

Thread thread1 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter1() > 0) {

//caution:
//shareObj.counter1 may change here

infoMsgA += "A 1: "
+ shareObj.delayDecCounter1(500) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoA.setText(infoMsgA);
}

});

} else {
stop = true;
final long endTime1 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration1.setText("Duration 1 (reference only): "
+ (endTime1 - startingTime));
}

});
}
}
}
});

Thread thread2 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter2() > 0) {

//caution:
//shareObj.counter2 may change here

infoMsgB += "B 2: "
+ shareObj.delayDecCounter2(500) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoB.setText(infoMsgB);
}

});

} else {
stop = true;
final long endTime2 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration2.setText("Duration 2 (reference only): "
+ (endTime2 - startingTime));
}

});
}
}
}
});

//
Thread thread3 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter1() > 0) {

//caution:
//shareObj.counter1 may change here

infoMsgC += "C 1: "
+ shareObj.delayDecCounter1(200) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoC.setText(infoMsgC);
}

});

} else {
stop = true;
final long endTime3 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration3.setText("Duration 3 (reference only): "
+ (endTime3 - startingTime));
}

});
}
}
}
});

Thread thread4 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter2() > 0) {

//caution:
//shareObj.counter2 may change here

infoMsgD += "D 2: "
+ shareObj.delayDecCounter2(1100) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoD.setText(infoMsgD);
}

});

} else {
stop = true;
final long endTime4 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration4.setText("Duration 4 (reference only): "
+ (endTime4 - startingTime));
}

});
}
}
}
});
//

startingTime = System.currentTimeMillis();
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
});

}

public class ShareClass {

int counter1;
int counter2;

Object lock1;
Object lock2;

ShareClass(int c) {
counter1 = c;
counter2 = c;
lock1 = new Object();
lock2 = new Object();
}

public int getCounter1() {
return counter1;
}

public int getCounter2() {
return counter2;
}

public int delayDecCounter1(int delay) {

//do something not access the share obj
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

synchronized (this) {
int tmpCounter = counter1;

try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

tmpCounter--;
counter1 = tmpCounter;

return counter1;
}

}

public int delayDecCounter2(int delay) {

//do something not access the share obj
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

synchronized (this) {
int tmpCounter = counter2;

try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

tmpCounter--;
counter2 = tmpCounter;

return counter2;
}

}
}

}



Synchronized Statements with separate objects, lock1 and lock2.
package com.example.androidthread;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

Button buttonStart;
TextView textInfoA, textInfoB, textInfoC, textInfoD;
TextView textDuration1, textDuration2;
TextView textDuration3, textDuration4;

String infoMsgA;
String infoMsgB;
String infoMsgC;
String infoMsgD;

ShareClass shareObj = new ShareClass(10);
long startingTime;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonStart = (Button) findViewById(R.id.buttonstart);
textInfoA = (TextView) findViewById(R.id.infoa);
textInfoB = (TextView) findViewById(R.id.infob);
textInfoC = (TextView) findViewById(R.id.infoc);
textInfoD = (TextView) findViewById(R.id.infod);
textDuration1 = (TextView) findViewById(R.id.duration1);
textDuration2 = (TextView) findViewById(R.id.duration2);
textDuration3 = (TextView) findViewById(R.id.duration3);
textDuration4 = (TextView) findViewById(R.id.duration4);

buttonStart.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {

infoMsgA = "Thread A\n";
infoMsgB = "Thread B\n";
infoMsgC = "Thread C\n";
infoMsgD = "Thread D\n";
textInfoA.setText(infoMsgA);
textInfoB.setText(infoMsgB);
textInfoC.setText(infoMsgC);
textInfoD.setText(infoMsgD);

Thread thread1 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter1() > 0) {

//caution:
//shareObj.counter1 may change here

infoMsgA += "A 1: "
+ shareObj.delayDecCounter1(500) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoA.setText(infoMsgA);
}

});

} else {
stop = true;
final long endTime1 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration1.setText("Duration 1 (reference only): "
+ (endTime1 - startingTime));
}

});
}
}
}
});

Thread thread2 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter2() > 0) {

//caution:
//shareObj.counter2 may change here

infoMsgB += "B 2: "
+ shareObj.delayDecCounter2(500) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoB.setText(infoMsgB);
}

});

} else {
stop = true;
final long endTime2 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration2.setText("Duration 2 (reference only): "
+ (endTime2 - startingTime));
}

});
}
}
}
});

//
Thread thread3 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter1() > 0) {

//caution:
//shareObj.counter1 may change here

infoMsgC += "C 1: "
+ shareObj.delayDecCounter1(200) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoC.setText(infoMsgC);
}

});

} else {
stop = true;
final long endTime3 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration3.setText("Duration 3 (reference only): "
+ (endTime3 - startingTime));
}

});
}
}
}
});

Thread thread4 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter2() > 0) {

//caution:
//shareObj.counter2 may change here

infoMsgD += "D 2: "
+ shareObj.delayDecCounter2(1100) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoD.setText(infoMsgD);
}

});

} else {
stop = true;
final long endTime4 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration4.setText("Duration 4 (reference only): "
+ (endTime4 - startingTime));
}

});
}
}
}
});
//

startingTime = System.currentTimeMillis();
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
});

}

public class ShareClass {

int counter1;
int counter2;

Object lock1;
Object lock2;

ShareClass(int c) {
counter1 = c;
counter2 = c;
lock1 = new Object();
lock2 = new Object();
}

public int getCounter1() {
return counter1;
}

public int getCounter2() {
return counter2;
}

public int delayDecCounter1(int delay) {

//do something not access the share obj
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

synchronized (lock1) {
int tmpCounter = counter1;

try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

tmpCounter--;
counter1 = tmpCounter;

return counter1;
}

}

public int delayDecCounter2(int delay) {

//do something not access the share obj
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

synchronized (lock2) {
int tmpCounter = counter2;

try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

tmpCounter--;
counter2 = tmpCounter;

return counter2;
}

}
}

}


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<Button
android:id="@+id/buttonstart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="start()" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<TextView
android:id="@+id/infoa"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />

<TextView
android:id="@+id/infob"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />

<TextView
android:id="@+id/infoc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />

<TextView
android:id="@+id/infod"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>

<TextView
android:id="@+id/duration1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/duration2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/duration3"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/duration4"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>




- More example about Thread

Synchronized Statements with separate objects for locking

This example compare how Synchronized Statements with single object vs separate objects.

Synchronized Statements with single object, this.
package com.example.androidthread;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

Button buttonStart;
TextView textInfoA, textInfoB;
TextView textDuration1, textDuration2;

String infoMsgA;
String infoMsgB;

ShareClass shareObj = new ShareClass(10);
long startingTime;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonStart = (Button) findViewById(R.id.buttonstart);
textInfoA = (TextView) findViewById(R.id.infoa);
textInfoB = (TextView) findViewById(R.id.infob);
textDuration1 = (TextView) findViewById(R.id.duration1);
textDuration2 = (TextView) findViewById(R.id.duration2);

buttonStart.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {

infoMsgA = "Thread A\n";
infoMsgB = "Thread B\n";
textInfoA.setText(infoMsgA);
textInfoB.setText(infoMsgB);

Thread thread1 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter1() > 0) {

infoMsgA += "A 1: "
+ shareObj.delayDecCounter1(500) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoA.setText(infoMsgA);
}

});

} else {
stop = true;
final long endTime1 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration1.setText("Duration 1 (reference only): "
+ (endTime1 - startingTime));
}

});
}
}
}
});

Thread thread2 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter2() > 0) {

infoMsgB += "B 2: "
+ shareObj.delayDecCounter2(500) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoB.setText(infoMsgB);
}

});

} else {
stop = true;
final long endTime2 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration2.setText("Duration 2 (reference only): "
+ (endTime2 - startingTime));
}

});
}
}
}
});

startingTime = System.currentTimeMillis();
thread1.start();
thread2.start();
}
});

}

public class ShareClass {

int counter1;
int counter2;

ShareClass(int c) {
counter1 = c;
counter2 = c;
}

public int getCounter1() {
return counter1;
}

public int getCounter2() {
return counter2;
}

public int delayDecCounter1(int delay) {

//do something not access the share obj
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

synchronized (this) {
int tmpCounter = counter1;

try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

tmpCounter--;
counter1 = tmpCounter;

return counter1;
}

}

public int delayDecCounter2(int delay) {

//do something not access the share obj
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

synchronized (this) {
int tmpCounter = counter2;

try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

tmpCounter--;
counter2 = tmpCounter;

return counter2;
}

}
}

}



Synchronized Statements with separate objects, lock1 and lock2.
package com.example.androidthread;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

Button buttonStart;
TextView textInfoA, textInfoB;
TextView textDuration1, textDuration2;

String infoMsgA;
String infoMsgB;

ShareClass shareObj = new ShareClass(10);
long startingTime;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonStart = (Button) findViewById(R.id.buttonstart);
textInfoA = (TextView) findViewById(R.id.infoa);
textInfoB = (TextView) findViewById(R.id.infob);
textDuration1 = (TextView) findViewById(R.id.duration1);
textDuration2 = (TextView) findViewById(R.id.duration2);

buttonStart.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {

infoMsgA = "Thread A\n";
infoMsgB = "Thread B\n";
textInfoA.setText(infoMsgA);
textInfoB.setText(infoMsgB);

Thread thread1 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter1() > 0) {

infoMsgA += "A 1: "
+ shareObj.delayDecCounter1(500) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoA.setText(infoMsgA);
}

});

} else {
stop = true;
final long endTime1 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration1.setText("Duration 1 (reference only): "
+ (endTime1 - startingTime));
}

});
}
}
}
});

Thread thread2 = new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter2() > 0) {

infoMsgB += "B 2: "
+ shareObj.delayDecCounter2(500) + "\n";

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoB.setText(infoMsgB);
}

});

} else {
stop = true;
final long endTime2 = System.currentTimeMillis();
MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textDuration2.setText("Duration 2 (reference only): "
+ (endTime2 - startingTime));
}

});
}
}
}
});

startingTime = System.currentTimeMillis();
thread1.start();
thread2.start();
}
});

}

public class ShareClass {

int counter1;
int counter2;

Object lock1;
Object lock2;

ShareClass(int c) {
counter1 = c;
counter2 = c;
lock1 = new Object();
lock2 = new Object();
}

public int getCounter1() {
return counter1;
}

public int getCounter2() {
return counter2;
}

public int delayDecCounter1(int delay) {

//do something not access the share obj
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

synchronized (lock1) {
int tmpCounter = counter1;

try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

tmpCounter--;
counter1 = tmpCounter;

return counter1;
}

}

public int delayDecCounter2(int delay) {

//do something not access the share obj
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

synchronized (lock2) {
int tmpCounter = counter2;

try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

tmpCounter--;
counter2 = tmpCounter;

return counter2;
}

}
}

}


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<Button
android:id="@+id/buttonstart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="start()" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<TextView
android:id="@+id/infoa"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />

<TextView
android:id="@+id/infob"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>

<TextView
android:id="@+id/duration1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/duration2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>

This example compare between Synchronization with single lock object and separate lock objects, one-on-one; one thread access one object and another object access another object. Next example demonstrate a more complicated case, two threads access one object, and other two thread access another object.


- More example about Thread

Share object between threads with Synchronized Statements

Last post show how to create synchronized code with Synchronized Method. Alternatively, we can synchronize block of code with Synchronized Statements.


Example code:
package com.example.androidthread;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

Button buttonStart;
TextView textInfoA, textInfoB;

String infoMsgA;
String infoMsgB;

ShareClass shareObj = new ShareClass(10);

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonStart = (Button) findViewById(R.id.buttonstart);
textInfoA = (TextView) findViewById(R.id.infoa);
textInfoB = (TextView) findViewById(R.id.infob);

buttonStart.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {

infoMsgA = "Thread A\n";
infoMsgB = "Thread B\n";
textInfoA.setText(infoMsgA);
textInfoB.setText(infoMsgB);

new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter() > 0) {

infoMsgA += "A: "
+ shareObj.delayDecCounter(2500) + "\n";

try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoA.setText(infoMsgA);
}

});

} else {
stop = true;
}
}
}
}).start();

new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter() > 0) {

infoMsgB += "B: "
+ shareObj.delayDecCounter(500) + "\n";

try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoB.setText(infoMsgB);
}

});

} else {
stop = true;
}
}
}
}).start();

}
});

}

public class ShareClass {

int counter;

ShareClass(int c) {
counter = c;
}

public int getCounter() {
return counter;
}

public int delayDecCounter(int delay) {

//do something not access the share obj
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

synchronized (this) {
int tmpCounter = counter;

try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

tmpCounter--;
counter = tmpCounter;

return counter;
}

}
}

}


The layout XML, refer to last post.


- More example about Thread

Share object between threads with synchronized methods

This example show how to synchronize share object between threads with synchronized methods.

Share object between threads WITH synchronized method


Share object between threads WITHOUT synchronized method


package com.example.androidthread;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

Button buttonStart;
TextView textInfoA, textInfoB;

String infoMsgA;
String infoMsgB;

ShareClass shareObj = new ShareClass(10);

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonStart = (Button) findViewById(R.id.buttonstart);
textInfoA = (TextView) findViewById(R.id.infoa);
textInfoB = (TextView) findViewById(R.id.infob);

buttonStart.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {

infoMsgA = "Thread A\n";
infoMsgB = "Thread B\n";
textInfoA.setText(infoMsgA);
textInfoB.setText(infoMsgB);

new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter() > 0) {

infoMsgA += "A: "
+ shareObj.delayDecCounter(2500) + "\n";

try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoA.setText(infoMsgA);
}

});

} else {
stop = true;
}
}
}
}).start();

new Thread(new Runnable() {

boolean stop = false;

@Override
public void run() {

while (!stop) {
if (shareObj.getCounter() > 0) {

infoMsgB += "B: "
+ shareObj.delayDecCounter(500) + "\n";

try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

MainActivity.this.runOnUiThread(new Runnable() {

@Override
public void run() {
textInfoB.setText(infoMsgB);
}

});

} else {
stop = true;
}
}
}
}).start();

}
});

}

public class ShareClass {

int counter;

ShareClass(int c) {
counter = c;
}

public int getCounter() {
return counter;
}

public synchronized int delayDecCounter(int delay) {

int tmpCounter = counter;

try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

tmpCounter--;
counter = tmpCounter;
return counter;
}
}

}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />

<Button
android:id="@+id/buttonstart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="start()" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="with synchronized" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >

<TextView
android:id="@+id/infoa"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />

<TextView
android:id="@+id/infob"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>

</LinearLayout>

Next: Share object between threads with Synchronized Statements


- More example about Thread

Set name of Thread

We can assign a name to a thread by calling its setName(String threadName) method. Inside the thread, we can get the name with: Thread.currentThread().getName().

name of Thread



package com.example.androidthread;

import android.os.Bundle;
import android.os.Looper;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

Button buttonStartA, buttonStart, buttonRun;
TextView textInfo;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonStartA = (Button)findViewById(R.id.buttonstarta);
buttonStart = (Button)findViewById(R.id.buttonstart);
buttonRun = (Button)findViewById(R.id.buttonrun);
textInfo = (TextView)findViewById(R.id.info);

buttonStartA.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View arg0) {
//start a thread with name
Thread thread = new Thread(new MyRunnable());
thread.setName("Thread A");
thread.start(); //in background thread
}});

buttonStart.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View arg0) {
Thread thread = new Thread(new MyRunnable());
thread.start(); //in background thread
}});

buttonRun.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View arg0) {
Thread thread = new Thread(new MyRunnable());
thread.setName("another main");
thread.run(); //in current thread
}});
}

private class MyRunnable implements Runnable {

@Override
public void run() {

final String myThreadName = Thread.currentThread().getName();

// check if it's run in main thread, or background thread
if(Looper.getMainLooper().getThread()==Thread.currentThread()){
//in main thread
textInfo.setText("in main thread: " + myThreadName);
}else{
//in background thread

runOnUiThread(new Runnable(){

@Override
public void run() {
textInfo.setText("in background thread: " + myThreadName);
}

});
}
}

}

}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />
<Button
android:id="@+id/buttonstarta"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="start() with name" />
<Button
android:id="@+id/buttonstart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="start()" />
<Button
android:id="@+id/buttonrun"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="run()" />
<TextView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>


- When button "start() with name" pressed, it show thread name of "Thread A".
- When button "start()" pressed, without name assigned, it show something like "Thread-10807".
- When button "run()" pressed, even a name is assigned to the thread, but the run() method is called in main thread actually. So it show name of "main"



- More example about Thread

Runnable in background thread

This exercise show how to run a Runnable in background.
Runnable in background thread
Runnable in background thread

In this exercise, we need to check if the current thread is the main thread, or called UI thread. Using the code:

Looper.getMainLooper().getThread()==Thread.currentThread()

If it's true, means it's in main thread, otherwise it's in background thread.

The example show how to implement a Thread with Runnable object. And call its start() method to starts the new Thread of execution. Also notice that if you call its run() method, it will calls the run() method of the Runnable object directly, in current thread.

package com.example.androidthread;

import android.os.Bundle;
import android.os.Looper;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

Button buttonStart, buttonRun;
TextView textInfo;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonStart = (Button)findViewById(R.id.buttonstart);
buttonRun = (Button)findViewById(R.id.buttonrun);
textInfo = (TextView)findViewById(R.id.info);

buttonStart.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View arg0) {
Thread thread = new Thread(new MyRunnable());
thread.start(); //in background thread
}});

buttonRun.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View arg0) {
Thread thread = new Thread(new MyRunnable());
thread.run(); //in current thread
}});
}

private class MyRunnable implements Runnable {

@Override
public void run() {
// check if it's run in main thread, or background thread
if(Looper.getMainLooper().getThread()==Thread.currentThread()){
//in main thread
textInfo.setText("in main thread");
}else{
//in background thread

runOnUiThread(new Runnable(){

@Override
public void run() {
textInfo.setText("in background thread");
}

});
}
}

}

}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://arteluzevida.blogspot.com/"
android:textStyle="bold" />
<Button
android:id="@+id/buttonstart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="start()" />
<Button
android:id="@+id/buttonrun"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="run()" />
<TextView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>


More example about Thread:
Set name of Thread
Share object between threads with synchronized methods
Share object between threads with synchronized Statements
Synchronized Statements with separate lock object, I
Synchronized Statements with separate lock object, II
Example of using Lock/ReentrantLock
Solve Producer–consumer problem with wait() and notifyAll()