Using Retrofit 2.x as a REST client - Tutorial

 
3r3-31. 3r33089. 1. Retrofit
 

1.1. What is Retrofit


 
Retrofit is a REST client for Java and Android. It makes it easy to get and upload JSON (or other structured data) through a REST based web service. In Retrofit, you configure which converter is used to serialize data. Usually GSon is used for JSON, but you can add your own converters for processing XML or other protocols. Retrofit uses the OkHttp library for HTTP requests. www.jsonschema2pojo.org This can be useful for creating complex Java data structures from existing JSON.
 
 
 

1.2. Using Retrofit


 
You will need the following three classes to work with Retrofit:
 
 
 
The Model class that is used as the JSON
model.  
Interfaces that define possible HTTP operations 3r33333.  
The Retrofit.Builder class is an instance that uses the interface and API Builder to define the definition of the endpoint URL for HTTP operations 3r333333.  
3r33333.
 
Each interface method represents one of the possible API calls. It must have HTTP annotation (GET, POST, etc.) to indicate the type of request and the relative URL. The return value completes the response in the Call object with the type of expected result.
 
 
@GET ("users")
Call
3r33078.
 
You can use replacement blocks and query parameters to customize the URL. The replacement block is added to the relative URL using {}. Using the @ Path annotation for a method parameter, the value of this parameter is bound to a specific replacement block.
 
 
@GET ("users /{name} /commits")
Call
3r33078.
 
Query parameters are added using the @ Query annotation to the method parameter. They are automatically added at the end of the URL.
 
 
@GET ("users")
Call
getUserById (@Query ("id") Integer id); 3r33077. 3r33078.
 
Annotation @ Body to the parameter of the method tells Retrofit to use the object as the request body for the call.
 
 
@POST ("users")
Call
postUser (@Body User user)
3r33078.
 
3r33089. 2. Prerequisites 3r33090.
 
The following examples use the Eclipse IDE with the Gradle build system.
 
This exercise assumes that you are familiar with r3r3694. Gradle
and 3r3696. using gradle with Eclipse
.
 
 
Other development environments, such as Visual Studio Code or IntelliJ, allow you to do the same, so you can use your favorite tool.
 
 
3r33089. 3. Exercise: First Retrofit client
 
In this exercise, you will create a standalone REST client. Responses are generated by the Mock server.
 
 

3.1. Creating and setting up a

project.
 
Create a new Gradle project, named com.vogella.retrofitgerrit. Add a new package to src /main /java with the name com.vogella.retrofitgerrit.
 
 
Add the following dependencies to the build.gra file.
 
 
//retrofit
implementation 'com.squareup.retrofit2: retrofit: ???'
implementation 'com.squareup.retrofit2: converter-gson: ???'
//Junit
testImplementation ("org.junit.jupiter: junit-jupiter-api: ???")
testRuntime ("org.junit.jupiter: junit-jupiter-engine: ???")
//to run JUnit 3/4 tests: 3r333339. testImplementation ("junit: junit: ???")
testRuntime ("org.junit.vintage: junit-vintage-engine: ???")
3r33077. 3r33078.
 

3.2. Identify the API and Retrofit adapter


 
In the JSON response from Gerrit, we are only interested in the question of changes. Therefore, create the following data class in a previously added default package.
 
 
    package com.vogella.java.retrofitgerrit;
public class Change {
String subject;
public String getSubject () {
return subject;
}
public void setSubject (String subject) {3r333339. this.subject = subject;
}
}
3r33078.
 
Define the REST API for Retrofit through the following interface.
 
 
    package com.vogella.java.retrofitgerrit;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface GerritAPI {
@GET ("changes /")
Call 3r3 to 3594.}
3r33078.
 
 
Create the next controller class. This class creates a Retrofit client, calls the Gerrit API, and processes the result (outputs the result of the call to the console).
 
 
    package com.vogella.java.retrofitgerrit;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class Controller implements Callback changesList = response.body ();
