android (9)


Setup CI in GitLab for an Android project

CI – continuous integration – is a practice of automated implementation and distribution of the programmer’s work. CI has many advantages, and features, among which we would note the following:

  • Unified codebase, into which developers branches are being merged – achieved using GitFlow
  • Building automation – assembly starts automatically after new code is merged into master or develop branch using the pipeline in GitLab
  • Testing automation – automatically starts testing as a stage in the pipeline
  • Convenient access to build artifacts
  • Automated deploy – an additional script can publish artifacts in application stores or along the distribution path.

CI, like any automation, eliminates the human factor and automates repetitive actions. Therefore, it is desirable to configure CI on any serious project. And in the case when there are several developers and releases happens frequently, CI must be configured necessarily.
Typically, CI configuration is the responsibility of the DevOps engineer. However, although most companies have such a person, he may not be available or may not be able to configure CI specifically for the Android project. That is why Android developers should be able to do this on their own. How do we do it? I will tell and show in this article.

Let’s begin.
From GitLab point of view, CI consists of:

  • Jobs, which describes what to do. For example to build or to test the code
  • Stages, which describes when to start the job

Usually there are the following stages:
build job called compile
test – starts tests
staging – deploy on stage
production – deploy on prod

All this information about jobs and stages we need to tell GitLab. We will do it using .gitlab-ci file, which we need to put in the root of Android project. It will describe all the steps, as well as execute the necessary scripts.

At first we need to specify the image, which will perform the job

image: openjdk:8-jdk

Next, in the before_script section we’ll write the scripts that the server must execute in order to configure the build environment.

before_script:
 - apt-get --quiet update --yes
 - apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1
 - wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_TOOLS}.zip
 - unzip -d android-sdk-linux android-sdk.zip
 - echo y | android-sdk-linux/tools/bin/sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" >/dev/null
 - echo y | android-sdk-linux/tools/bin/sdkmanager "platform-tools" >/dev/null
 - echo y | android-sdk-linux/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null
 - export ANDROID_HOME=$PWD/android-sdk-linux
 - export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/

There is no magic here, and if you are familiar with unix systems and their commands, then you should understand everything: we just tell the system to download and install necessary packages.
Be sure to include the SDK version numbers that the server must install. To do this, add a variables block above before_script

variables:
 ANDROID_COMPILE_SDK: [version_number]
 ANDROID_BUILD_TOOLS: [version_number]
 ANDROID_SDK_TOOLS: [version_number]

Next, specify the types of assembly steps in the stages block

stages:
 - build
 - test
 - deploy

Now we are ready to complete the assembly itself.
I recommend to use two types of builds – release and debug. You can make as many options as you like based on your tasks.
At first specify debug build

buildDebug:
 stage: build
 tags: [android]
 only:
 - develop
 - master
 - /^release.*$/
 cache:
 paths:
 - .gradle/caches
 variables:
 VAR_NAME: BUILD_NUMBER // autoincrement build number
 TOKEN: ${CI_PIPELINE_IID_TOKEN}

The only tag allows to trigger assembly in case of pushing to a specific branch or by tag, as we will see later.
The BUILD_NUMBER variable will be read using a token CI_PIPELINE_IID_TOKEN from Variables preferences in the CI block in the GitLab. It allows us to add an autoincrement build number to the version name. This approach is described in more detail here. And below I will show how to configure build.gradle to generate the version name and build number. In the meantime, continue with .gitlab-ci

Next block is the script

script:
 # готовим переменную с номером сборки
 - GITLAB_URL=$(echo ${CI_PROJECT_URL} |awk -F "/" '{print $1 "//" $2$3}')
 - "VAR=$(curl -s -f --header \"PRIVATE-TOKEN: ${TOKEN}\" \"${GITLAB_URL}/api/v4/projects/${CI_PROJECT_ID}/variables/${VAR_NAME}\" | jq -r '.value' ) "
 - let VAR=VAR+1
 - "curl -s -f --request PUT --header \"PRIVATE-TOKEN: ${TOKEN}\" \"${GITLAB_URL}/api/v4/projects/${CI_PROJECT_ID}/variables/${VAR_NAME}\" --form \"value=${VAR}\" "
 # записываем переменную во временный файл, из которого будет считывать этот номер в build.gradle
 - echo $VAR > build.version
 - chmod +wx build.version
 - sed -i 's/android.enableBuildCache=false/android.enableBuildCache=true/g' gradle.properties
 # и собственно собираем сборку
 - ./gradlew clean app:assembleDebug

