Friday, 4 April 2014

Working with Log4net


Log4Net

  • A small introduction
  • Start working with log4net
  • Levels of logging
  • Loggers and Appenders
  • How to debug issues with log4net
  • Layouts – and types
  • Filters – and types

A Small introduction: 

From http://logging.apache.org/log4net/release/manual/introduction.html, you can see Log4net framework is based on log4j. In short we can say, log4net is for Microsoft .Net applications and log4j is for java. 
So why do we need log4net or log4j?
This tool helps the programmer to log information to different targets. 
So what kind of information we log and what are those targets?
Suppose, we have a project in production. During its daily usage, there might be some case where it might have caused some unexpected error. Now how will you track that issue? Or how will you confirm that some specific code caused that issue? This can be done by logging some information.
What is a target?
Targets can be a file system, database or any other location to log information, which we will be discussing later.


Start working with log4net:

To log some information, we need to configure it. This can be done in the following ways
BasicConfigurator: Supports logging to only one source. 
When this configurator is used, by default the logged information will be shown on Console (Supports only logging to Console). When we use this, the consoleappender is logically added to the root logger.
DomConfigurator: (deprecated. Use Xml configurator instead of this.)
XmlConfigurator: This means that we are using a file written in xml format to configure log4net (Supports logging to different places at a time).


These configurators look for a config section element with name log4net in web.config / app.config file. The below one should be added as child to the configuration element in config file:

<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>


This line will be added only when we are writing entire configuration in either web.config/app.config file. If we using a seperate file to configure log4net, the above lines are not necessary

Configuring using the above configurators can be done either through 
  1. Code or 
  2. Assembly-level attributes.

Using code: 

We need to write the below line well before using log4net to perform any logging operations. Generally this will be written in Application_Start(arg1,arg2) of global.asax.


log4net.Config.XmlConfigurator.Configure(); 


We can use BasicConfigurator/ DomConfigurator in the similar way.
The static Configure() is overloaded to accept a file, stream, URI, or an XmlElement object as input to it. The parameter-less method is used to refer to the application’s web.config or app.config file.

In addition to this, we are having ConfigureAndWatch() which will accept the file info as reference.
ConfigureAndWatch (parameter) method watches the file to keep track of any changes made to it. If there is a change in the file the log4net configuration will be automatically configured.

Using Assembly level attributes:

Instead of configuring log4net via code, it can also be done using AssemblyInfo.cs file located in properties. 
We can configure log4net in the below ways:
  1. [assembly: log4net.Config.XmlConfigurator(Watch=true)]
    This, by default, looks for web.config or an app.config file to load appenders, loggers etc. This is similar to Configure() that we write from code.

  2. [assembly: log4net.Config.XmlConfigurator(ConfigFileExtension="log4net",Watch=true)]     
  3. [assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4netexplicitfile.txt", Watch = true)]
    Using this, we can have a separate file for configurations to log4net instead of writing in application’s web.config or app.config file.
    Mentioning it as a separate file helps us to change the configuration without restarting the application again.
Here the parameters ConfigFile can be any file (say txt, xml etc). 
Watch: Implies, It keeps track of any changes to that file. If this is false, even if configuration file changes, there will be no impact to log4net functionality.
Suppose we have added a text file having configuration settings as below. 

<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="AppenderFile" type="log4net.Appender.FileAppender">
<file value="LogFileName1.log" />

<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-5p %d{hh:mm:ss} %message%newline" />
</layout>
</appender>

<root>
<level value="DEBUG" />
<appender-ref ref="AppenderFile" />
</root>
</log4net>


And this file has been configured using assembly statement as in point 3. 
Publish two different binaries one with watch = false and the other with watch = true.


Do the following steps with both the binaries.
  • Run the project and ensure logging is happening.
  • Open the log4netexplicitfile.txt file and change file attribute value to someother filename.
  • Perform action that logs something using log4net.
  • See whats happening with both the binaries. You should be seeing the following.

  • With watch = true: A new file is created and it will be used hereon for logging purposes.
    With watch = false: The changes done will not be recognized and will continue logging to the same old file.

Levels of logging:  

  • Debug
  • Info
  • Warn
  • Error
  • Fatal

Loggers and Appenders: 


Loggers:

Loggers control the levels of message that needs to be logged. It has two important parts.
  1. level
  2. appernder-ref
Consider the following logger example

<logger name="SmtpAppenderLog4net.WebForm1">
<level value="All" />
<appender-ref ref="WebForm1logger" />
</logger>


Here, name of the logger is SmtpAppenderLog4net.WebForm1. This indicates, SmtpAppenderLog4net is the namespace and WebForm1 is a class inside it or inner namespace. Level mentioned is “All”. This indicates the logger accepts all the levels of logging. It can be anyone of the below:
  • All
  • Off – Logging will not be done.
  • Fatal
  • Error
  • Warn
  • Info
  • Debug

Appender-ref is which appender we should refer. Here WebForm1logger is an appender name. In the previous examples, you might have seen an xml element <appender>. Now let’s discuss what exactly an appender is.

Appenders:

Appenders say where to store the log messages in our application. There can be one or more appenders in the configuration. If we have multiple appenders, its name must be unique. You can see the list of appenders in http://logging.apache.org/log4net/release/config-examples.html For now, we will have small info on the below appenders
  • ConsoleAppender
  • FileAppender
  • Rolling file appender
  • AdoNetAppender

ConsoleAppender:

This appender logs messages to the console. This can be configured as below:

<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.SimpleLayout" />
</appender>

FileAppender:

This appender when used logs the messages to the specified file. This can be written as below:

<appender name="LoggingToFileAppender" type="log4net.Appender.FileAppender">
<file value="FileToLog.txt"/>
<appendToFile value="false" />
<layout type="log4net.Layout.SimpleLayout" />
</appender>


Here logging of messages will be done to FileToLog file.
appendToFile element specifies whether we should append the logs to the previous content in the file or not.

RollingFileAppender:

This logs messages to different files depending on size or date or both. You might think we are having a fileappender and why again this RollingFileAppender. Using FileAppender is not recommended because the file can grow in size in a time frame. Opening/reading such a file takes much time. Using RollingFileAppender, we can have backup of files.

<appender name="LoggingUsingRollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="LoggingUsingRollingFileAppender.txt"/>
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="2" />
<maximumFileSize value="10MB" />
<staticLogFileName value="false" />
<countDirection value="-1"></countDirection>
<layout type="log4net.Layout.SimpleLayout" />
</appender>


Here logging of messages will be done to LoggingUsingRollingFileAppender file based on size. Here I specified maximumFileSize as 10MB which means for every 1kb a new file will be created and the previous logs will be as backup.
Number of backup files that willbe saved depends on the maxSizeRollBackups value. Here at any time, 2 backup files will be present.
RollingStyle is Size. This means that rolling of files should happen based on size. Here in this example, for every 10MB file generated, rolling should happen.

We can have the following values for RollingStyle:
  • Size - Rolling will happen based on Size
  • Date - Rolling happens based on Date
  • Composite - Rolling happens considering both size and date
  • Once - Rolling happens every time log4net is configured. To check this just make some changes to the log4net configuration and run your project again. This will leave a backup file and create a new file
appendToFile element specifies whether we should append the logs to the previous content in the file or not.

AdoNetAppender:

Using this, we can log messages to a table in the database.
We will create a table in database and use it as source to store logs. For now I am using below SQL statement to create a table in database.

CREATE TABLE [dbo].[Log] (
[Id] [int] IDENTITY (1, 1) NOT NULL,
[message] [varchar] (2000) NULL
)


AdoNetAppender can be configured using the below snippet.

<appender name="SQLServerAppender" type="log4net.Appender.ADONetAppender">
<bufferSize value="1" />
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<connectionString value="Data Source=yourDataSource;Initial Catalog=YourDatabase;User ID=userid;password=password" />
<commandText value="INSERT INTO Log ( [message] ) VALUES ( @message )" />
<parameter>
<parameterName value="@message" />
<dbType value="String" />
<size value="2" />
<layout type="log4net.Layout.PatternLayout" value="%message" />
</parameter>
</appender>


Element bufferSize specifies that, log4net will keep these many records in memory before inserting into the database. Suppose if we specify its value as 10, After 10 logs you will see data to be inserted into database.
dbtype is the datatype of the value.
size: here only 2 characters are allowed and logged. Suppose the log message is "Hello World", only "He" will be seen in database column.

Once I ran into an issue where I saw nothing is written to my database(I have used ADONetAppender). I crosschecked the connectionstring, the appenders part and all and felt everything is perfect. Thanks to log4net guys for adding a way to find out whats wrong with the configuration. I have added the following key to app settings in web.config(in your case may be in app.config). Ran the application again and looked into the Output window (ctrl+W,O) in Visual studio and found that I have given wrong column name.
So guys, from the next time, if you find nothing is being logged, dont forget that there is a debugging feature in log4net.

Layouts and types:

In appender section, you might have seen xml element <layout> . So what is a layout? Layout is just a template for your messages. You can customize your message using this.
  • SimpleLayout
  • PatternLayout
  • XMLLayout
  • RawPropertyLayout
  • RawTimeStampLayout
  • RawUtcTimeStampLayout etc…...

SimpleLayout:

This is the simplest layout which contains minimal information about the logged message. It gives only level and the message of the logged message

<appender name="WebForm1logger" type="log4net.Appender.FileAppender">
<file value="WebForm1logger.txt" />
<layout type="log4net.Layout.SimpleLayout"></layout>
</appender>


Logs in WebForm1logger.txt will be of format level_of_logged_message message

PatternLayout:

This is similar to printf statement in C or the string.Format() in C#. Simply to say, it is a conversion pattern. We have the flexibility to format the message by adding some information to it.

Some of the conversion patterns are listed below:

Pattern name Effect seen at the time of logging event
Appdomain Outputs appdomain name
Date Outputs date
Exception Outputs exception passed with the message
Level Outputs level of logging event
Logger Outputs the logger that logged the event
Message Outputs the message passed with the logging event call
Newline Inserts a new line
Property Used to output an event specific property. The property will be specified within braces. Example : property{customproperty}. This will look for the key named customproperty and will get the value associated to it. If no specific key is given, then all the keys and their values are printed in a comma separated list.
Timestamp Used to output the number of milliseconds elapsed since start of the application till the logging event.
Thread Used to output name of the thread. If no name is given, number will be considered.
Utcdate Outputs date in universal time.
% Prints a %

We can use PatternLayout using the below code:

<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date - %message%newline" />
</layout>

This results in date - message_passed_from_the_logger

XMLLayout:

This logs information in xml format.This can be used in the following way:

<layout type="log4net.Layout.XMLLayout"></layout>

RawPropertyLayout:

This is mostly used in ADOAppender while adding value to new column. This can be used as:

<parameter>
<parameterName value="@Email" />
<dbType value="String"/>
<size value="255"/>
<layout type="log4net.Layout.RawPropertyLayout">
<key value="CustomPropertyEmailid"></key>
</layout>
</parameter>


Here Email is the column name in log4net. We are binding a property “CustomPropertyEmailid” value to this column. Value to this property can be set using the following line (this is one of the way):

log4net.ThreadContext.Properties["CustomPropertyEmailid"] = "hellomail@gmail.com";

RawTimeStampLayout, RawUtcTimeStampLayout will be used mostly in ADONetAppender.

Filters - Its types

Suppose there is a case where you need to log Info, debug, warn level logs to one file and Error, Fatal level logs to other. Or suppose we need to log messages that match certain search criteria or reject messages matching some criteria. How this can be accomplished? Log4net comes up with Filters concept to accomplish this. It has the following filters.
  • LevelMatchFilter
  • LevelRangeFilter
  • LoggerMatchFilter
  • StringMatchFilter
  • PropertyFilter
  • DenyAllFilter
LevelMatchFilter: This filters messages against this specific level. It accepts the following properties
levelToMatch: this contains the level that needs to be matched.
acceptOnMatch: Boolean value to indicate whether to accept this level or reject it. Default is true

<appender name="LoggingToFileAppender" type="log4net.Appender.FileAppender">
<file value="Debuglogs.txt"/>
<appendToFile value="false" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %logger %message %newline "/>
</layout>
<filter type="log4net.Filter.LevelMatchFilter">
<acceptOnMatch value="true" />
<levelToMatch value="DEBUG" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>


The above filter logs only when the level matches debug. You can check this in Debuglogs file. Dont forget to include denyAllFilter filter.

LevelRangeFilter: This filters messages that fall within the range specified or not in the range depending on acceptToMatch property.

<appender name="LoggingToFileAppender" type="log4net.Appender.FileAppender">
<file value="DebugToWarnlogs.txt"/>
<appendToFile value="false" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %logger %message %newline "/>
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="DEBUG" />
<levelMax value="WARN" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>


The above filter logs only when the level is in range of debug to warn. All others will be ignored. You can check this in DebugToWarnlogs file. Dont forget to include denyAllFilter filter.

LoggerMatchFilter: This filters messages against the logger that logged the message. This uses startsWith() operation to check for logger match.

<appender name="LoggingToFileAppender" type="log4net.Appender.FileAppender">
<file value="loggerMatchFilter.txt"/>
<appendToFile value="false" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %logger %message %newline "/>
</layout>
<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="MyLog4netProject"></loggerToMatch>
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>


Here the logger that will be match will be those that start with "MyLog4netProject".

StringMatchFilter: This helps to filter messages based on the message content. This can be configured as below:
regexToMatch: regex expression can be given to match the log message.
StringToMatch: Filters messages by checking whether message contains this string or not. It is case sensitive search.
acceptOnMatch: Boolean property.

<appender name="LoggingToFileAppender" type="log4net.Appender.FileAppender">
<file value="StringMatchFilter.txt"/>
<appendToFile value="false" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %logger %message %newline "/>
</layout>
<filter type="log4net.Filter.StringMatchFilter">
<stringToMatch value="MyLog4netProject"></stringToMatch>
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>


The above example logs messages if the message contains the string "MyLog4netProject". The search is case sensitive.
Usually, we specify either regexToMatch or stringToMatch property. If we specify both, regexToMatch will be considered and stringToMatch will be simply ignored.

PropertyFilter: Here match is made against the value of the property specified in the “key” attribute.
Key: Here we will specify on which property the match should happen.
And all the attributes available for StringMatchFilter are also available for PropertyFilter.

DenyAllFilter: This denies logging. Usually we will specify this at the end of all filters. This will be added as below.

<filter type="log4net.Filter.DenyAllFilter" />


Any suggestions are welcomed. Please comment if anyone has any issue with this post.


Monday, 15 October 2012

Displaying and Saving Image in Sdcard

Hi all.
In this post i am going to discuss how to get an image from any URL and saving an image into sdcard and also capturing the whole screen. Now lets design a small layout as in the capture.
   

The following xml named first_activity.xml is used to design the capture.


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/layoutCapture" >

    <Button
        android:id="@+id/showImage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="15dp"
        android:layout_marginTop="25dp"
        android:text="Show Image" />

    <Button
        android:id="@+id/saveImage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/showImage"
        android:layout_marginLeft="59dp"
        android:layout_toRightOf="@+id/showImage"
        android:text="Save Image" />

    <Button
        android:id="@+id/captureScreen"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignRight="@+id/saveImage"
        android:layout_below="@+id/showImage"
        android:layout_marginRight="66dp"
        android:text="Capture Screen" />
    
    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
      android:layout_below="@+id/captureScreen"
        />

</RelativeLayout>


Now as we can see the three buttons the showImage, saveImage, captureScreen  I am going to write three functions which are used to show the image, save the image and capture the Screen.

Lets have a look at the functions : 

"Show the image" function : 
Here the image source is from the URL : http://www.android.com/images/whatsnew/jb-new-logo.png  and the following the function that helps in getting a bitmap from the url that can be used to display the image onto the ImageView.

private Bitmap showImage(String urlStr) {
try {
URL url = new URL(urlStr);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
bitmap = BitmapFactory.decodeStream(input);
return bitmap;
} catch (IOException e) {
e.printStackTrace();
Log.e("Exception", e.getMessage());
return null;
}

}

The most important thing here is dont forget to give the internet permission i.e.., <uses-permission android:name="android.permission.INTERNET"  in the manifest file. Here the urlStr is "http://www.android.com/images/whatsnew/jb-new-logo.png" and if we observe the code we are just opening a connection for the url and saying connect. Next we are getting an inputstream so that we can get data from the particular url and finally decoding the input stream to get a Bitmap object.


Function to "save the image" :
The following function is used to save the image in the sdcard

    void saveImage(){
try {
String filename = Environment.getExternalStorageDirectory().toString();
File f = new File(filename ,"myImage.png");
f.createNewFile();
System.out.println("file created " + f.toString());
      FileOutputStream out = new FileOutputStream(f);
      Bitmap bitmap = showImage(urlStr);
      bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
} catch (Exception e) {
      e.printStackTrace();
}
}

Here i am getting the path of the sdcard dynamically using Environment.getExternalStorageDirectory().toString(); and creating a file with a name myImage.png using the statements 
File f = new File(filename ,"myImage.png");
f.createNewFile(); 
Next creating an output stream to that file so that we can save the image to the particular file using 
           bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
Here the second parameter i.e., 90 is the quality parameter which may vary from 0 -100. In case of png format this will be ignored as it is lossless.
here the bitmap is obtained from the previous function showImage(urlStr); 
The most important thing here is dont forget to give the  permission <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" 

Function to "Capture the Screen" :

The below function is to capture the screen. Have a look at the below code : 

 void saveScreenCapture() {

 View v1 = mBtnSave.getRootView();
//relCapture.setDrawingCacheEnabled(true);
Bitmap bm = v1.getDrawingCache();
mImgPreview.setImageBitmap(bm);

}
Here we need to have root access to your device. Some information about this is discussed in the link here . 
If we need to capture a particular layout we can do that using the particular layout too like the below sample .

instead of the lines  

                View v1 = mBtnSave.getRootView();
Bitmap bm = v1.getDrawingCache();

 use 

              mBtnSave.setDrawingCacheEnabled(true);
              Bitmap bm = mBtnSave.getDrawingCache();

The entire code is as below : 


public class FirstActivity extends Activity {
private RelativeLayout relCapture;
private ImageView mImgPreview;
private Button mBtnShow, mBtnSave, mBtnScreenCapture;
String urlStr = "http://www.android.com/images/whatsnew/jb-new-logo.png";
FileOutputStream outStream = null;
Bitmap bitmap = null;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_activity);
mImgPreview = (ImageView) findViewById(R.id.image);
mBtnShow = (Button) findViewById(R.id.showImage);
mBtnSave = (Button) findViewById(R.id.saveImage);
mBtnScreenCapture = (Button) findViewById(R.id.captureScreen);
relCapture = (RelativeLayout) findViewById(R.id.layoutCapture);
mBtnShow.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
final ProgressDialog dialog = ProgressDialog.show(
FirstActivity.this, "", "Loading...please wait");

new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = showImage(urlStr);
runOnUiThread(new Runnable() {
@Override
public void run() {

if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
mImgPreview.setImageBitmap(bitmap);

}
}
});
}
}).start();

}
});