changesList.forEach (change -> System.out.println (change.subject));
} Else {
System.out.println (response.errorBody ());
}
}
3r333339. @Override
Public void onFailure () 3r? 33119.} 3r33077. 3r33078. 3r? 33106.  
Create a class with a main method to start the controller.
 
 
    package com.vogella.java.retrofitgerrit;
public class Main {
public static void main (String[]args) {
Controller controller = new Controller ();
controller.start ();
}
}
3r33078.
 
3r33089. 4. Retrofit converters and adapters 3r3303090.
 

4.1. Retrofit converters


 
Retrofit can be configured to use a specific converter. This converter handles (de) serialization of data. Several converters are already available for various serialization formats.
 
 
To convert to JSON and back:
 
 
Gson: com.squareup.retrofit: converter-gson  
Jackson: com.squareup.retrofit: converter-jackson  
Moshi: com.squareup.retrofit: converter-moshi  
3r33333.
 
3r33333.  
To convert to Protocol Buffers and back: 3r333336.  
 
Protobuf: com.squareup.retrofit: converter-protobuf  
Wire: com.squareup.retrofit: converter-wire  
3r33333.
 
3r33333.  
To convert to XML and back:
 
 
Simple XML: com.squareup.retrofit: converter-simplexml  
3r33333.
 
3r33333.  
3r33333.
 
In addition to the listed converters, you can also create your own to handle other protocols by extending the Converter.Factory class.
 
 

4.2. Retrofit Adapters


 
Retrofit can also be extended with adapters to interact with other libraries, such as RxJava 2.x, Java ? and Guava.
 
 
An overview of the available adapters can be found on Github square /retrofit /retrofit-adapters / .
 
 
For example, an RxJava 2.x adapter can be connected using Gradle:
 
 
    compile 'com.squareup.retrofit2: adapter-rxjava2: latest.version'    3r33078.
 
or using Apache Maven:
 
 
      com.squareup.retrofit2  
[url]Http://vogella.com/article.rss[/url] ) And prints the channel name, titles and article links.
 
 

7.1. Creating and setting up a

project.
 
This exercise assumes that you are familiar with r3r3694. Gradle and 3r3696. Buildship for Eclipse .
 
 
Create a new Gradle project named com.vogella.java.retrofitxml. Add a new package to src /main /java with the name com.vogella.java.retrofitxml.
 
 
Add the following dependencies to the build.gra file.
 
 
    implementation 'com.squareup.retrofit2: retrofit: ???'
implementation 'com.squareup.retrofit2: converter-simplexml: ???'
3r33078.
 

7.2. We define the XML representation


 
RSS news feed as follows:
 
 
    <?xml version="1.0" encoding="UTF-8"?>

3r3733.
Eclipse and Android Information http://www.vogella.com
Eclipse and Android Information
3r3742. en
3r33737. Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany (CC BY-NC-SA 3.0)
3r33748. Tue, 03 May ???:46:11 +0200
Android user interface testing with Espresso - Tutorial This tutorial describes how to test Android applications with the Android Espresso testing framework. 3r33795.
http://www.vogella.com/tutorials/AndroidTestingEspresso/article.html
3r33800. [email protected] (Lars Vogel)
http://www.vogella.com/tutorials/AndroidTestingEspresso/article.html Using the Gradle build system in the Eclipse IDE - Tutorial Gradle tooling in Eclipse http://www.vogella.com/tutorials/EclipseGradle/article.html
3r33800. [email protected] (Lars Vogel)
http://www.vogella.com/tutorials/EclipseGradle/article.html Unit tests with Mockito - Tutorial This tutorial explains the framework for writting software tests. 3r33795.
http://www.vogella.com/tutorials/Mockito/article.html
3r33800. [email protected] (Lars Vogel)
http://www.vogella.com/tutorials/Mockito/article.html
3r3808.
3r33077. 3r33078.
 