Now it remains only to specify how and where to store artifacts, i.e. apk::

artifacts:
 when: always
 expire_in: 4 weeks
 paths:
 - app/build/outputs/apk/

Вот и все, а для релизной сборки укажем такой же блок, но stage и него будет deploy, а сборка будет собираться еще и с app:assembleRelease, и собираться такая сборка будет только при создании тэга в GitLab.

That’s all, and for the release build we will specify the same whole block, but stage will be deploy, and the assembly will also be built with app:assembleRelease, and such assembly will be triggered  only by tag in GitLab.

In addition, we need to configure automatic increment of build numbers.
To do so add next lines in build.gradle, which will generate versionCode and versionName based on the minimum version of the target api:

private Integer generateVersionCode() {
 def minSDK = rootProject.minSdkVersion * 1000000
 def major = rootProject.versionMajor * 10000
 def minor = rootProject.versionMinor * 100
 def patch = rootProject.versionPatch
 def versionCode = minSDK + major + minor + patch
 project.logger.debug('versonCode ', versionCode)
 return versionCode
 }

private String generateVersionName() {
 String versionName = "${rootProject.versionMajor}.${rootProject.versionMinor}.${rootProject.versionPatch}"
 versionName += '.' + getBuildNumberFromFile()
 return versionName
 }

Off course, in the root build.gradle you should have version numbers written in the form

 versionMajor = 1
 versionMinor = 0
 versionPatch = 0
 minSdkVersion = 23

Also add functions to get the build number from CI GitLab, and in the case of a local build – from git:

def getBuildNumberFromGit() {

try {
 def stdout = new ByteArrayOutputStream()
 exec {
 commandLine 'git', 'rev-list', '--all', '--count'
 standardOutput = stdout
 }
 return stdout.toString().trim()
 }
 catch (ignored) {
 return '?'
 }
}

// This method gets the build number from the special file that we generated above in the .gitlab-ci script
def getBuildNumberFromFile() {

File versionFile = file('../build.version')
 if (versionFile.exists()) {
   return versionFile.readLines().get(0).trim()
 } else {
   return getBuildNumberFromGit()
 }
}

It remains to call this method for the version name, as well as for the build number

defaultConfig {
 applicationId "ru.andreyaleev"
 minSdkVersion rootProject.minSdkVersion
 targetSdkVersion rootProject.targetSdkVersion
 versionName generateVersionName()
}

buildTypes {

applicationVariants.all { variant ->
 variant.outputs.each { output ->
   output.versionCodeOverride = generateVersionCode()
   output.outputFileName = "$setup.applicationId-${variant.versionName}.apk"
 }
 }
 ...

Done with the code, now you need to add the variable to GitLab, the idea is taken as described here

Go to Settings->CI/CD->Variables, and add BUILD_NUMBER and CI_PIPELIBE_ID_TOKEN


Set BUILD_NUMBER to 1, and into CI_PIPELIBE_ID_TOKEN put the value of the token that needs to be generated through the Access Tokens section of the profile

It remains to push the changes to GitLab, and it will automatically start Pipeline.
To create the release build that we configured above we need to create a Tag through Repository->Tags-> New Tag
Select the branch from which you want to create the assembly and run. All is ready!




RxJava. How to handle errors from multiple Observables?

Imagine you need to perform several parallel network request and you have Retrofit and Rx as instruments.
You may end up with zip or combineLatest operators:

    val firstObservable = ServerApi().firstRequest( requestParams1 )
    val secondObservable = ServerApi().secondRequest( requestParams2 )
    val thirdObservable = ServerApi().thirdRequest( requestParams3 )

    // combine observables into one
    Observable.combineLatest(
        firstObservable,
        secondObservable,
        thirdObservable,
        Function3<FirstResponse, SecondResponse, ThirdResponse, CombinedResult> { firstResponse, secondResponse, thirdResponse ->
            // combine responses and create result for return function Function3
            CombinedResult( firstResponse, secondResponse, thirdResponse )
        }
    )
    .flatMap { combinedResult ->
        // perform some intermediate logic with combined result if needed
        Observable.just(combinedResult) // return original observable unchanged
    }
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ combinedResult ->
        // end of request
        // show result
    }, {
       it.printStackTrace()
    })

