home.

tagged: android

Android: Live Data

Live data is a way for you to observe changes to data. Let's use it in a ViewModel:

import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.ViewModel;

public class MyViewModel extends ViewModel {
    public MutableLiveData<String> text = new MutableLiveData<>();

    public LiveData<String> getText() {
        return text;
    }

    public void setText(String text) {
        this.text.setValue(text);
    }
}

Instead of having a plain String we have a MutableLiveData object. And when we want to update data we use setValue.
 

getText() returns a LiveData object. Let's see how we use that in our activity or fragment:

MyViewModel vm = ViewModelProviders.of(this).get(MyViewModel.class);

vm.getText().observe(this, new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {
        ((TextView)findViewById(R.id.aTextView)).setText(s);
    }
});

We observe it. So when it changes we update our view.

android android-lifecycle


Android: RxJava BehaviorSubject

If you want to subcribe to something, and receive the last thing that was emitted, and then subscribe to further things, use BehaviorSubject:

BehaviorSubject<String> bs = BehaviorSubject.create();
bs.onNext("one");
bs.onNext("two");
bs.onNext("three");
bs.subscribe(value -> {
    Log.d("HIYA", "Found " + value);
});
bs.onNext("four");

It will print Found three and Found four only.

android-rxjava android


Android: View models

Android will save the state of a simple POJO between rotations if you use something called a View Model.
 
Let's first import it in our gradle file:

implementation "android.arch.lifecycle:extensions:1.1.0"
implementation "android.arch.lifecycle:viewmodel:1.1.0"

Then create a POJO that extends ViewModel:

import android.arch.lifecycle.ViewModel;

public class MyViewModel extends ViewModel {
    public String text = "";

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }
}

And then in our activity, you can initalise it:

MyViewModel vm = ViewModelProviders.of(activityOrFragment).get(MyViewModel.class);

Now anything you save in that POJO will be retained until the activity is destroyed.
 
We can't pass the POJOs constructors arguments, however. But you can give ViewModelProviders.of second factory argument.

MyViewModelFactory factory = new MyViewModelFactory("Develop");
final MyViewModel vm = ViewModelProviders.of(this, factory).get(MyViewModel.class);

This factory is responsible for creating the view models. And we can pass that a constructor argument. And we use that to give the view model a constructor argument:

public class MyViewModelFactory implements ViewModelProvider.Factory {

    private final String appVersion;

    public MyViewModelFactory(String appVersion) {
        this.appVersion = appVersion;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if(modelClass.isAssignableFrom(MyViewModel.class)) {
            return (T) new MyViewModel(appVersion);
        }
        throw new IllegalArgumentException("Not found that view model, sunshine.");
    }
}

android android-lifecycle


Android: Deep linking basics

Let's say you have an Activity in your manifest that has VIEW action and that's in the DEFAULT and BROWSABLE category:

<activity android:name=".TwoActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="https"
            android:host="someurl.com"
            android:path="/new" />
    </intent-filter>
</activity>

Note we have a new data tag. It has a scheme and a host and a path. It points to a URL.
 
Now if you make a <a> link to https://someurl.com/new and press on it in your browser, it will open the above activity.
 
If you want to open the deep link programmatically you can:

Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://someurl.com/new"));
startActivity(intent);

android


Android: A debug build signed with a constant keystore

Each debug build of your android application will be signed with the debug keystore.
 
This is a problem because in continous integration environments, like travis-ci or docker, the debug keystore is regenerated on each creation of the environment.
 
This means if someone has a debug version of your application, they won't be able to upgrade to a more recent version, because the keystores will be different.
 
To get around this, let's create and add a keystore to our repository for continuous integration environments. Create this in the app/ directory. And ensure the key password is the same as the keystore password.

keytool -genkey -v -keystore ci-key.keystore -alias ci-key-alias -keyalg RSA -keysize 2048 -validity 10000

Now in your app/build.gradle file add this to the android block:

signingConfigs {
  ci {
    keyAlias "ci-key-alias"
    keyPassword System.getEnv("CI_KEYSTORE_PASSWORD")
    storeFile file("ci-key.keystore")
    storePassword System.getEnv("CI_KEYSTORE_PASSWORD")
  }
}

We give it the alias and reference to the file we created. We'll get the keystore key from the environment, one injected into the contiuous integration environment, for example.
 
In the same android block, create a new build variant, initalised as the debug variable, called debug_with_ci_keystore:

buildTypes {
    debug_with_ci_keystore.initWith(buildTypes.debug)
    debug_with_ci_keystore {
        if(System.getenv("CI_KEYSTORE_PASSWORD")) signingConfig signingConfigs.ci
        minifyEnabled false
    }
...
}

We give the variant the ci signing config above if we have the correct environment variable, CI_KEYSTORE_PASSWORD. This means, if we don't, we only create app/build/outputs/apk/app-debug_with_ci_keystore-unsigned.apk, not the signed app-debug_with_ci_keystore.apk.
 
Now on ./gradlew build, if we have CI_KEYSTORE_PASSWORD set in our environment, we will create app/build/outputs/apk/app-debug_with_ci_keystore.apk, which will have a constant keystore.
 
The above obviously works for a release signing: just change the build type to release, although you may not want to keep your release keystore in your repository.

android android-keystore keytool

Page 1 of 16
next