In addition to the XML header, this file consists of various XML elements. An RSS element contains a channel element that contains other elements (for example, title, description, pubDate) and several item-elements (articles).
 
 
Create the following two data classes: RSSFeed and Article.
 
 
    package com.vogella.java.retrofitxml;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
@Root (name = "item", strict = false)
public class Article {
@Element (name = "title")
private String title;
@Element (name = "link")
private String link;
public String getTitle () {
return title;
}
public void setTitle (String title) {3r333339. this.title = title;
}
public String getLink () {
return link;
}
public void setLink (String link) {3r333339. this.link = link;
}
}
3r33078.
 
    package com.vogella.java.retrofitxml;
import java.util.List;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Path;
import org.simpleframework.xml.Root;
@Root (name = "rss", strict = false)
public class RSSFeed {
@Element (name = "title")
@Path ("channel")
private String channelTitle;
@ElementList (name = "item", inline = true)
@Path ("channel")
private List Stackoverflow API page .
 
 
In this exercise, you will use the REST Retrofit library. You will use it to query StackOverflow questions for the tag and their responses.
 
 
In our example, we use the following request URL. Open this URL in a browser and look at the answer.
 
 
    https://api.stackexchange.com/2.2/search?order=desc&sort=votes&tagged=android&site=stackoverflow    3r33078.
 

8.1. Creating and setting up a

project.
 
Create an Android app called com.vogella.android.stackoverflow. Use com.vogella.android.stackoverflow as the name of the top-level package.
 
 
Add the following dependencies to the build.gra file.
 
 
    compile "com.android.support:recyclerview-v7:???"
compile 'com.google.code.gson: gson: ???'
3r33078.
 

8.2. Creating a data model 3r332759.
 
We are interested in questions and answers from Stackoverflow. For this purpose, create the following two classes of data.
 
 

    package com.vogella.android.stackoverflow;
import com.google.gson.annotations.SerializedName;
public class Question {
public String title;
public String body;
@SerializedName ("question_id")
public String questionId;
@Override
public String toString () {
return (title);
}
}
3r33078.
 
    package com.vogella.android.stackoverflow;
import com.google.gson.annotations.SerializedName;
public class Answer {
@SerializedName ("answer_id")
public int answerId;
@SerializedName ("is_accepted")
public boolean accepted;
public int score;
@Override
public String toString () {
return answerId + "- Score:" + score + "- Accepted:" + (accepted? "Yes": "No");
}
}
3r33078.
 

8.3. Creating activity and layout


 
Set activity_main.xml for your activity.
 
 
    <?xml version="1.0" encoding="utf-8"?>

3r31146. android: id = "@ + id /list"
android: layout_width = "match_parent" 3r333339. android: layout_height = "0dp"
android: layout_weight = "1" />

3r33077. 3r33078.
 
Add a recycler view adapter class named RecyclerViewAdapter to your project.
 
 
One of the possible implementations is as follows.
 
 
    package com.vogella.android.stackoverflow;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
public class RecyclerViewAdapter extends RecyclerView.Adapter {
private list RxJava2 CallAdapter , add the following dependencies to the build.gradle file
 
 
    implementation 'com.squareup.retrofit2: retrofit: ???'
implementation 'com.squareup.retrofit2: converter-gson: ???'
implementation 'com.squareup.retrofit2: adapter-rxjava2: ???'
implementation 'io.reactivex.rxjava2: rxandroid: ???'
3r33078.
 
Add permission to access the Internet in the manifest.
 
 
    <?xml version="1.0" encoding="utf-8"?>

here is The repository owner is a separate JSON object in the repository response, and therefore it usually needs an appropriate Java class for (de) serialization. Fortunately, Retrofit gives you the ability to add your own typed JSONDeserializer to control the de-serialization of a particular type. Each time an object of a particular type must be deserialized, this custom deserializer is used.
 
 
To do this, add the following GithubRepoDeserialzer class.
 
 
    package com.vogella.android.retrofitgithub;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
public class GithubRepoDeserializer implements JsonDeserializer {
@Override
public GithubRepo deserialize (JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
GithubRepo githubRepo = new GithubRepo ();
JsonObject repoJsonObject = json.getAsJsonObject ();
githubRepo.name = repoJsonObject.get ("name"). getAsString ();
githubRepo.url = repoJsonObject.get ("url"). getAsString ();
JsonElement ownerJsonElement = repoJsonObject.get ("owner");
JsonObject ownerJsonObject = ownerJsonElement.getAsJsonObject ();
githubRepo.owner = ownerJsonObject.get ("login"). getAsString ();
return githubRepo;
}
}
3r33078.
 
Define the REST API for Retrofit through the following interface: 3r333336.  
 