There is a problem with this chain. When one of observables throw an exception, for example, HttpException, then the whole chain will be terminated and we would not be able to show combinedResult.
How to avoid this situation? We just need to handle errors correctly for every Observable in the combinable list.
There is a handy operator for this: onErrorReturn() Call it on the combineLatest parameters as follows:

    Observable.combineLatest(
        firstObservable
            .onErrorReturn { responseFromException(it) },
        secondObservable
            .onErrorReturn { responseFromException(it) },
    ....

onErrorReturn

onErrorReturn is a handy operator which emits new created object instead of terminating chain.

Use Faсtory Method pattern or just write a function called responseFromException(throwable: Throwable)) which will create an empty Response object with error for you:

    ...
    // FirstResponse, SecondResponse and ThirdResponse should inherit from BaseResponse
    fun <T : Any> responseFromException(throwable: Throwable): BaseResponse<T> {
        var errorResponse: BaseResponse<T> = BaseResponse()
        if (throwable is HttpException) {
            errorResponse.code = it.code()
            errorResponse.message = it.message()
        }
      return errorResponse
     }

That’s all. The rest code stay the same. With only one line added for each observable we now may expect the initial chain to work as expected: perform all request without terminal state.




Kodein: Inherit parent dependencies

When you override Kodein instance in activity, fragment or somewhere else, you will lose all dependencies from parent scope.
To prevent this, there is a extend method

Example

In my App I have settings instance I want to use in Activity

 
class App : Application(), KodeinAware {

   override val kodein = Kodein {
     import(androidXContextTranslators)
     bind<ISettings>() with singleton { Settings(this@App) }
   }

Now in activity:

class MainActivity : Activity(), KodeinAware {

    private val appKodein by kodein(App.instance)

    override val kodein = Kodein {
       // or
       // extend(appApplication.appKodein(), allowOverride = true)
       extend(appKodein)
       // now you have all dependecies, you initialized in App
       //...
    }

    private val settings: ISettings by instance() // here we have it. Able to use settings instance



Dependency Injection with Kodein

If you build your Android application with Kotlin and want to implement DI technique then take a look at Kodein framework. Today I will take a quick look at it with you. I will not dive deep into dependency injection itself in this post. If you are not familiar with DI pattern, search articles on Dagger 2 for more info.
I will show Kodein practical usage on a simple example. Let’s say you want to inject singleton retrofit instance somewhere.

First, add Kodein dependency to the gradle file:

 implementation "org.kodein.di:kodein-di-generic-jvm:6.1.0"
 implementation "org.kodein.di:kodein-di-framework-android-core:6.1.0"
 implementation "org.kodein.di:kodein-di-framework-android-x:6.1.0"

Implement KodeinAware interface in your Application class. You will have to override kodein val and implement Retrofit instance there:

class App : Application(), KodeinAware {

    override val kodein = Kodein {
        import(androidXContextTranslators)
        bind<Retrofit>() with singleton {
            Retrofit.Builder()
                .client(OkHttpClient().newBuilder().build())
                .baseUrl("htttp://example.com")
                .addCallAdapterFactory(CoroutineCallAdapterFactory())
                .build()
        }
    }
}

All dependencies always starts with

