Android/Android Applications

Structure of an Android application
An Android application (commonly abbreviated to app) resides in a file with extension .apk. This is an extension of the .jar file format as traditionally used for Java applications. Among the contents of this file are:
 * AndroidManifest.xml—the app manifest, which tells the Android system about the components of the app, what Android versions it will run on, and what hardware features and permissions it needs. The permissions, in particular, are shown to the user when they want to install the app from the Google Play Store, so they can choose whether to accept or reject your app on that basis. If a particular Android system service is controlled by a permission, and your manifest does not list that permission, then your app will be prevented from accessing that service.
 * Compiled Java bytecode. However, instead of using Sun’s JVM .class format, this code is in Dalvik classes.dex format.
 * (Optional) native machine code. Since this has to be architecture-specific, the APK format allows for alternative versions of this to be supplied, under different subdirectories named armeabi, mips etc.
 * Application resources. This can include screen layouts, text strings (to allow localization for different languages/regions), icons, colour specifications, style definitions and various other things, mostly in XML format. Most of these can be created at runtime directly in the code, but keeping them in static form usually allows for easier management. In particular, it allows the Android system to automatically provide selection from alternative resources based on the system configuration, without having to include special cases in your code.

The difference between an app and an activity
The icons the user sees in the app tray correspond, not to applications or .apk files as such, but to activities. Basically, an activity is a single screen that the user interacts with. If an activity is marked as exported in the manifest, then the launcher is able to show the user an icon for it, that can be tapped to launch that activity. An app may contain other activities that are not exported, that are only started internally, perhaps (directly or indirectly) in response to user actions in an exported activity. If an app has more than one exported activity, then corresponding multiple icons will appear in the launcher.

ELF Executables
Android is built on a Linux kernel, and can run executables in the usual Linux format. However, you’ll note that the APK format includes no provision for installing such files, and such executables cannot easily access the usual Android class libraries. There are some standard executables included with every Android system, including <TT>Toolbox</TT>, which is a very basic command-line shell. You can get access to this from the SDK on a connected PC with the “ ” command. It is also possible to implement a terminal window running on the Android device itself; for an example of how to do this, see the source code for ConnectBot.

Security Model
An APK file must be digitally signed by the developer before it can be installed on an Android device. There is no need to officially register your signing key with Google or anybody: the signing key merely allows the device to tell whether two different APK files come from the same developer or not.

Once installed on a device, the app is assigned its own (dynamically-generated) Linux user ID. This allows the system to prevent apps from accessing each other’s data, except in carefully-authorized ways.

Process Concepts
Being based on a Linux kernel, Android supports standard POSIX-style processes and threads. However, the user doesn’t typically see applications in terms of these; instead, the Android UI works in terms of tasks and activities.

Typically, all the activities defined by an app run in a single process, and different apps run in different processes. However, this can be controlled by the programmer in various ways, by specifying the <TT>launchMode</TT> and <TT>taskAffinity</TT> for an activity. Also, the application <TT>process</TT> attribute allows two apps that trust each other (are signed by the same private key) to run in the same process, if the programmer desires.

Note that, in the Android docs where it says that an activity is “stopped” while in the background, all that means is that its <TT>onStop</TT> method has been called, and its UI is no longer considered to be active. Any processes and threads associated with the activity keep right on running as normal, and can continue performing any actions they like, though of course the only recommended UI-related operations would be the sending of notifications indicating that they would like some user attention.

And of course such background processes/threads are near the top of the priority queue for being killed if system resources run low.

The UI (Main) Thread
Note that your app process initially starts with a single thread. In particular, all UI calls must happen on this thread. With the obvious exception of the relevant Handler methods, and Activity.runonUiThread, the Android UI classes (views, widgets etc) are not thread-safe, and must not be invoked on any other thread. You are free to create additional processes and threads; the easiest way to manage background activities that need to coordinate with the UI (display progress updates, handle cleanup on cancellation etc) is to use an AsyncTask.