    package com.vogella.android.retrofitgithub;
import java.util.List;
import io.reactivex.Single;
import okhttp3.ResponseBody;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Url;
public interface GithubAPI {
String ENDPOINT = "https://api.github.com";
@GET ("user /repos? Per_page = 100")
Single postComment (@Url String url, @Body GithubIssue issue);
}
3r33078.
 
You may have a question about the @ Url annotation. With this annotation we can specify the URL for this request. This allows us to change the URL for each request dynamically. We need this for the comments_url field of the GithubIssue class.
 
 
The @ Path annotations associate the value of the parameter with the corresponding variable (braces) in the request URL. This is necessary to indicate the owner and name of the repository for which discussions should be requested.
 
 

9.3. Creation of Credentials Dialog Box 3r3-32759.
 
To allow the user to store their credentials in the application, use DialogFragment. Therefore, create the following class with the name CredentialsDialog, and also add a layout file with the name dialog_credentials.xml to the resources layout folder.
 
 
The result should look something like the following screenshot.
 
 
3r32030.
 
 

    <?xml version="1.0" encoding="utf-8"?>
3r33077. 3r33078.
 
    package com.vogella.android.retrofitgithub;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.View;
import android.widget.EditText;
import okhttp3.Credentials;
public class Credentials Dialog extends DialogFragment {
EditText usernameEditText;
EditText passwordEditText;
ICredentialsDialogListener listener;
public interface ICredentialsDialogListener {
void onDialogPositiveClick (String username, String password);
}
@Override
public void onAttach (Context context) {3r333339. super.onAttach (context);
if (getActivity () instanceof ICredentialsDialogListener) {
listener = (ICredentialsDialogListener) getActivity ();
}
}
@NonNull
@Override
public Dialog onCreateDialog (Bundle savedInstanceState) {
View view = getActivity (). GetLayoutInflater (). Inflate (R.layout.dialog_credentials, null);
usernameEditText = (EditText) view.findViewById (R.id.username_edittext);
passwordEditText = (EditText) view.findViewById (R.id.password_edittext);
usernameEditText.setText (getArguments (). getString ("username"));
passwordEditText.setText (getArguments (). getString ("password"));
AlertDialog.Builder builder = new AlertDialog.Builder (getActivity ())
.setView (view)
.setTitle ("Credentials")
.setNegativeButton ("Cancel", null)
.setPositiveButton ("Continue", new DialogInterface.onclickListener () {
@Override
public void onclick (DialogInterface dialog, int which) {
if (listener! = null), to get the popup, click ()) to set the value in the popup window; () .toString (), passwordEditText.getText (). toString ());
}
} 3r333339.});
return builder.create ();
}
}
3r33078.
 

9.4. Create Activity


 
Modify activity_main.xml as follows.
 
 
    <?xml version="1.0" encoding="utf-8"?>

Twitter application-only authentication with OAuth 2 for authorization. To do this exercise, you need to have a Twitter account. In addition, you need to go to Twitter applications. and create a new application to get your consumer key and consumer secret. We will need this later to request our OAuth token.
 
 

10.1. Setting up the project 3r375329.
 
Create an Android app called Retrofit Twitter. Use com.vogella.android.retrofittwitter as the name of the top-level package.
 
 
To use Retrofit, add the following lines to the build.gradle file
 
 

    implementation 'com.squareup.retrofit2: retrofit: ???'