 bind<TYPE>() with

followed by “singleton”, “provides” or “factory”
singleton” speaks for itself, “provides” generates new instance every time. If you need custom instance, then use “factory“; like”provides” it creates new instance every time but with applied constructor.

After declaring Dependency let’s use it!
You may need to inject Retrofit instance in Activity/Fragment or, if you use MVP pattern, in presenter. Difference is context: when in presenter you do not have context by default, you need to pass it. Let’s start with Activity case first.

Again, you need to imlpement KodeinAware interface and get the instance:

class MainActivity : AppCompatActivity, KodeinAware{

    override val kodein: Kodein by kodein()

    private val retrofit: Retrofit by instance()
    ...
    // use retrofit

Now, if you want to use retrofit in presenter or in some other class that is not aware of Android context,
first pass Context instance to get Kodein instance. I did it with App instance.
Add this into App class:

    ...
    override fun onCreate() {
        super.onCreate()
        instance = this
    }
    
    companion object {
        lateinit var instance: App
            private set
    }
    ...

Now in presenter:

class MyPresenterImpl : IMyPresenter, KodeinAware {

    override val kodein by kodein(App.instance)

    override val kodeinContext = kcontext(App.instance)

    private val retrofit: Retrofit by instance()

    ...
    // use retrofit

For more info check out official documentation




Adapt Android application for Wear OS

In March 2018 Google made Android Wear platform re-branding. New name – Wear OS. According to statistics, currently in the world of smart watches the leader is the Apple’s watchOS, taking 16,2 %, whereas Wear OS has 7%. However, the smart watch market is still new and we definitely can expect the number of users to growth. This means, we may create apps and watchfaces for wearable gadgets.
Let’s create Wear OS module for an existing Android app. I have just the right one – same old Workout Stopwatch.

 

Create module

Create new module in android studio project and choose wearable app

In build.gradle of created module add wearable dependecies:

dependencies {
    implementation 'com.android.support:wear:27.1.1'
    implementation 'com.google.android.support:wearable:2.3.0'
    compileOnly 'com.google.android.wearable:wearable:2.3.0'
}

Wearable app may be in three states:

  • Completely independent of a phone app
  • Semi-independent (a phone app is not required and would provide only optional features)
  • Dependent on a phone app

For the first two, for independent ones, in Android Manifest add following for <application ../> tag:

 <meta-data
 android:name="com.google.android.wearable.standalone"
 android:value="true" />

otherwise, the application will not be available to users with iPhone.
That is, if this flag is false, then the application can be installed only on the device connected to the phone on which the Play Store is installed.

In addition, for the wear-module, you must add In AndroidManifest.xml <uses-feature> with the value:

<manifest>
...
 <uses-feature android:name="android.hardware.type.watch" />
...
</manifest>


Packaging

Previously, apk for Wear 1.0 were embedded phone APKs. Wear OS allows us separate apk compiled for Wear 1.0 and upload it directly into Play Store. This helps to decrease phone apk size and gives flexibility in app versioning and distribution. “When you upload a watch APK via the Play Console, you can update your Wear APK independently from the phone APK, and users receive updates via the watch Play Store”  more…

Our app will be independent apk, but this doesn’t mean that we cannot use existing code again. Just extract common logic into a library module, and import it into app and wear. Thus, in app and wear modules, only the UI part of the application remains basically.

 

UI

To create beautiful responsive applications in wear os there are new UI-elements . For example, BoxInsetLayout – automatically adapting to round and rectangular screens layout.

BoxInsetLayout      BoxInsetLayout

To create lists for wearable devices there is a RecyclerView analog – WearableRecyclerView

WearableRecyclerView

 

 

Summary

We created wearable module in our project and adapted existing application for Wear OS. Remember that because of hardware limitations watches are not designed for complex applications. They are ideal for notifications, quick messages, or simple applications. Keep this in mind, and publish new applications or Wear OS versions of existing apps on Google Play!

That’s all for today!




A/B Testing with Firebase. Simple. Fast.

Sometimes we need to test a new feature in our mobile app. For example, if you want to see how new design will affect retention and DAU, whether users like it or not. AB Testing helps us here.
In a simplified form, it looks like this: on the backend side, administrator exposes which part of the users will see option A (new design), and which part is version B (old design). The client-side requests the server which option, A or B, it should show. And then a regular if- or switch- check starts the necessary code.
Fortunately for many mobile developers, the multifunctional service Firebase provides among other the possibility of AB-tests.
To create an experiment open Firebase console.
Open your project. If you do not have a project yet, create one.
For clarity, I will add an experiment to change the color of the UI element in my stopwatch project.
So, on the left, in the section Grow select A/B Testing and then Create experiment->Remote config. Enter the name of the experiment, description, select our application and enter the percentage of the target audience

Press Next. We may add not only two, but several options. In my case, two is enough. Now add parameter red_circle_enabled. Set values for created varinats:

It remains to choose the goals by which we will monitor the effectiveness of the tests:

Press Review and start experiment.

Now we need to implement A/B logic on client side.

In Codelabs there is a good tutorial how to make it on Android
Everything we need belongs to com.google.firebase.remoteconfig, so make sure you have added next dependency in your build.gradle:

 implementation 'com.google.firebase:firebase-config:15.0.0'

Next, initialise Remote Config instance where you will implement A/B test logic (e.g. MainActivity):

FirebaseRemoteConfig mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();FirebaseRemoteConfigSettings firebaseRemoteConfigSettings =
 new FirebaseRemoteConfigSettings.Builder()
 .setDeveloperModeEnabled(true)
 .build();
// Initialise default data
Map<String, Object> defaultConfigMap = new HashMap<>();
defaultConfigMap.put("red_circle_enabled", false);
firebaseRemoteConfig.setConfigSettings(firebaseRemoteConfigSettings);
firebaseRemoteConfig.setDefaults(defaultConfigMap);
fetchConfig();

fetchConfig() requests config data from Firebase

public void fetchConfig() {
    long cacheExpiration = 3600;
    if (firebaseRemoteConfig.getInfo().getConfigSettings()
            .isDeveloperModeEnabled()) {
        cacheExpiration = 0;
    }
    firebaseRemoteConfig.fetch(cacheExpiration)
            .addOnSuccessListener(aVoid -> {
                firebaseRemoteConfig.activateFetched();
                applyRedCircleTest();
            })
            .addOnFailureListener(e -> {
                Log.w(TAG, "Error fetching config: " +
                        e.getMessage());
                applyRedCircleTest();
            });
}

applyRedCircleTest() directly implements the logic depending on the received A/B test value

private void applyRedCircleTest() {
    Boolean redCircleEnabled = firebaseRemoteConfig.getBoolean(Constants.AB_TEST_RED_CIRCLE);
    circleStrokeTextView.setRedCircleEnabled(redCircleEnabled.booleanValue());
}

That’s all! We added the A/B test to the application in 15 minutes using Firebase!




RxJava. ConcatMap for dependent Observables

Today I’m gonna tell how concatMap helps to transform an existing sequential dependent synchronous chain of methods into a reactive and multithreaded one.
Suppose we get a boolean value from a presenter, and perform some UI event:

  ...
  boolean state = mPresenter.getBooleanState(context)
  if(state) {
      showViewA();
    } else {
      showViewB();
  }

The class, that provides data, Presenter in our case, has a dependent method structure, the second depends on the result of the first:

public class MyPresenter<MyView> {

  ...

  private int getIntValue(Context context) {
      int retValue = someCalculationMethod(context);
      return retValue;
  }
  public boolean getBooleanState(Context context) {
      int intValue = getIntValue(context);
      return performSomeCalculationWith(intValue);
  }
}

When someCalculationMethod executes immediately, it might be run on the main thread. If it for example, server request, and we may expect some time delay, then it should be executed in a background thread.
With Rx we can easily achieve it, preliminary transformed methods getIntValue and getBooleanState into Observable:

  private Observable<Integer> getIntValue(Context context) {
      return Observable.fromCallable(() -> {
         int retValue = someCalculationMethod(context);
         return retValue;
      });
  }

