image/svg+xml $ $ ing$ ing$ ces$ ces$ Res Res ea ea Res->ea ou ou Res->ou r r ea->r ch ch ea->ch r->ces$ r->ch ch->$ ch->ing$ T T T->ea ou->r

Applications, processes and threads

Process

Order of precedence for the importance of an application (process):

  1. Foreground process
    • One of its activities is visible on the top of the screen
    • One of its BroadcastReceiver is currently running (onReceive method)
    • One of its Service has a running event method (onCreate, onStart, onDestroy)
  2. Visible process
    • One of its activities is partially visible
    • One of its Service is running as a foreground service
  3. Service process
    • One of its Service is running (but not as a foreground service)
  4. Cached process
    • A process that is kept in cache in memory

☞ An application that is used by an application with more priority inherits of its priority
Example: if an application A with a visible activity uses the ContentProvider of an application B, B will be considered also as a foreground process

Threads

Main thread

Utility threads

Secondary threads

Coroutines

Case of Thread.sleep and suspend fun delay:

Creation of a new project

With an IDE

With Android Studio (File > New Project wizard):

With the CLI

android create project 
	--target <target_ID> \
	--name <your_project_name> \
	--path path/to/your/project \
	--activity <your_activity_name> \
	--package <your_package_namespace>

From the API level 19, the options -g -v 0.10 (0.10 to be replaced by the Gradle version) allow to create a project managed by Gradle rather than Ant.

One can update a project from a previous SDK:

android update project \
	--name <project_name> \
	--target <target_ID> \
	--path <path_to_your_project>

Typical structure of a project

Project structure Source

A first activity: the traditional Hello World !

Building programmatically the user interface

We can build the user interface with Java code.

// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License

package fr.upem.coursand.helloworld;

import java.util.Date;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Gravity;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

/**
 * An Hello World example programmed in Java.
 * The simple Hello World layout is installed programmatically.
 */
public class HelloWorldJava extends Activity
{
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		// We must never forget to call the super method in every onX() method overridden in the activity
		super.onCreate(savedInstanceState);
		// We create ourselves the layout rather than loading it from a XML description
		LinearLayout layout = new LinearLayout(this); // New layout: container for the graphical elements
		layout.setOrientation(LinearLayout.VERTICAL);
		TextView tv = new TextView(this);
		tv.setText("Hello World at " + new Date()); // The string should be externalized as a resource
		tv.setGravity(Gravity.CENTER);
		LinearLayout.LayoutParams tvParams =
			new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT /*width*/, LayoutParams.WRAP_CONTENT /*height*/, 1/*weight*/);
		layout.addView(tv, tvParams);
		Button b = new Button(this);
		b.setText("Quit the activity");
		// We choose a weight of 1 for the TextView and 0 for the button (only the TextView will be resized)
		LinearLayout.LayoutParams buttonParams =
			new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 0);
		layout.addView(b, buttonParams);
		Button b2 = new Button(this);
		b2.setText("Restart activity");
		layout.addView(b2, buttonParams);
		// We add a listener for the click event of the button to finish the activity
		b.setOnClickListener(view -> finish());
		// We add a listener on a button for the activity to start a new instance of itself
		b2.setOnClickListener(view -> startActivity(new Intent(this, HelloWorldJava.class)));
		setContentView(layout);
	}
}

Building the user interface with a XML layout

Using a XML file to describe the layout is prefered:

<?xml version="1.0" encoding="utf-8"?>
<!-- Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License -->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="fr.upem.coursand.helloworld.HelloWorldXml">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="TextView" />


    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="quitActivity"
        android:text="@string/quit_the_activity" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="startNewActivityInstance"
        android:text="@string/restart_the_activity" />


</LinearLayout>

AndroidManifest.xml : example

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="fr.upemlv.helloworld"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="16" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppBaseTheme" >
        <activity
            android:name="fr.upemlv.helloworld.HelloWorld"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Some notes:

Resource management

Internationalization of HelloWorld

Android Studio proposes an embedded editor to internationalize the strings. One can also edit the XML files by hand.

File res/values/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello_world">Hello World UMLV!</string>
</resources>

File res/values-fr/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello_world">Bonjour le monde UMLV !</string>
</resources>

Using the string reference

tv.setText(R.string.hello_world);

String with placeholders

One can put printf-like pladeholders in the strings that can be replaced:

<string name="hello_world">Hello World to %s, you are %d year old!</string>

tv.setText(getString(R.string.hello_world, "foobar", 42));

Compilation of the project

Compilation chain

Some explanations to compile a project from command-line can be found in this tutorial

Using Gradle

Gradle configuration files

Build variants

Sets of sources

Example of a build.gradle file for a module

apply plugin: 'com.android.application'

android {
	compileSdkVersion 28
	buildToolsVersion "29.0.0"

	defaultConfig {
		applicationId 'com.example.myapp'
		minSdkVersion 15
		targetSdkVersion 28
		versionCode 1
		versionName "1.0"
	}
	
	buildTypes {
		release {
			minifyEnabled true // Enables code shrinking for the release build type.
			proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
		}
	}

  
  flavorDimensions "cost", "google"
  productFlavors {
    free {
      dimension "cost"
      applicationId 'com.example.myapp.free'
    }

    paid {
      dimension "cost"
      applicationId 'com.example.myapp.paid'
    }
    
    premium {
      dimension "premium"
      applicationId 'com.example.myapp.premium'
      // we can add a new source set in /app/src/premium for the additional features
    }
    
    withGoogleApi {
      dimension "google"
      
      dependencies {
		implementation 'com.google.android.gms:play-services-gcm:17.0.0'
		implementation 'com.google.android.gms:play-services-location:17.0.0'
	  }
	}
	
	withoutGoogleApi {
	  dimension "google"
	}
  }
}

dependencies {
    implementation project(":lib")
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation fileTree(dir: 'libs', include: ['*.jar'])
}

Paquetages d'applications Android

Fichier APK

Fichier AAR

Tester un projet

Logcat

DDMS

Tâches

Comportements spécifiques pour les instances d'activité

Ajout d'un intent-filter pour permettre le lancement depuis le launcher :

<activity ... >
    <intent-filter ... >
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    ...
</activity>

Cycle de vie d'une activité

Diagramme de cycle de vie

Listeners de changement d'état

Etat des activités en premier-plan en mode multi-fenêtré :

Exemple : activité qui loggue tous les événements du cycle de vie avec logcat et les affiche dans un TextView

// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License

package fr.upem.coursand.lifecycle;

import android.os.SystemClock;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import fr.upem.coursand.R;

public class LifeCycleActivity extends AppCompatActivity {

    private TextView logView;
    private long startTimestamp;

    protected void log(String message) {
        Log.i("lifeCycleEvent", message);
        logView.append((SystemClock.elapsedRealtime() - startTimestamp) + " ms, " + message + "\n");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        startTimestamp = SystemClock.elapsedRealtime(); // in millis
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_life_cycle);
        logView = findViewById(R.id.logView);
        log("onCreate");
    }

    @Override
    protected void onStart() {
        super.onStart();
        log("onStart");
    }

    @Override
    protected void onStop() {
        super.onStop();
        log("onStop");
    }

    @Override
    protected void onPause() {
        super.onPause();
        log("onPause");
    }

    @Override
    protected void onResume() {
        super.onResume();
        log("onResume");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        log("onRestart");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        log("onDestroy"); // will not see in the TextView
    }
}