The UI thread runs a Looper, which you can also instantiate to run on your own threads. If the UI Looper does not get some CPU time within 5(?) seconds of a user event, you see the dreaded “Application Not Responding” (ANR) alert.

If you need to run short tasks that will not hold up the UI thread for too long, you can queue them to the Looper via Handlers (see the <TT>post</TT> and <TT>postAtTime</TT> methods). More time-consuming operations that cannot be split up into short-duration tasks should be relegated to a background thread.

Programmers familiar with Java from other platforms may be accustomed to using a TimerTask to schedule timed events, but these run on their own thread, so they are not safe for directly making UI calls. You can, however, call Activity.runonUiThread from a background thread.

Tasks And The Back Stack
A task is anything started by tapping an icon in the launcher. The launcher’s app tray is automatically populated with appropriate entries (i.e. main activities) from all installed apps, and these entries can be copied to home screens. It is also possible to add shortcuts to home screens. Typically a task is started in a new process, and if such a process is already running, then tapping the icon simply brings the existing process to the front. However, it is possible to alter both these behaviours.

The first (root) activity in a task can launch additional activities as appropriate. Indeed, any application you write other than the very simplest is likely to consist of more than one activity. Tasks can also launch other, entirely separate tasks, the difference being that these show up in the recent-tasks list as separate entries, whereas app-internal activities do not. As each new activity is started, it becomes the topmost one visible to the user, and the one previously topmost has its UI deactivated. These additional activities usually run in the same process as the one that started them, but again, this behaviour can be varied.

Pressing the standard Android Back key normally terminates the topmost activity, and makes the previously-topmost one active. Terminating the root activity of a task terminates the task and returns the user to whatever task was running before (perhaps the launcher).

Activities within a task are never reordered as long as they are running; the only way to bring an activity that is not at the top of the back stack of a task to the top is to terminate all the activities above it in that task. However, tasks can be reordered, since any of the most recent eight tasks launched can be brought to the front at any time simply by leaning on the Home key and tapping on one of the entries that appear.

Activities are launched via some varient of the <TT>startActivity</TT> calls available to subclasses of a Context (which includes your own Activities). The main argument to each of these calls is an intent.

Memory Usage
On an unrooted Android device, Java code is strictly limited in how much memory it can use. Java objects are restricted to a heap size of about 20MB, while Bitmap objects are limited to their own heap area of a similar size. Oh, and contrary to what it says on that page about the <TT>recycle</TT> method being “an advanced call, and normally need not be called”, I would recommend you always call <TT>recycle</TT> on bitmaps when you have finished with them; failure to do this is liable to make your app crash at some point with the dreaded message “ ”.

Native code is not arbitrarily limited in its memory usage, and is free to allocate all available memory.

Save/Restore Instance State
Activities and views can implement calls to save and restore their “instance state”. This is not the same as saving/restoring user data; this is purely part of the mechanism to make it look like your process has been running all along, even though the system needed to kill it either because it was running low on memory, or to relaunch it on an orientation change.

If your activity is frontmost, and the user presses the Back button, then that is generally considered to be a request to terminate the activity. The next time the user starts your activity, they will not normally expect it to resume precisely as though it had never terminated. In this situation, <TT>onSaveInstanceState</TT> and <TT>onRestoreInstanceState</TT> are not called.

If your activity is frontmost, and the user launches some other activity on top of it, then your <TT>onSaveInstanceState</TT> methods will be called along with <TT>onPause</TT>. If the user returns to your activity while it is still running, then <TT>onResume</TT> will be called as usual, but there is no need to call <TT>onRestoreInstanceState</TT>.

If, however, the system ran short of memory while your activity was not frontmost, it can be killed without the user noticing. Then, when the user returns to it, it will be relaunched, and <TT>onRestoreInstanceState</TT> will be called so you can make it look like you were running all along.