  private Observable<Boolean> getBooleanState(Context context, Integer intValue) {
     return Observable.fromCallable(() -> {
        boolean retValue = performSomeCalculationWith(intValue);
        return retValue;
     });
  }

So, how do we get Observable from the getBooleanState without having getIntValue performed? All magic on merging these two Observables is made by concatMap operator:

  private Observable<Boolean> rxGetBooleanState(Context context) {
      Observable integerObservable = getIntValue(context)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());

      Observable retObservable = integerObservable
        .concatMap(intValue -> getBooleanState(context, intValue));
      return retObservable;
  }

ConcatMap works similarly to flatMap. Important distinctive feature is it keeps elements order.

Let’s check docs:

Returns a new Observable that emits items resulting from applying a function that you supply to each item emitted by the source Observable, where that function returns an Observable, and then emitting the items that result from concatenating those resulting Observables.

And look at the concatMap operator scheme:

concatMap

concatMap scheme

concatMap applies a function (Observable) that you supply to each item emitted by the original Observable, and then merges the results of that function applied to every item emitted by the original Observable, thus creating a new Observable saving original order from first Observable.

And now we’re only need to call our method from appropriate context:

Disposable disposable = mPresenter.rxGetBooleanState(this)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe { state ->
            if (required) {
                showViewA();
            } else {
               showViewB();
            }
        }

Update
Actually, the easiest way to execute dependant observables is to use .flatMap operator, as described here: https://github.com/ReactiveX/RxJava/issues/442


getIntValue(context).flatMap( intValue -> {
   return getBooleanState(context, intValue)
})



RxJava. share operator with RxBinding

Today I’ll tell you about a convenient reactive approach to handle EditText input. As an example, we will take a search string in a SearchView.

The task

Search usually takes a while. No matter if we requesting data from the server, or read the local database. User may input letters in the textfield much faster than the requests would work off. Therefore we need to settle some time threshold not making any requests more frequent than necessary. Besides, we may need to update some UI changes in real-time, without those time gaps.

Solution

Without Rx in Android we can use Runnable and Handler, which is not handy. But thanks to the RxBinding we may use the debounce operator and get an elegant solution:

RxSearchView.queryTextChanges(searchView) 
 .debounce(500, TimeUnit.MILLISECONDS) // delay for 500 ms
 .subscribe(query -> mPresenter.searchRequest(query));

Now, regardless of input speed, requests to the server will be performed no more often than twice a second.
But, what should we do, if we need to update UI on every letter input? For example, hint needs to be changed. Using the code above we would have debounced UI updates with 500 ms, what is not quite user-friendly.

Fortunately, in Rx there is the possibility to broadcast to multiple subscribers. In order to use it we need to call .share() on our Observable and voila!

Observable<String> sharedTextChanges = RxSearchView.queryTextChages(searchViw).share()
 
sharedTextChanges 
 .debounce(500, TimeUnit.MILLISECONDS) // use debounce 
 .subscribe(query -> mPresenter.searchRequest(query)); 
 
sharedTextChanges 
 .subscribe(query -> mPresenter.updateUI(query));

We subscribed to Observable twice: with and without debounce. Now UI refreshes on every letter input, whereas server request being performed no more frequent than 500 ms.

What’s under the hood?

Operator .share() is an wrapper for .publish().refcount(). They allow to “share” items emitted by a stream. Let’s look deeper.

Operator.publish( ) — transforms Observable into ConnectableObservable.

rxjava publish operator scheme

“ConnectedObservable” is an Observable, which is not emitting data until someone wouldn’t call .connect() operator on it.

Operator .refcount() controls multiple subscribers. According to the docs,

Returns an Observable that stays connected to this ConnectableObservable as long as there is at least one subscription to this ConnectableObservable.

RxJava operator refcount

refcount() operator knows how many subscribers are subscribed to the Observable and doesn’t disconnect from the source ConnectedObservable until all Observables are unsubscribed.

That’s all. Very effective and elegant, as always with Rx. Hope this post was useful for you!




Setup proxy in Android Studio

Recently I was need to setup Android Studio IDE on a windows host with corporate proxy. Topic isn’t new and has been discussed on stackoverflow and in other blogs However, this problem can not be solved immediately – it takes a lot of time and a bit of blood. So I decided to write this post with step-by-step instruction on IDE setup for Android-developer in proxy environment.

We will use windows PC. For linux algorithm would be the same.

After first run Android Studio suggests to configure a proxy:

Click Setup Proxy and enter proxy server address and your credentials:

You can check out your proxy server address with ipconfig (windows)

ipconfig /all | find /i "Dns Suffix"

 

You may check connection on same window. If everything is OK, move next.

After IDE starts next window appears where you should write your proxy settings again for http and https:


Also, you can set configurations via gradle.properties file:

systemProp.http.proxyPassword=<PASSWORD>
systemProp.http.proxyHost=<PROXY_URL>
systemProp.http.proxyUser=<LOGIN>
systemProp.http.proxyPort=<PORT>
systemProp.https.proxyPassword=<PASSWORD>
systemProp.https.proxyHost=<PROXY_URL>
systemProp.https.proxyUser=<LOGIN>
systemProp.https.proxyPort=<PORT>

But keep in mind that IDE’s settings proxy overrides your projects settings.

If you try to compile project now, it will likely fail with error message

SSLHandshakeException: sun.security.validator.ValidatorException: PKIX fix

Gradle tries to get to repositories servers without certificates. We need to add them into cacerts storage.

But before we will add new lines in gradle.properties:

systemProp.javax.net.ssl.trustStore=<ANDROID STUDIO PATH>\\jre\\lib\\security\\cacerts
systemProp.javax.net.ssl.trustStorePassword=changeit

Here we set path to cacerts storeage file. Default password is changeit. If you didn’t changed it, leave it as it is.

So, how do you add certificates to cacerts?

Setting certificates

After first start IDE suggest to accept certificates. You should accept them, but it may not help. We need to import certificates by hands into cacerts storage of IDE and JVM. To do so perform next steps:

