how to Combine Glide and Stetho to debug image loading system using okhttp3
I did the following
1.Added the dependancies
//stetho
compile 'com.facebook.stetho:stetho:1.3.1'
compile 'com.facebook.stetho:stetho-okhttp3:1.3.1'
//glide
compile 'com.github.bumptech.glide:glide:3.7.0'
// Glide's OkHttp3 Integration
compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0#aar'
//okhttp3
compile 'com.squareup.okhttp3:okhttp:3.2.0'
1.Added the initialization
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
Stetho.initialize(
Stetho.newInitializerBuilder(this)
.enableDumpapp(Stetho.defaultDumperPluginsProvider(this))
.enableWebKitInspector(Stetho.defaultInspectorModulesProvider(this))
.build());
}
}
3.add glide config to use okhttp3
/**
* Created by Arnaud Camus Copied by MOMANI on 2016/04/15.
* http://arnaud-camus.fr/combining-glide-and-stetho-to-easily-debug-your-image-loading-system/
*/
public class StethoOkHttpGlideModule implements GlideModule {
#Override
public void applyOptions(Context context, GlideBuilder builder) { }
#Override
public void registerComponents(Context context, Glide glide) {
okhttp3.OkHttpClient client = new okhttp3.OkHttpClient();
client.networkInterceptors().add(new com.facebook.stetho.okhttp3.StethoInterceptor());
glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client));
}
}
4.added the GlideModule metadata tag in AndroidManifest.xml
<application
android:name=".....">
.....
<meta-data android:name="android.alcode.com.material.utils.glide.StethoOkHttpGlideModule"
android:value="GlideModule" />
</application>
5.Glide loadimg images line
Glide.with(((ViewHolderSmall) holder).imageView.getContext())
.load(post.getImageUrl())
// .diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(((ViewHolderSmall) holder).imageView);
when I open chrome Developer tool Resources Tab works perfectly but network tab doesn't!!!
why? where is my mistake? and is it recommended to use okhttp3 with Glide? and how how to connect it without using okhttp3
any duplication or links would help
Re: is it recommended to use okhttp3 with Glide?
Yes, this is the way to go for OkHttp integration.
Re: why? where is my mistake?
This is likely needed because the built-in GlideModule may be overwriting your intercepted client (there's no defined ordering between GlideModule execution). Consider the following from the wiki:
When overriding default behaviour make sure your custom GlideModule is registered in the manifest and the default one is excluded. Exclusion may mean removing the corresponding metadata from the manifest or using the jar dependency instead of the aar one. -- Overriding default behaviour - Glide Wiki
Based on Conflicting GlideModules - Glide Wiki: remove #aar from the okhttp3-integration dependency, or add to manifest:
<meta-data android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule" tools:node=”remove” />
Also make sure you're not fooled by the cache: .diskCacheStrategy(NONE).skipMemoryCache(true); you can remove this as soon as you see the requests as you expect them to.
OkHttp3 changed the API client.networkInterceptors() is not writable any more:
okhttp3.OkHttpClient client = new okhttp3.OkHttpClient.Builder()
.addNetworkInterceptor(new com.facebook.stetho.okhttp3.StethoInterceptor())
.build();
Re: how how to connect it without using okhttp3
The default (built-in) network integration uses HttpUrlFetcher created by HttpUrlGlideUrlLoader, so in order to integrate with Stetho you'll need to replace those with one that intercepts requests.
'
GlideModule
#Override public void registerComponents(Context context, Glide glide) {
glide.register(GlideUrl.class, InputStream.class, new StethoHttpUrlGlideUrlLoader.Factory());
}
Make sure StethoHttpUrlGlideUrlLoader returns the modified classes and not the built-in ones in build and getResourceFetcher.
Custom Fetcher
Only the structural diff is included here for brevity, it should be clear where these lines go from the context. Full code is available on GitHub:
public class StethoHttpUrlFetcher implements DataFetcher<InputStream> {
private final StethoURLConnectionManager stethoManager;
public StethoHttpUrlFetcher(GlideUrl glideUrl) {
...
this.stethoManager = new StethoURLConnectionManager("Glide");
}
private InputStream loadDataWithRedirects(...) {
...
//urlConnection.setRequestProperty("Accept-Encoding", "gzip"); // don't request, it's wasteful for images
...
stethoManager.preConnect(urlConnection, null);
try {
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
stethoManager.postConnect();
} catch (IOException ex) {
stethoManager.httpExchangeFailed(ex);
throw ex;
}
...
}
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection) throws IOException {
try {
InputStream responseStream = stethoManager.interpretResponseStream(urlConnection.getInputStream());
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
...
stream = ContentLengthInputStream.obtain(responseStream, contentLength);
} else {
...
stream = responseStream;
}
return stream;
} catch (IOException ex) {
stethoManager.httpExchangeFailed(ex);
throw ex;
}
}
#Override public void cleanup() {
...
if (isCancelled) { // otherwise it stays pending indefinitely because the stream is not read
stethoManager.httpExchangeFailed(new IOException("Cancelled"));
}
}
}
Finally it worked
according to Glide Wikis I changed the following
<application
android:name=".....">
.....
<meta-data android:name="android.alcode.com.material.utils.glide.StethoOkHttpGlideModule"
android:value="GlideModule" />
<meta-data android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule" tools:node="remove" />
</application>
and according to #TWiStErRob's Answer I changed the following
public class StethoOkHttpGlideModule implements GlideModule {
#Override
public void applyOptions(Context context, GlideBuilder builder) { }
#Override
public void registerComponents(Context context, Glide glide) {
okhttp3.OkHttpClient client = new okhttp3.OkHttpClient.Builder()
.addNetworkInterceptor(new com.facebook.stetho.okhttp3.StethoInterceptor())
.build();
glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client));
}
}
that's it but some questions about okhttp3 not answered
Related
I am migrating an app to glide v4.7.1 from v3 and have noticed that FifoPriorityThreadPoolExecutor class was removed. We use it to limit number of threads Glide use to do network operations.
Old code
public class GlideConfiguration implements GlideModule {
#Override
public void applyOptions(Context context, GlideBuilder builder) {
builder.setResizeService(new FifoPriorityThreadPoolExecutor(1));
}
...
}
New code
#GlideModule
public class MyAppGlideModule extends AppGlideModule {
private static final String FIFO_SOURCE_EXECUTOR_NAME = "fifo-source";
#Override
public void applyOptions(#NonNull Context context, #NonNull GlideBuilder builder) {
builder.setSourceExecutor(newSourceExecutor(1, FIFO_SOURCE_EXECUTOR_NAME, GlideExecutor.UncaughtThrowableStrategy.DEFAULT));
}
...
}
But it still uses 4 threads. What am I doing wrong?
After checking glide's source code.
Glide will initialize its default source executor only if no source executor has been provided to builder.
Glide build(#NonNull Context context) {
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
...
}
// Glide executor is simply a wrapper over ThreadPoolExecutor class.
public static GlideExecutor newSourceExecutor(int threadCount, String name, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
return new GlideExecutor(new ThreadPoolExecutor(threadCount /* corePoolSize */,threadCount /* maximumPoolSize */,0 /* keepAliveTime */,TimeUnit.MILLISECONDS,new PriorityBlockingQueue<Runnable>(),new DefaultThreadFactory(name, uncaughtThrowableStrategy, false)));
}
Since you are inspecting threads using Stetho library to monitor network call which doesn't show any information about threads. It's very likely that these requests are being initiated by single thread.
To better understand thread usage in app, you should inspect it using Android monitor -> DDMS -> Threads.
I have checked using DDMS Threads tool and found only one thread running with mentioned code snippet.
the below posted method in the code section contains a static method which is "with()". I want to test the code in below, so I coded the test of this method
as shown in the testing section.
i tried to test the method using both of "spy()" and "mock()" but the test fails alwyas.
please let me know how can I test a method returns void?
code
public RequestCreator requestCreatorFromUrl(String picUrl) {
return Picasso.with(mCtx).load(picUrl);
}
testing:
public class ValidationTest {
#Mock
private Context mCtx = null;
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#Before
public void setUp() throws Exception {
mCtx = Mockito.mock(Context.class);
Assert.assertNotNull("Context is not null", mCtx);
}
#Test
public void whenRequestCreatorFromUrlTest() throws Exception {
Picasso picasso = Picasso.with(mCtx);
Picasso spyPicasso = spy(picasso);
Uri mockUri = mock(Uri.class);
RequestCreator requestCreator = Picasso.with(mCtx).load(mockUri);
RequestCreator spyRequestCreator = spy(requestCreator);
doReturn(spyRequestCreator).when(spyPicasso.load(mockUri));
//when(spyPicasso.load(mockUri)).thenReturn(spyRequestCreator);
RequestCreator actual = spyPicasso.load(mockUri);
Assert.assertEquals(requestCreator, actual);
}
Usually, if you end up using PowerMock, that’s a good sign that you most possibly are on the wrong way.
What if instead of directly referring to Picasso, you create a component, whose responsibility will be to load an image, let's say class ImageLoader. What will this give to you?
Separation of concerns: if tomorrow you decide to move to Glide, you shouldn't change each and every class where you were using Picasso, you will just change implementation of ImageLoader. Other components are non-wiser of these changes, because they are dependent on an abstraction, not on implementation
Seam: this will allow you easily mock dependencies in order to perform unit testing
This will be our abstraction:
interface ImageLoader {
RequestCreator load(String url);
}
Let’s provide an implementation:
class ImageLoaderImpl implements ImageLoader {
private final Picasso picasso;
public ImageLoaderImpl(Context context) {
this.picasso = Picasso.with(context);
}
#Override
public RequestCreator load(String url) {
return picasso.load(url);
}
}
Now, in your components whenever you need Picasso use ImageLoader instead.
Thus, your method becomes following:
public static RequestCreator requestCreatorFromUrl(String picUrl) {
return imageLoader.load(picUrl);
}
Then your test will look like this:
#Test
public void test() {
ImageLoaderImpl imageLoader = Mockito.mock(ImageLoaderImpl.class);
RequestCreator expected = Mockito.mock(RequestCreator.class);
String TEST_URL = "https://www.some.url/img.jpg";
when(imageLoader.load(TEST_URL)).thenReturn(expexted);
RequestCreator actual = clazzToTest.requestCreatorFromUrl(TEST_URL);
assertEquals(expected, actual);
}
No mocking of static method, no PowerMock needed.
From the Mockito's FAQ:
What are the limitations of Mockito
...
Cannot mock static methods
Use PowerMock instead. Here you will find the detailed instruction about how to mock the static methods.
Update
In order to apply the PowerMock to your test you need to:
Add #PrepareForTest at test class level:
#PrepareForTest(Picasso.class)
public class ValidationTest {
...
}
Call PowerMockito.mockStatic() to mock a static class
PowerMockito.mockStatic(Picasso.class);
Just use Mockito.when() to setup your expectation:
Mockito.when(Picasso.with(mCtx)).thenReturn(requestCreator);
The same set of steps is applicable for the RequestCreator.class.
P.S. I can make mistakes because I do not know the API of 3rd party library you use.
I'm following this tutorial and this Custom Detector Example in order to implement Custom Lint Rules. Basically what I've done is:
Create a new Android Project in Android Studio;
Create a java module for project created in step 1;
On module's build.gradle, import Lint API dependencies;
Create an Issue & IssueRegistry & CustomDetector;
Reference the IssueRegistry on module's build.gradle;
Create Unit tests;
My problem is, during the execution of my JUnits, I always receive "No Warning". When I debug the test, I can see that my Custom Detector isn't called, what am I doing wrong?
Strings.java
public class Strings {
public static final String STR_ISSUE_001_ID = "VarsMustHaveMoreThanOneCharacter";
public static final String STR_ISSUE_001_DESCRIPTION = "Avoid naming variables with only one character";
public static final String STR_ISSUE_001_EXPLANATION = "Variables named with only one character do not pass any meaning to the reader. " +
"Variables name should clear indicate the meaning of the value it is holding";
}
Issues.java
public class Issues {
public static final
Issue ISSUE_001 = Issue.create(
STR_ISSUE_001_ID,
STR_ISSUE_001_DESCRIPTION,
STR_ISSUE_001_EXPLANATION,
SECURITY,
// Priority ranging from 0 to 10 in severeness
6,
WARNING,
new Implementation(VariableNameDetector.class, ALL_RESOURCES_SCOPE)
);
}
IssuesRegistry.java
public class IssueRegistry extends com.android.tools.lint.client.api.IssueRegistry {
#Override
public List<Issue> getIssues() {
List<Issue> issues = new ArrayList<>();
issues.add(ISSUE_001);
return issues;
}
}
VariableNameDetector.java
public class VariableNameDetector extends Detector implements Detector.JavaScanner {
public VariableNameDetector() {
}
#Override
public boolean appliesToResourceRefs() {
return false;
}
#Override
public boolean appliesTo(Context context, File file) {
return true;
}
#Override
#Nullable
public AstVisitor createJavaVisitor(JavaContext context) {
return new NamingConventionVisitor(context);
}
#Override
public List<String> getApplicableMethodNames() {
return null;
}
#Override
public List<Class<? extends Node>> getApplicableNodeTypes() {
List<Class<? extends Node>> types = new ArrayList<>(1);
types.add(lombok.ast.VariableDeclaration.class);
return types;
}
#Override
public void visitMethod(
JavaContext context,
AstVisitor visitor,
MethodInvocation methodInvocation
) {
}
#Override
public void visitResourceReference(
JavaContext context,
AstVisitor visitor,
Node node,
String type,
String name,
boolean isFramework
) {
}
private class NamingConventionVisitor extends ForwardingAstVisitor {
private final JavaContext context;
NamingConventionVisitor(JavaContext context) {
this.context = context;
}
#Override
public boolean visitVariableDeclaration(VariableDeclaration node) {
StrictListAccessor<VariableDefinitionEntry, VariableDeclaration> varDefinitions =
node.getVariableDefinitionEntries();
for (VariableDefinitionEntry varDefinition : varDefinitions) {
String name = varDefinition.astName().astValue();
if (name.length() == 1) {
context.report(
ISSUE_001,
context.getLocation(node),
STR_ISSUE_001_DESCRIPTION
);
return true;
}
}
return false;
}
}
}
build.gradle
apply plugin: 'java'
configurations {
lintChecks
}
ext {
VERSION_LINT_API = '24.3.1'
VERSION_LINT_API_TESTS = '24.3.1'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "com.android.tools.lint:lint-api:$VERSION_LINT_API"
implementation "com.android.tools.lint:lint-checks:$VERSION_LINT_API"
testImplementation "com.android.tools.lint:lint-tests:$VERSION_LINT_API_TESTS"
}
jar {
manifest {
attributes('Lint-Registry': 'br.com.edsilfer.lint_rules.resources.IssueRegistry')
}
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
TestVariableNameDetector.java
private static final String ARG_DEFAULT_LINT_SUCCESS_LOG = "No warnings.";
#Override
protected Detector getDetector() {
return new VariableNameDetector();
}
#Override
protected List<Issue> getIssues() {
return Collections.singletonList(Issues.ISSUE_001);
}
public void test_file_with_no_variables_with_length_equals_01() throws Exception {
assertEquals(
ARG_DEFAULT_LINT_SUCCESS_LOG,
lintProject(java("assets/Test.java", "public class Test {public String sampleVariable;}"))
);
}
public void test_file_with_variables_with_length_equals_01() throws Exception {
assertEquals(
ARG_DEFAULT_LINT_SUCCESS_LOG,
lintProject(java("assets/Test3.java", "public class Test {public String a;bnvhgvhj}"))
);
}
}
P.S.: on Java's module I do not have access to assetsor res folder, that is the reason why I've created a String.java and I'm using java(to, source) in my Unit test - I assumed that this java method does the same as the xml from the tutorial link I referenced at the top of this question.
It turned out that in my case the problem was with the JUnit itself. I think that the way I was attempting to simulate the file was wrong. The text below is part of the README.md of a sample project that I've created in order to document what I've learned from this API and answers the question in the title:
Create
Create a new Android Project;
Create a new Java Library Module - Custom Lint Rules are packaged into .jar libraries once they are ready, therefore the easiest way to implement them using them is inside a Java Module Library;
On module's build.gradle:
add target and source compatibility to Java 1.7;
add dependencies for lint-api, lint-checks and test dependencies;
add jar packing task containing two attributes: Manifest-Version and Lint-Registry, set the first to 1.0 and the second as the full path to a class that will later on contain the issue's catalog;
add a default tasl assemble;
[OPTIONAL]: add a task that will copy the generated .jar into ~/.android/lint;
Check REF001 and choose a Detector that best suits your needs, based on it create and implement a class to fulfill the Detector's role;
Still based on REF0001 chosen file, create and implement a Checker class, later referring to it inside Detector's createJavaVisitor() method;
for the sake of SRP, do not place Checker in the same file of Detector's class;
Copy the generated .jar file from build/lib to ~/.android/lint - if you added a task on build.gradle that does this you can skip this step;
Restart the computer - once created and moved into ~/.android/lint, the Custom Rules should be read by Lint next time the program starts. In order to have the alert boxes inside Android Studio, it is enough to invalidate caches and restart the IDE, however, to have your custom rules caught on Lint Report when ./gradlew check, it might be necessary to restart your computer;
Testing Detectors and Checkers
Testing Custom Rules is not an easy task to do - mainly due the lack of documentation for official APIs. This section will present two approaches for dealing with this. The main goal of this project is to create custom rules that will be run against real files, therefore, test files will be necessary for testing them. They can be places in src/test/resources folder from your Lint Java Library Module;
Approach 01: LintDetectorTest
Make sure you've added all test dependencies - checkout sample project's build.gradle;
Copy EnhancedLintDetectorTest.java and FileUtils.java into your project's test directory;
There is a known bug with Android Studio that prevents it from seeing files from src/test/resources folder, these files are a workaround for that;
EnhancedLintDetectorTest.java should return all issues that will be subject of tests. A nice way to do so is getting them from Issue Registry;
Create a test class that extends from EnhancedLintDetectorTest.java;
Implement getDetector() method returning an instance of the Detector to be tested;
Use lintFiles("test file path taking resources dir as root") to perform the check of the Custom Rules and use its result object to assert the tests;
Note that LintDetectorTest.java derives from TestCase.java, therefore, you're limited to JUnit 3.
Approach 02: Lint JUnit Rule
You might have noticed that Approach 01 might be a little overcomplicated, despite the fact that you're limited to JUnit 3 features. Because of that GitHub user a11n created a Lint JUnit Rule that allows the test of Custom Lint Rules in a easier way that counts with JUnit 4 and up features. Please, refer to his project README.md for details about how to create tests using this apprach.
Currently, Lint JUnit Rule do not correct the root dir for test files and you might no be able to see the tests passing from the IDE - however it works when test are run from command line. An issue and PR were created in order to fix this bug.
I'm not sure how to use the AST Api, however I'm personally using Psi and this is one of my lint checks that are working for me.
public final class RxJava2MethodCheckReturnValueDetector extends Detector implements Detector.JavaPsiScanner {
static final Issue ISSUE_METHOD_MISSING_CHECK_RETURN_VALUE =
Issue.create("MethodMissingCheckReturnValue", "Method is missing the #CheckReturnValue annotation",
"Methods returning RxJava Reactive Types should be annotated with the #CheckReturnValue annotation.",
MESSAGES, 8, WARNING,
new Implementation(RxJava2MethodCheckReturnValueDetector.class, EnumSet.of(JAVA_FILE, TEST_SOURCES)));
#Override public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
return Collections.<Class<? extends PsiElement>>singletonList(PsiMethod.class);
}
#Override public JavaElementVisitor createPsiVisitor(#NonNull final JavaContext context) {
return new CheckReturnValueVisitor(context);
}
static class CheckReturnValueVisitor extends JavaElementVisitor {
private final JavaContext context;
CheckReturnValueVisitor(final JavaContext context) {
this.context = context;
}
#Override public void visitMethod(final PsiMethod method) {
final PsiType returnType = method.getReturnType();
if (returnType != null && Utils.isRxJava2TypeThatRequiresCheckReturnValueAnnotation(returnType)) {
final PsiAnnotation[] annotations = method.getModifierList().getAnnotations();
for (final PsiAnnotation annotation : annotations) {
if ("io.reactivex.annotations.CheckReturnValue".equals(annotation.getQualifiedName())) {
return;
}
}
final boolean isMethodMissingCheckReturnValueSuppressed = context.getDriver().isSuppressed(context, ISSUE_METHOD_MISSING_CHECK_RETURN_VALUE, method);
if (!isMethodMissingCheckReturnValueSuppressed) {
context.report(ISSUE_METHOD_MISSING_CHECK_RETURN_VALUE, context.getLocation(method.getNameIdentifier()), "Method should have #CheckReturnValue annotation");
}
}
}
}
}
Checkout the many more I wrote here.
I've got a basic setup using Robospice with Retrofit in a shell Android application making REST calls, parsing JSON response into a POJO and that I can then use to render in an Activity. I now want to use TLS only for transport security (not SSL). I've read that Retrofit with OkHttp can be used to achieve this but I don't know where to make the update in my code.
I have a basic interface:
public interface RandomAPI {
#GET("/users")
List<User> getUsers(#Path("owner") String owner, #Path("repo") String repo);
#GET("/users/{userid}")
User getUser(#Path("userid") int userID);
}
I have a Service:
public class RandomService extends RetrofitGsonSpiceService {
private final static String BASE_URL = "http://jsonplaceholder.typicode.com";
#Override
public void onCreate() {
super.onCreate();
addRetrofitInterface(RandomAPI.class);
}
#Override
protected String getServerUrl() {
return BASE_URL;
}
}
and finally a request:
public class RandomRequest extends RetrofitSpiceRequest<User, RandomAPI> {
private int userID;
public RandomRequest(int userID) {
super(User.class, RandomAPI.class);
this.userID = userID;
}
#Override
public User loadDataFromNetwork() throws Exception {
return getService().getUser(userID);
}
}
I'm guessing I need to update the Service but not really sure how. I really like the simplicity of this pattern so would like to keep it if possible. I can drop the OkHttp jars into the application but I don't know how to get at the actual implementation of the service, or how to add my custom one so that all requests use it.
Has any one had experience with this that could share some code snippets or point me to an example?
~~ EDIT ~~
Looking into the API for Robospice, looks like my request can just extend SpiceRequest, then within the loadFromNetwork() method I just do plain Retrofit and OkHTTP stuff. Is that the only way though? Thought there would be a way to set your own RestAdapter implementation in RetrofitSpiceService instead of just using the default.
So to do this is actually quite simple. Create a class which extends RetrofitGsonSpiceService and override the createRestAdapterBuilder() method.
e.g.
#Override
protected Builder createRestAdapterBuilder() {
RestAdapter.Builder builder = new RestAdapter.Builder()
.setEndpoint(SERVICE_URL)
.setRequestInterceptor(requestInterceptor);
return builder;
}
I want to make POST request with custom header. I can't find information how to do this using AA Rest API - https://github.com/excilys/androidannotations/wiki/Rest%20API .
Should I use ClientHttpRequestInterceptor, which is used for authenticated requests?
https://github.com/excilys/androidannotations/wiki/Authenticated-Rest-Client
Thanks for any help!
There is currently an open issue for this : https://github.com/excilys/androidannotations/issues/323
For now, the only way to do this is with a custom ClientHttpRequestInterceptor. Here is a little example :
#EBean
public class CustomHeaderInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] data, ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().add("myHeader", "value");
return execution.execute(request, data);
}
}
Then, you need to link it to the restTemplate, like this :
#EBean
public class MyService {
#RestService
RestClient restClient;
#Bean
MobileParametersInterceptor mobileParametersInterceptor;
#AfterInject
public void init() {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
interceptors.add(mobileParametersInterceptor);
restClient.getRestTemplate().setInterceptors(interceptors);
}
}
Indeed you have to use the ClientHttpRequestInterceptor for custom header.
Currently, it's the only way I know.
See the official documentation of Spring-Android for more informations about the RestTemplate.