Also if the device changes between portrait and landscape orientation while your activity is frontmost, the default action, if your code does not specifically handle this itself, is for the system to kill and relaunch you. Provided your layouts are properly designed to handle both cases, this is often good enough.

Note that what constitutes “instance state” is entirely up to you. For example, a map viewer app might always revert to a fully-zoomed-out view every time the user launches it. So the scroll position and zoom magnification would be saved and restored as part of the instance state. Or you might decide that the app will remember its scroll offset and zoom magnification every time it is quitted and relaunched regardless, in which case save/restore instance state doesn’t have anything to do at all. As the writer of the app, it’s your choice.

You Can’t Kill Threads
Note that it is not possible for one thread to kill or abort another through the Java API: the relevant Thread.stop/destroy methods are not implemented, because of the risk they can leave the Dalvik VM in an inconsistent state.

This also applies to classes that build on Thread, like AsyncTask: once the doInbackground method starts executing on the background thread, it will run to completion, regardless of whether you pass <TT>true</TT> to cancel or not.

Of course, native code can bypass this restriction, with all the dangers that implies. And also, processes can always be killed in their entirety, because once the entire process state is gone, we don’t care about its consistency.

Writing Android applications in Java
Android applications are primarily written in the Java programming language, and are designed to run in isolation from each other so as to maintain a secure operating environment. Each application runs in its own instance of the Dalvik Virtual Machine and under its own Linux user.

In contrast to most (if not all) other mobile platforms, Android truly supports multitasking/threading; Thus multiple applications can run at the same time and each application can perform multiple operations at the same time. This means that the user can have its mail/news/et.c. application open and get notified when there arrives a new mail instead of constantly having to check for this herself.

Android applications are delivered in form of .apk-files (zip-files signed with jartool, just with another suffix). These packages hold all of the files needed for the application to operate, there is the classes.dex-file which is the file that gets executed inside the Dalvik VM, and the AndroidManifest.xml which is a binary representation of the plain-text XML Manifest. Often there is other resources as well, as drawables (images/sprites/icons) and XML-layouts.

The supported IDE for writing android Application is Eclipse, but it is by no means mandatory to use it.

The Android (Java) applications themselves consists of several classes that subclasses the Intent and Activity classes including Services and content providers.

Android applications are compiled into a .dex file format binary and is then packaged into an apk (zip archive) file.

Alternative programming languages
People write programs for Android in many languages.

We discuss writing programs for Android in JavaScript, HTML5, and CSS3 in another chapter of this book -- Android/PhoneGap.

Scripting Layer for Android (SL4A) supports many scripting languages.

Some people write Python apps for Android using Python For Android together with SL4A.

Other people write Python apps for Android using Kivy.

Alternative IDEs
A few people develop Java applications using an editor and compiler that runs on the Android device itself. We discuss the Terminal IDE in a later chapter -- Android/Terminal IDE.

Utility libraries
Some libraries which may help with coding; for testing libraries and systems, see the testing chapter.


 * ActionBarSherlock - supports modern (4.x) action-bar style interfaces on earlier Android releases, allowing one app to serve all versions.
 * AndroidAnnotations - code generator that allows the programmer to avoid having to write much of the boilerplate Android UI and asynchronous code.
 * SlidingMenus - provides for the 'slide panel right to reveal a menu' functionality.
 * android-query - simplifies UI and asynchronous coding.

Tutorials

 * Lars Vogel. "Android Development Tutorial": How to create Android applications with Eclipse.
 * Android Portal on the Embedded Linux Wiki has a collection of Android app development tutorials.

UserProfile class :-

package com.mock.denethanjanaperera.mymock;

import android.provider.BaseColumns;

public final class UserProfile {

private UserProfile{

}

public class Users implements BaseColumns{

public final static String TABLE_NAME = "UserInfo"; public final static String COLUMN_USERNAME = "username"; public final static String COLUMN_PASSWORD = "password"; public final static String COLUMN_GENDER = "gender"; public final static String COLUMN_DOB = "dateofBirth";

}

}