mBtnSave.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
// save_Image_to_sdcard();
saveImage();
}
});
mBtnScreenCapture.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
saveScreenCapture();
}
});

}

void saveScreenCapture() {
                View v1 = mBtnSave.getRootView();
v1.setDrawingCacheEnabled(true);
                Bitmap bm = relCapture.getDrawingCache();
               mImgPreview.setImageBitmap(bm);

}

void saveImage() {

try {
String filename = Environment.getExternalStorageDirectory()
.toString();

File f = new File(filename, "1111.png");
f.createNewFile();
System.out.println("file created " + f.toString());
FileOutputStream out = new FileOutputStream(f);
Bitmap bitmap = showImage(urlStr);
bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
} catch (Exception e) {
e.printStackTrace();
}

}
private Bitmap showImage(String urlStr) {
try {
URL url = new URL(urlStr);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
bitmap = BitmapFactory.decodeStream(input);
return bitmap;
} catch (IOException e) {
e.printStackTrace();
return null;
}

}
}
The following is the capture obtained when storing the image into the sdcard 
    



Any suggestions are welcomed.

Please comment if anyone has some or the other problem with this one.




Friday, 17 August 2012

Image Slide Show

Hi everyone.
In this tutorial i am going to show you all the code which when run will display the images as a slideshow.
Below is the attached screen shot : 



