0

I am working on an Application, which can work with external apks. For this i used the DexClassLoader , to load classes, from external apks into an classes-Array, and work with the classes like this:

getFragment(){
    for (Class<?> cls : classes) {
        Log.v("loadDexClasses", "Class loaded " + cls.getName());
        if (cls.getName().contains("OpenQuestionFragment")) {
            Method m = null;
            Fragment xb = null;
            try
            {
                Class[] cArg = new Class[3];
                cArg[0] = Integer.class;
                cArg[1] = String.class;
                cArg[2] = String[].class;
                m = cls.getMethod("getInstance",cArg);
                xb = (Fragment) m.invoke(null,INTNULL,STRINGNULL,STRINGARRAYNULL);
                showFragment(xb);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
                if(xb==null){
                    return;
                }
                if (xb.equals(ClassLoader.getSystemClassLoader()))
                    Log.v("loadDexClasses", "Same ClassLoader");
                else
                    Log.v("loadDexClasses", "Different ClassLoader");
            }
        }
    }
}

external apk:

public class OpenQuestionFragment extends Fragment{

//flags
final static int INTNULL = -1;
final static String STRINGNULL = null;
final static String[] STRINGARRAYNULL = null;

static View view;
static String setter;
static boolean run = true;
private static int edittextid;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    RelativeLayout rl = new RelativeLayout(getActivity());
    EditText et = new EditText(getActivity());
    edittextid = view.generateViewId();
    et.setId(edittextid);
    rl.addView(et);
    et.setTextSize(70);
    //blub
    et.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
    RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)et.getLayoutParams();
    layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT,RelativeLayout.TRUE);
    et.setLayoutParams(layoutParams);
    et.setHint("hier eingeben");
    view = rl; //inflater.inflate(R.layout.testlayout, container, false);
    if(setter!=STRINGNULL && setter != ""){
        ((EditText) view.findViewById(edittextid)).setText(setter);
    }
    //initializeListenerThread();
    return view;
}


public static OpenQuestionFragment newInstance(String seter){
    /*setter=seter;
    run = true;*/
    return new OpenQuestionFragment();
}

public static Fragment getInstance(int intloader, String stringloader, String[] arrayloader){
    setter = stringloader;
    return newInstance(null);
}

public String getResult(){
    EditText ET = (EditText) view.findViewById(edittextid);
    return ET.getText().toString();
}

public String getQuestionTag(){
    return "OpenQuestion";
}

public boolean isAnswered(){
    EditText ET = (EditText) view.findViewById(edittextid);
    if(ET.getText().toString()!=""){
        return false;
    }else{
        return true;
    }
}

@Override
public void onDestroy(){
    run = false;
    super.onDestroy();
}
}

and the proguard, to prevent unused methods to not be assembled:

-keepclassmembers class dexloader.openquestion.OpenQuestionFragment {
   public *;
}

i already know, that there isnt a problem with the loaded class, as the "newInstance" function can be called the EXACT same way, except tweaking the cArg-Array slightly, to match the sought after method, like follows:

Class[] cArg = new Class[3];
cArg[0] = Integer.class;
cArg[1] = String.class;
cArg[2] = String[].class;
m = cls.getMethod("getInstance",cArg);
xb = (Fragment) m.invoke(null,INTNULL,STRINGNULL,STRINGARRAYNULL); 

into

Class[] cArg = new Class[1];
cArg[0] = String.class;
m = cls.getMethod("newInstance",cArg);
xb = (Fragment) m.invoke(null,STRINGNULL);

When i start the Application, it still throws a NoSuchMethodException, when i call m.invoke();, which gets caught, and well... that isnt supposed to happen. The only difference, i can see is, that getInstance() isnt used, but the proguard-rule should have prevented that.

Help is appreciated

Cheers, Jacobus

PlatinTato
  • 378
  • 2
  • 19
  • Are you sure the API of the external apk corresponds with the actual version of apk in use? – D-Dᴙum Jan 13 '16 at 09:56
  • 1
    I find that problems like this are almost exclusively due to incorrect ProGuard settings. Although yours appears to be correct. You can easily test that by not running ProGuard on your external APK for test purposes. If it works after that, then you know there is a problem in your ProGuard settings. – Knossos Jan 13 '16 at 09:57
  • @Kerry the minSdkVersion in build.gradle was the same, the targetversions were 23, and 22. changing both to 22, didnt resolve anything :/ thanks for the idea tho – PlatinTato Jan 13 '16 at 10:02
  • @Knossos i doubt, that it is the proguard, as i tried it, without the proguard file, by removing the 3 lines, and even calling the functions manually, to make them not "unused" – PlatinTato Jan 13 '16 at 10:04

1 Answers1

1

I solved it myself, after trying literally everything. It turns out, that Integer.class != int it should have been

                Class[] cArg = new Class[3];
            cArg[0] = int.class;
            cArg[1] = String.class;
            cArg[2] = String[].class;
PlatinTato
  • 378
  • 2
  • 19