DBhelper class :-

package com.mock.denethanjanaperera.mymock.database;

import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper;

import com.mock.denethanjanaperera.mymock.User; import com.mock.denethanjanaperera.mymock.UserProfile;

import java.util.ArrayList;

public class DBHelper extends SQLiteOpenHelper {

private final static String DATABASE_NAME = "UserInfo.db";

public DBHelper(Context context) { super(context, DATABASE_NAME,null, 1); }

@Override public void onCreate(SQLiteDatabase sqLiteDatabase) {

String CREATE_TABLE = "CREATE TABLE " + UserProfile.Users.TABLE_NAME +" (" +               UserProfile.Users._ID + " INTEGER PRIMARY KEY," +                UserProfile.Users.COLUMN_USERNAME + " TEXT," +                UserProfile.Users.COLUMN_DOB + " TEXT," +                UserProfile.Users.COLUMN_GENDER + " TEXT," +                UserProfile.Users.COLUMN_PASSWORD + " TEXT )";

sqLiteDatabase.execSQL(CREATE_TABLE);

}

@Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

}

public Long addInfo(String username, String password){

SQLiteDatabase sqLiteDatabase = getWritableDatabase;

ContentValues cv = new ContentValues; cv.put( UserProfile.Users.COLUMN_USERNAME, username); cv.put( UserProfile.Users.COLUMN_PASSWORD, password );

Long rowId = sqLiteDatabase.insert(UserProfile.Users.TABLE_NAME, null,cv);

return rowId; }

public int updateInfo(String userId, String username, String password, String dob, String gender ){

SQLiteDatabase sqLiteDatabase = getWritableDatabase;

ContentValues cv = new ContentValues; cv.put( UserProfile.Users.COLUMN_USERNAME, username); cv.put( UserProfile.Users.COLUMN_PASSWORD, password ); cv.put( UserProfile.Users.COLUMN_GENDER, gender); cv.put( UserProfile.Users.COLUMN_DOB, dob);

String select = UserProfile.Users._ID + " = ?"; String args[] = {userId};

int count = sqLiteDatabase.update(UserProfile.Users.TABLE_NAME, cv, select,args);

return count;

}

public ArrayList readAllInfo{

SQLiteDatabase sqLiteDatabase = getReadableDatabase;

String[] projection = {

UserProfile.Users._ID, UserProfile.Users.COLUMN_USERNAME, UserProfile.Users.COLUMN_DOB, UserProfile.Users.COLUMN_GENDER, UserProfile.Users.COLUMN_PASSWORD };

String sortOrder = UserProfile.Users._ID + " DESC";

Cursor cursor = sqLiteDatabase.query(               UserProfile.Users.TABLE_NAME,                projection,                null,                null,                null,                null,                sortOrder        );

ArrayList<User> list = new ArrayList<>;

if (cursor.getCount > 0){

while(cursor.moveToNext){

User newUser = new User;

int id = cursor.getInt(cursor.getColumnIndexOrThrow(UserProfile.Users._ID)); String user = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_USERNAME)); String date = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_DOB)); String gen = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_GENDER)); String pass = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_PASSWORD));

newUser.setUserId(id+""); newUser.setUserName(user); newUser.setDateOfBirth(date); newUser.setGender(gen); newUser.setPassword(pass);

list.add(newUser); }       }

return list; }

