Подтвердить что ты не робот

Странное назначение, TextView to Bundle, после декомпиляции, почему?

Я создал простое приложение, приложение-счетчик, которое при нажатии кнопки увеличивает число на единицу и обновляет текстовое представление. Код можно увидеть ниже:

public class MainActivity extends Activity {
    public static int count = 0;

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

        final TextView textView = (TextView) findViewById(R.id.count);
        textView.setText(Integer.toString(count));
        final Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                count++;
                textView.setText(Integer.toString(count));
            }
        });
    }
    ...
}

После декомпиляции того же приложения с dex2jar и jd-gui я получил следующий код:

public class MainActivity extends Activity {
    public static int count = 0;

    protected void onCreate(final Bundle paramBundle) {
        super.onCreate(paramBundle);
        setContentView(2130903040);
        paramBundle = (TextView)findViewById(2131296257);
        paramBundle.setText(Integer.toString(count));
        ((Button)findViewById(2131296256)).setOnClickListener(new View.OnClickListener() {
            public void onClick(View paramAnonymousView) {
                MainActivity.count += 1;
                paramBundle.setText(Integer.toString(MainActivity.count));
            }
        });
    }
    ...
}

В следующей строке:

        paramBundle = (TextView)findViewById(2131296257);
        paramBundle.setText(Integer.toString(count));

Как можно настроить систему textview на paramBundle? И почему это происходит? paramBundle имеет тип Bundle, а TextView не является подклассом Bundle, далее Bundle является окончательным в соответствии с декомпилированной версией. Что-то пошло не так при декомпиляции? Является ли информация из декомпилятора неправильной или почему мы получаем этот результат?


Edit:

# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
    .locals 3
    .param p1, "savedInstanceState"    # Landroid/os/Bundle;

    .prologue
    .line 17
    invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V

    .line 18
    const/high16 v2, 0x7f030000

    invoke-virtual {p0, v2}, Lcom/example/rawa/helloworld/MainActivity;->setContentView(I)V

    .line 20
    const v2, 0x7f090001

    invoke-virtual {p0, v2}, Lcom/example/rawa/helloworld/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v1

    check-cast v1, Landroid/widget/TextView;

    .line 21
    .local v1, "textView":Landroid/widget/TextView;
    sget v2, Lcom/example/rawa/helloworld/MainActivity;->count:I

    invoke-static {v2}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;

    move-result-object v2

    invoke-virtual {v1, v2}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V

    .line 22
    const/high16 v2, 0x7f090000

    invoke-virtual {p0, v2}, Lcom/example/rawa/helloworld/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v0

    check-cast v0, Landroid/widget/Button;

    .line 23
    .local v0, "button":Landroid/widget/Button;
    new-instance v2, Lcom/example/rawa/helloworld/MainActivity$1;

    invoke-direct {v2, p0, v1}, Lcom/example/rawa/helloworld/MainActivity$1;-><init>(Lcom/example/rawa/helloworld/MainActivity;Landroid/widget/TextView;)V

    invoke-virtual {v0, v2}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V

    .line 30
    return-void
.end method

Я определенно не специалист по smali, только новичок. Но я также расшифровал приложение, используя apktool, и получил код smali выше. По моему мнению, сохраненное состояние (paramBundle) загружается в p1 (= v3) и используется в onCreate, и оно никоим образом не используется в строке 20 или 21. Для меня это указывает на ошибку декомпрессии? Имейте в виду, что apktool позволяет снова создавать приложение, и при декомпиляции данные не теряются.

4b9b3361

Ответ 1

Причина в том, что тип локальных переменных изменился, но некоторые декомпиляторы не справляются с этим правильно.

Здесь ваш код onCreate декомпилируется с помощью dex2jar + javap:

  protected void onCreate(android.os.Bundle);
    Code:
       0: aload_0
       1: aload_1
       2: invokespecial #20                 // Method android/app/Activity.onCreate:(Landroid/os/Bundle;)V
       5: aload_0
       6: ldc           #21                 // int 2130903040
       8: invokevirtual #25                 // Method setContentView:(I)V
      11: aload_0
      12: ldc           #26                 // int 2131230720
      14: invokevirtual #30                 // Method findViewById:(I)Landroid/view/View;
      17: checkcast     #32                 // class android/widget/TextView
      20: astore_1
      21: aload_1
      22: getstatic     #12                 // Field count:I
      25: invokestatic  #38                 // Method java/lang/Integer.toString:(I)Ljava/lang/String;
      28: invokevirtual #42                 // Method android/widget/TextView.setText:(Ljava/lang/CharSequence;)V
      31: aload_0
      32: ldc           #43                 // int 2131230721
      34: invokevirtual #30                 // Method findViewById:(I)Landroid/view/View;
      37: checkcast     #45                 // class android/widget/Button
      40: new           #6                  // class com/example/test/MainActivity$1
      43: dup
      44: aload_0
      45: aload_1
      46: invokespecial #48                 // Method com/example/test/MainActivity$1."<init>":(Lcom/example/test/MainActivity;Landroid/widget/TextView;)V
      49: invokevirtual #52                 // Method android/widget/Button.setOnClickListener:(Landroid/view/View$OnClickListener;)V
      52: return

Здесь aload_0 - локальная переменная для объекта MainActivity, а aload_1 - локальная переменная для объекта Bundle. Источник проблемы возник из кода # 20, когда хранит ссылку только что извлеченного объекта TextView в локальную переменную 1 (astore_1), которая ранее хранила объект Bundle!

Это выполняется, так как объект Bundle не используется для остальной части метода, поэтому более эффективно повторно использовать его локальную переменную вместо новой переменной. Однако это также означает, что декомпиляторы должны работать с большим трудом для создания правильного кода Java.

Ответ 2

Полученный вами smali-код выглядит правильно. Запуск приложения также работает с исходным кодом. Однако код, предоставляемый jd-gui, даже не компилируется.

Мне стало любопытно и декомпилировало ваше приложение с помощью dex2jar. Я загрузил полученный MainActivity.class в этот сайт.

Интересная часть: Некоторые декомпиляторы (Procyon и Fernflower) генерировал правильный код и делал отдельные переменные как для Bundle, так и для TextView. JAD и CFR, однако сделал та же ошибка, что и jd-gui. Они оба использовали переменную Bundle для TextView.


Похоже, вы можете обвинить jd-gui в ошибке. К сожалению, я не могу сказать, почему это происходит.