Странное назначение, 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 позволяет снова создавать приложение, и при декомпиляции данные не теряются.
Ответы
Ответ 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 в ошибке. К сожалению, я не могу сказать, почему это происходит.