public ArrayList readAllInfo(String userId, String userName){

String selection; String[] args = {""};

if(userId == null){

selection = UserProfile.Users.COLUMN_USERNAME + " LIKE ?"; args[0] = userName; }       else {           selection = UserProfile.Users._ID + " = ?"; args[0] = userId; }

SQLiteDatabase sqLiteDatabase = getReadableDatabase;

String[] projection = {

UserProfile.Users._ID, UserProfile.Users.COLUMN_USERNAME, UserProfile.Users.COLUMN_DOB, UserProfile.Users.COLUMN_GENDER, UserProfile.Users.COLUMN_PASSWORD };

String sortOrder = UserProfile.Users._ID + " DESC";

Cursor cursor = sqLiteDatabase.query(               UserProfile.Users.TABLE_NAME,                projection,                selection,                args,                null,                null,                sortOrder        );

ArrayList<User> list = new ArrayList<>;

if (cursor.getCount > 0){

while(cursor.moveToNext){

User newUser = new User;

int id = cursor.getInt(cursor.getColumnIndexOrThrow(UserProfile.Users._ID)); String user = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_USERNAME)); String date = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_DOB)); String gen = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_GENDER)); String pass = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_PASSWORD));

newUser.setUserId(id+""); newUser.setUserName(user); newUser.setDateOfBirth(date); newUser.setGender(gen); newUser.setPassword(pass);

list.add(newUser); }       }

return list; }   public int deleteInfo(String username){

SQLiteDatabase sqLiteDatabase = getReadableDatabase;

String select = UserProfile.Users._ID + " = ?"; String [] args = {username};

int deleteRows = sqLiteDatabase.delete(UserProfile.Users.TABLE_NAME, select, args);

return deleteRows;

}   }

Home class :-

package com.mock.denethanjanaperera.mymock;

import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast;

import com.mock.denethanjanaperera.mymock.database.DBHelper;

import java.util.ArrayList;

public class Home extends AppCompatActivity {

private Button login, reg; private EditText uname, password; private DBHelper dbHelper;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home);

dbHelper = new DBHelper(this);

login = findViewById(R.id.btnLogin); reg = findViewById(R.id.btnReg); uname = findViewById(R.id.etHUname); password = findViewById(R.id.etHPassword);

login.setOnClickListener(new View.OnClickListener {           @Override            public void onClick(View v) {

ArrayList<User> list = dbHelper.readAllInfo;

for(User u : list){

if(u.getUserName.equals(uname.getText.toString)){

if(u.getPassword.equals(password.getText.toString)){

Intent intent = new Intent(Home.this, ProfileManagement.class); intent.putExtra("id", u.getUserId); startActivity(intent); }else{ Toast.makeText(Home.this, "Invalid Username and Password", Toast.LENGTH_SHORT).show; }                   }                }            }        });

reg.setOnClickListener(new View.OnClickListener {           @Override            public void onClick(View v) {

String user = uname.getText.toString; String passwrd = password.getText.toString;

if ((user.isEmpty || passwrd.isEmpty)) { Toast.makeText(Home.this, "Enter Registration Info", Toast.LENGTH_SHORT).show; } else { dbHelper.addInfo(user, passwrd);

Toast.makeText(Home.this, "User Registered!", Toast.LENGTH_SHORT).show; }           }        });

}

}

profilManagemant class :-

package com.mock.denethanjanaperera.mymock;

import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.RadioButton;

import com.mock.denethanjanaperera.mymock.database.DBHelper;

import java.util.ArrayList;

public class ProfileManagement extends Activity {

private Button update; private EditText uname, dob, pass; private RadioButton male, female; private DBHelper dbHelper; private String userId;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_profile_management);

dbHelper = new DBHelper(this); Intent intent = getIntent; userId = intent.getStringExtra("id");

uname = findViewById(R.id.etUuser); dob = findViewById(R.id.etUdob); pass = findViewById(R.id.etUdob); update = findViewById(R.id.btnUpdate); male = findViewById(R.id.radioMale); female = findViewById(R.id.radioFe);

ArrayList<User> list = dbHelper.readAllInfo(userId, null);

for (User u : list){

uname.setText(u.getUserName); pass.setText(u.getPassword); dob.setText(u.getDateOfBirth);

if(u.getGender != null){

if(u.getGender.equals("Male")){

male.setChecked(true); }               else {                   female.setChecked(true); }           }        }

update.setOnClickListener(new View.OnClickListener {           @Override            public void onClick(View view) {

Intent intent = new Intent(ProfileManagement.this, EditProfile.class); intent.putExtra("id", userId); startActivity(intent); }       });    } }