For this i am considering the images from the Sdcard's images folder where i am having all the images.
Next to display the images i am using View Flipper concept. I am adding ImageViews to the viewflipper so that the images can be displayed onto the screen.
Now lets start 
Design an xml layout as below (say main.xml)



  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2.     xmlns:tools="http://schemas.android.com/tools"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent" >

  5.     <ViewFlipper
  6.         android:id="@+id/main_flipper"
  7.         android:layout_width="wrap_content"
  8.         android:layout_height="wrap_content"
  9.         android:layout_alignParentLeft="true"
  10.         android:layout_alignParentTop="true" >
  11.     </ViewFlipper>

  12. </RelativeLayout>
Now place few images into the sdcard's images folder.
We can get the path of the images folder of our sdcard using the following statement.

File sdcardPath = new File(Environment.getExternalStorageDirectory()
.getPath() + "/images");
String sdcard =  sdcardPath .getPath();

Now let us create a method named  to setImagesToFlipper() add ImageViews dynamically to the ViewFlipper.
I am creating imageviews dynamically because the number of images in the sdcard may not be the same all the times.
Consider the following function.

  1. private void setImagesToFlipper(ViewFlipper flipper, File sdcardPath) {
  2. int imageCount = sdcardPath.listFiles().length;
  3. for (int count = 0; count < imageCount - 1; count++) {
  4. ImageView imageView = new ImageView(this);
  5. Bitmap bmp = BitmapFactory.decodeFile(sdcardPath.listFiles()[count]
  6. .getAbsolutePath());
  7. imageView.setImageBitmap(bmp);
  8. flipper.addView(imageView);

  9. }

  10. }