  1. Download certificate. You may use web-browser or openssl
  2. Import certificate via keytool

To import downloaded on step 1 certificate on Windows PC  run as administrator cmd and type:

keytool -import -alias <alias> -keystore C:\Progra~1\Android\Android Studio3.0\jre\jre\lib\security\cacerts -file <path_to/certificate_file>

Also, add this certificate into other cacerts storages (JVM и Android Studio):

keytool -import -alias <alias> -keystore <path_to_studio>\.AndroidStudio3.0\system\tasks\cacerts -file <path_to/certificate_file>
 keytool -import -alias <alias> -keystore "C:\Progra~1\Java\jre_V.V.V\lib\security\cacerts" <path_to/certificate_file>

alternatively, instead of adding, we may just copy certificates from one storage to another with next command:

keytool -importkeystore -srckeystore <path_to_studio>\.AndroidStudio3.0\system\tasks\cacerts -destkeystore C:\Progra~1\Java\jre_V.V.V\lib\security\cacerts -v
 password changeit

After importing the certificates, clean the gradle cache in the C:\Users\<User>\.gradle folder and reboot the system.

Also, start Android Studio as administrator if you getting Access denied error when IDE tries to read cacerts file.

Build your project… Build successful!

If certificate import didn’t help, you can try to use regular http instead of https:

jcenter {
 url "http://jcenter.bintray.com/"
 }

Git

Besides gradle, you may face some problems with git also. To avoid 403 error change git global proxy settings. Run command:

 git config --global http.proxy http[s]://userName:password@proxyaddress:port

And if you see next error when trying to pull/push from/into Git server

SSL certificate problem: self signed certificate in certificate chain

then run next command from administrator:

 git config --system http.sslCAPath <path_to_studio>/.AndroidStudio<v.No>/system/tasks/cacerts

To be able to push/pull with Android Studio IDE’s Git also change Settings->Version Control->Git section SSH executable set Native

That’s all, we can work! Hope this article was useful for you. If you liked it, leave your comments below!