EditProfile class :-

package com.mock.denethanjanaperera.mymock;

import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.Toast;

import com.mock.denethanjanaperera.mymock.database.DBHelper;

import java.util.ArrayList;

public class EditProfile extends AppCompatActivity {

private Button edit, delete, search; private EditText uname, dob, pass; private RadioGroup radioGroup; private RadioButton male, female; private String gender;

private DBHelper dbHelper; private String userId;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_edit_profile);

dbHelper = new DBHelper(this); Intent intent = getIntent; userId = intent.getStringExtra("id");

Toast.makeText(EditProfile.this, "User Id: " + userId, Toast.LENGTH_SHORT).show;

uname = findViewById(R.id.etEUser); dob = findViewById(R.id.etEdob); pass = findViewById(R.id.etEpassword); edit = findViewById(R.id.btnEdit);

delete = findViewById(R.id.btnDelete); search = findViewById(R.id.btnSearch);

radioGroup = findViewById(R.id.radio); male = findViewById(R.id.radEmale); female = findViewById(R.id.radEfemale);

ArrayList<User> list = dbHelper.readAllInfo(userId, null);

if (!list.isEmpty) {

for (User u : list) {

uname.setText(u.getUserName); pass.setText(u.getPassword); dob.setText(u.getDateOfBirth);

if (u.getGender != null) {

if (u.getGender.equals("Male")) {

male.setChecked(true); } else { female.setChecked(true); }               }            }        }

radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener {

@Override public void onCheckedChanged(RadioGroup radioGroup, int view) {

if(view == R.id.radEfemale){

gender = "Female"; }               else{

gender = "Male"; }           }        });

search.setOnClickListener(new View.OnClickListener {           @Override            public void onClick(View view) {

ArrayList<User> urs = dbHelper.readAllInfo(null, uname.getText.toString);

for (User u : urs){

userId = u.getUserId; uname.setText(u.getUserName); pass.setText(u.getPassword); dob.setText(u.getDateOfBirth);

if(u.getGender != null){

if(u.getGender.equals("Male")){

male.setChecked(true); }                       else {                           female.setChecked(true); }                   }                }            }        });

edit.setOnClickListener(new View.OnClickListener {           @Override            public void onClick(View view) {

String userName = uname.getText.toString; String date = dob.getText.toString; String pwrd = pass.getText.toString; if(female.isChecked){

gender = "Female"; }               else{

gender = "Male"; }

int count = dbHelper.updateInfo(userId, userName, pwrd, date, gender);

if(count > 0){

Toast.makeText(EditProfile.this, "Updated!", Toast.LENGTH_SHORT).show; }               else{

Toast.makeText(EditProfile.this, "Error in data Sending!", Toast.LENGTH_SHORT).show; }           }        });

delete.setOnClickListener(new View.OnClickListener {           @Override            public void onClick(View view) {

int count = dbHelper.deleteInfo(userId);

if(count > 0){

Toast.makeText(EditProfile.this, "Deleted!", Toast.LENGTH_SHORT).show; }               else{

Toast.makeText(EditProfile.this, "Something went wrong!", Toast.LENGTH_SHORT).show; }           }        });

} }

User class :-

package com.mock.denethanjanaperera.mymock;

public class User {

private String userId; private String userName; private String dateOfBirth; private String gender; private String password;

public User{ }

public String getUserId { return userId; }

public void setUserId(String userId) { this.userId = userId; }

public String getUserName { return userName; }

public void setUserName(String userName) { this.userName = userName; }

public String getDateOfBirth { return dateOfBirth; }

public void setDateOfBirth(String dateOfBirth) { this.dateOfBirth = dateOfBirth; }

public String getGender { return gender; }

public void setGender(String gender) { this.gender = gender; }

public String getPassword { return password; }

public void setPassword(String password) { this.password = password; }

}