implementation 'com.squareup.retrofit2: converter-gson: ???'
3r33078.
 
Add permission to access the Internet in the manifest.
 
 
    <?xml version="1.0" encoding="utf-8"?>
3r33077. 3r33078.
 

10.2. API definition


 
Create the following two classes of data, called OAuthToken and UserDetails.
 
 
    package com.vogella.android.retrofittwitter;
import com.google.gson.annotations.SerializedName;
public class OAuthToken {
@SerializedName ("access_token")
private String accessToken;
@SerializedName ("token_type")
private String tokenType;
public String getAccessToken () {3r333339. return accessToken;
}
public String getTokenType () {3r333339. return tokenType;
}
public String getAuthorization () {
return getTokenType () + "" + getAccessToken ();
}
}
3r33078.
 
    package com.vogella.android.retrofittwitter;
public class UserDetails {
private String name;
private String location;
private String description;
private String url;
public String getName () {
return name;
}
public void setName (String name) {3r333339. this.name = name;
}
public String getLocation () {
return location;
}
public void setLocation (String location) {3r333339. this.location = location;
}
public String getDescription () {
return description;
}
public void setDescription (String description) {3r333339. this.description = description;
}
public String getUrl () {
return url;
}
public void setUrl (String url) {3r333339. this.url = url;
}
}
3r33078.
 
The OAuthToken class is used to store the bearer token that we request from Twitter, with our key and secret. We use the @ SerializedName annotation to set the name of the Retrofit to (de) serialize the fields.
 
 
The UserDetails class simply saves several fields from a Twitter response when requesting user details. We do not show all user data that contains answers, just the name, location, URL, and description.
 
 
Define the REST API for Retrofit through the following interface: 3r333336.  
 
    package com.vogella.android.retrofittwitter;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Query;
public interface TwitterApi {
String BASE_URL = "https://api.twitter.com/";
@FormUrlEncoded
@POST ("oauth2 /token")
Call postCredentials (@Field ("grant_type") String grantType);
@GET ("/1.1 /users /show.json")
Call getUserDetails (@Query ("screen_name") String name);
}
3r33078.
 

10.3. Create Activity


 
Modify the activity_main.xml file and the corresponding MainActivity class as follows:
 
 
    <?xml version="1.0" encoding="utf-8"?>

