Why doesn't console android java application work without ODEX file, at the same time when GUI application works good?

StackOverflow https://stackoverflow.com/questions/19683139

  •  01-07-2022
  •  | 
  •  

Question

I wrote two simple java programs for Android 4.1.2.

1) Console program:

public class console_hello_world
{
    console_hello_world() {
        System.out.println("Init!");
    }
    protected static int method() {
        System.out.println("Method!");
        return 0;
    }
    public static void main(String[] args)
    {   console_hello_world variable=new console_hello_world();
        variable.method();
        System.out.println("Hello World!");
    }
}

It was built in a such way (As you can see this script also launches this program):

#!/bin/bash

DX="~/Programs/android-sdk-linux/build-tools/18.0.1/dx"
REMOTE_PATH=/data/local/tmp

CLASS_NAME="console_hello_world"
javac "${CLASS_NAME}.java"
${DX} --dex --output="classes.dex" "${CLASS_NAME}.class"
zip "${CLASS_NAME}.zip" "classes.dex"

ADB="~/Programs/android-sdk-linux/platform-tools/adb"
"${ADB}" push "${CLASS_NAME}.zip" $REMOTE_PATH/
"${ADB}" shell mkdir $REMOTE_PATH/dalvik-cache

"${ADB}" shell "logcat -c"
"${ADB}" shell ANDROID_DATA=$REMOTE_PATH dalvikvm -cp "$REMOTE_PATH/${CLASS_NAME}.zip" ${CLASS_NAME}
"${ADB}" shell "logcat -d" > out.log

2) GUI application:

package com.example.my_app;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;

public class my_app_activity extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    /** Called when the user clicks the button */
    public void onButtonClickMethod(View view)
    {
        // do smth
        EditText editText = (EditText) findViewById(R.id.edit_message);
        String message = editText.getText().toString();
        message += " + something!\n";
        editText.setText(message);
    }
}

It was built in a standard way.

Each of application launches OK. The way I launch console program is in the bash script inserted before.

Then I removed ODEX files of each program:

/data/local/tmp/dalvik-cache/data@local@tmp@console_hello_world.zip@classes.dex

/data/dalvik-cache/data@app@com.example.my_app-1.apk@classes.dex

(It's OK - my GUI application name is "my_app-1.apk")

For console program I also removed "/data/local/tmp/dalvik-cache/" directory and created new one from root, access flags were changed to 771 (the same access flags "/data/dalvik-cache/" directory has). So Dalvik VM is not able to write to "/data/local/tmp/dalvik-cache/" if it wasn't launched from root.

So after all these steps:

1) GUI application still works OK. Dalvik VM process has no access to "/data/dalvik-cache/" and new ODEX file isn't been created.

2) Console application throws an exception:

Dalvik VM unable to locate class 'console_hello_world'
java.lang.NoClassDefFoundError: console_hello_world
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.ClassNotFoundException: console_hello_world
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:61)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
    ... 1 more

It is surprisingly for me.

Because I thought that ODEX file is just verified and optimized version of classes.dex file, that is in archive, and applications can work without it.

My GUI application proofs this theory. But console application doesn't.

Can somebody explain me, why doesn't console android java application work without ODEX file, at the same time when GUI application feels good?

UPDATE:

I tested my GUI application more thoroughly.

If I delete odex file "/data/dalvik-cache/data@app@com.example.my_app-1.apk@classes.dex" before I launch "my_app-1.apk" first time, "my_app-1.apk" doesn't work after that. Here is Android log (I modified dalvik little bit):

W/ActivityThread( 4133): Application com.example.my_app can be debugged on port 8100...
I/dalvikvm( 4133): dvmJarFileOpen. fileName = /data/app/com.example.my_app-1.apk; odexOutputName = (null)
I/dalvikvm( 4133): dvmOpenCachedDexFile. fileName = /data/app/com.example.my_app-1.apk; cacheFileName = /data/dalvik-cache/data@app@com.example.my_app-1.apk@classes.dex
E/dalvikvm( 4133): Dex cache directory isn't writable: /data/dalvik-cache
I/dalvikvm( 4133): Unable to open or create cache for /data/app/com.example.my_app-1.apk (/data/dalvik-cache/data@app@com.example.my_app-1.apk@classes.dex)
D/AndroidRuntime( 4133): Shutting down VM

But if I remove ODEX file after I launched my_app-1.apk several times before, everything is OK with my_app-1.apk. Also there is no "/data/app/com.example.my_app-1.apk" file reading in Android log. I guess, Android puts ODEX somewhere in RAM for optimization and then just sets a pointer to it when it's necessary.

So, Mr. fadden was right, and everyone that was agree with him was also right! :)

It would be great, if someone could answer am I right saying that Android leaves the application in RAM for optimization, and explain me what Service/application/etc... is responsible for this Android cheat.

Was it helpful?

Solution

The odex file is always required. The classes.dex must be extracted from the zip file, byte-swapped if necessary, and have some basic structural verification performed. For a GUI app, the package manager will recreate it automatically if necessary; the PM has the necessary permissions to update /data/dalvik-cache.

A full explanation of the process can be found in the Dalvik sources (dalvik/docs/dexopt.html). (Some of it is a bit out of date, but it's mostly correct.)

BTW, in your creation step, if you tell dx to create foo.zip or foo.jar instead of foo.dex it will automatically zip the output file for you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top