Often an abstraction layer is commonly used to 'abstract' away detail.
Say you had a program for moving money around between different banks. There is a function for moving money to BankA, and a different function for moving money to BankB and so on. The different functions might exist because the information that different banks request varies (As a simple example maybe one requests the senders first name and surname, and another requests the senders first initial and surname).
Your functions might be:
MoveMoneyToBankA(amount, accountNo, senderFirstName, senderSurname)
{
... Code to move money to bank A
}
MoveMoneyToBankB(amount, accountNo, senderFirstInitial, senderSurname)
{
... Code to move money to bank A
}
To abstract away all the different things a program needs to think about when communicating with different banks, an abstration layer might be implemented by adding the function 'MoveMoneyToBank'
MoveMoneyToBank(amount, accountNo, senderFirstName, senderSurname, bankName)
{
... If bank name = 'BankA' call MoveMoneyToBankA
... If bank name = 'BankB', find senderFirstInitial using senderFirstName, and call 'MoveMoneyToBankB'
}
So what you have done here is abstracted away the detail that needs to be managed when communicating with two different banks. A programmer can now just use the general MoveMoneyToBank function, and not have to think about the different requirements of each bank.
In reality, this might not just be functions with in the same program, but even different projects within the same solution, or a new software project that draws in lots of components and deals with the complexity of working with all the separate components to achieve a common goal.