3r333339.
3r3333311.
3r3333311.
3r33077. 3r33078.
 
    package com.vogella.android.retrofittwitter;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import okhttp3.Credentials;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class MainActivity extends AppCompatActivity {
private String credentials = Credentials.basic ("aConsumerKey", "aSecret");
Button requestTokenButton;
Button requestUserDetailsButton;
EditText usernameEditText;
TextView usernameTextView;
TextView nameTextView;
TextView locationTextView;
TextView urlTextView;
TextView descriptionTextView;
TwitterApi twitterApi;
OAuthToken token;
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_main);
requestTokenButton = (Button) findViewById (R.id.request_token_button);
requestUserDetailsButton = (Button) findViewById (R.id.request_user_details_button);
usernameEditText = (EditText) findViewById (R.id.username_edittext);
usernameTextView = (TextView) findViewById (R.id.username_textview);
nameTextView = (TextView) findViewById (R.id.name_textview);
locationTextView = (TextView) findViewById (R.id.location_textview);
urlTextView = (TextView) findViewById (R.id.url_textview);
descriptionTextView = (TextView) findViewById (R.id.description_textview);
createTwitterApi ();
}
private void createTwitterApi () {3r3333311. . OkHttpClient okHttpClient = new OkHttpClient.Builder () addInterceptor (new Interceptor () {
@Override
public okhttp3.Response intercept (Chain chain) throws IOException {
Request originalRequest = chain.request ();
Request.Builder builder = originalRequest.newBuilder (). Header ("Authorization",
Token! = Null? Token.getAuthorization (): credentials); 3r333339.
.proceed (newRequest);
} 3rr33119.}). build ();
Retrofit retrofit = new Retrofit.Builder ()
.baseUrl (TwitterApi.BASE_URL)
.client (okHttpClient)
.addConverterFactory (GsonConverterFactory.create ())
.build ();
twitterApi = retrofit.create (TwitterApi.class);
}
public void onclick (View view) {3r33333119. switch (view.getId ()) {3r33333119. case R.id.request_token_button:
twitterApi.postCredentials ("client_credentials"). enqueue (tokenCallback);
break;
case R.id.request_user_details_button:
String editTextInput = usernameEditText.getText (). ToString ();
if (! editTextInput.isEmpty ()) {
twitterApi.getUserDetails (editTextInput) .enqueue (userDetailsCallback);
} else {
Toast.makeText (this, "Please provide a username", Toast.LENGTH_LONG) .show ();
}
break;
}
}
Callback 3r33045. tokenCallback = new Callback () {
@Override
public void onResponse (Call call, Response response) {
if (response.isSuccessful ()) {3r333339. requestTokenButton.setEnabled (false);
requestUserDetailsButton.setEnabled (true);
usernameTextView.setEnabled (true);
usernameEditText.setEnabled (true);
token = response.body ();
} else {
Toast.makeText (MainActivity.this, "Failure while requesting token", Toast.LENGTH_LONG) .show ();
Log.d ("RequestTokenCallback", "Code:" + response.code () + "Message:" + response.message ());
}
}
@Override
public void onFailure (Call call, Throwable t) {
t.printStackTrace ();
}
};
Callback 3r33072. userDetailsCallback = new Callback () {
@Override
public void onResponse (Call call, Response response) {
if (response.isSuccessful ()) {3r333339. UserDetails userDetails = response.body ();
nameTextView.setText (userDetails.getName () == null? "no value": userDetails.getName ());
locationTextView.setText (userDetails.getLocation () == null? "no value": userDetails.getLocation ());
urlTextView.setText (userDetails.getUrl () == null? "no value": userDetails.getUrl ());
descriptionTextView.setText (userDetails.getDescription (). isEmpty ()? "no value": userDetails.getDescription ());
} else {
Toast.makeText (MainActivity.this, "Failure while requesting user details", Toast.LENGTH_LONG) .show ();
Log.d ("UserDetailsCallback", "Code:" + response.code () + "Message:" + response.message ());
}
}
@Override
public void onFailure (Call call, Throwable t) {
t.printStackTrace ();
}
};
}
3r33078.
 
Replace aConsumerKey and aSecret with a consumer key and secret received from Twitter.
 
 
 Also take a look at the interceptor that we add to our Retrofit client. Because we use OAuth, our credentials are different for each call. The postCredentials method should place credentials (consumer key and secret) in the Basic scheme for Twitter. As a result, this call returns bearer token, which Retrofit deserializes into our OAuthToken class, which is then stored in the token field. Any other request can (and should) now use this token as credentials for authorization. Also requested information about the user.
 
 
3r33089. 11. Retrofit resources
 
3r33093. Consuming APIs with Retrofit tutorial
 
 
3r33099. In dept blog series about Retrofit
 
 
3r? 33105. Consuming APIs with Retrofit
 
3r333335.
3r333112. ! function (e) {function t (t, n) {if (! (n in e)) {for (var r, a = e.document, i = a.scripts, o = i.length; o-- ;) if (-1! == i[o].src.indexOf (t)) {r = i[o]; break} if (! r) {r = a.createElement ("script"), r.type = "text /jаvascript", r.async =! ? r.defer =! ? r.src = t, r.charset = "UTF-8"; var d = function () {var e = a.getElementsByTagName ("script")[0]; e.parentNode.insertBefore (r, e)}; "[object Opera]" == e.opera? a.addEventListener? a.addEventListener ("DOMContentLoaded", d ): d ()}}} t ("//mediator.mail.ru/script/2820404/"""_mediator") () (); 3r333333.
3r333335.
+ 0 -

Add comment