In the above code 

Line 2 : imageCount will hold the number of files in the path (here images folder).
Line 4 : Dynamically creating an imageView.
Line 5 : Getting a bitmap object of the image present in the sdcard/images folder.
Line 7 : Setting the bitmap image to the image view 
Line 8 : Adding the image to the ViewFlipper.

Now we are with all the images of the sdcard/images folder. Now we need to give a slide show for the images.The following code helps to do this.

  1.             runnable = new Runnable() {
  2. public void run() {
  3. handler.postDelayed(runnable, 3000);
  4. imageViewFlipper.showNext();

  5. }
  6. };
  7. handler = new Handler();
  8. handler.postDelayed(runnable, 500);
If you want to update the UI in the middle of a thread execution, the best approach to do this is creating a handler which will update the UI and let the thread to continue running.
That's the reason i used a handler here.
The line 4 makes the images to slide.showNext() of the ViewFlipper manullay shows the next Child i.e.., the next image .

Now lets have a look at the entire code 

  1.   public class MainActivity extends Activity {

  2. ViewFlipper imageViewFlipper;
  3.         Handler handler;
  4. Runnable runnable;

  5. @Override
  6. public void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.main);
  9. imageViewFlipper = (ViewFlipper) findViewById(R.id.main_flipper);

  10. // getting the path of the images folder in the sdcard
  11. File sdcardPath = new File(Environment.getExternalStorageDirectory()
  12. .getPath() + "/images");

  13. setImagesToFlipper(imageViewFlipper, sdcardPath);

  14. runnable = new Runnable() {

  15. public void run() {

  16. handler.postDelayed(runnable, 3000);
  17. imageViewFlipper.showNext();

  18. }
  19. };
  20. handler = new Handler();
  21. handler.postDelayed(runnable, 500);

  22. }
  23.      /**
  24.        * Sets the images to the ViewFlipper
  25.        * @param flipper : the Flipper object
  26.        * @param sdcardPath : the image file in the sdcard
  27.      */
  28.      private void setImagesToFlipper(ViewFlipper flipper, File sdcardPath) {
  29. int imageCount = sdcardPath.listFiles().length;
  30. for (int count = 0; count < imageCount - 1; count++) {
  31. ImageView imageView = new ImageView(this);
  32. Bitmap bmp = BitmapFactory.decodeFile(sdcardPath.listFiles()[count]
  33. .getAbsolutePath());
  34. imageView.setImageBitmap(bmp);
  35. flipper.addView(imageView);

  36. }

  37. }
  38. }



Any suggestions are welcomed.



Please comment if anyone has some or the other problem with this one.