I've hit a bit of a wall. Any help would be appreciated. I have an app that I want to use DexClassLoader to load another apk file.
Here is my code:
DexClassLoader dLoader = new DexClassLoader("/sdcard/download/test.apk","/sdcard/download",null,ClassLoader.getSystemClassLoader().getParent());
Class calledClass = dLoader.loadClass("com.test.classname");
Intent it=new Intent(this, calledClass);
it.setClassName("com.test", "com.test.classname");
startActivity(it);
Now I had already installed test.apk so when I ran the above code it
worked fine and launched the application. However I want to be able to
run this without test.apk being installed already (as that would
defeat the entire point of the application) . So I uninstalled it and
when I ran the my app again I get this error:
android.content.ActivityNotFoundException: Unable to find explicit
activity class {com.test/com.test.classname}; have you declared this
activity in your AndroidManifest.xml.
So I'm a bit stumped here. This activity is declared in the Manifest
of the apk I am trying to run. I can't declare it in my applications
Manifest. Any ideas?
Thanks,
Craig
Try using Android's PathClassLoader:
String packagePath = "com.mypackage";
String classPath = "com.mypackage.ExternalClass";
String apkName = null;
try {
apkName = getPackageManager().getApplicationInfo(packagePath,0).sourceDir;
} catch (PackageManager.NameNotFoundException e) {
// catch this
}
// add path to apk that contains classes you wish to load
String extraApkPath = apkName + ":/path/to/extraLib.apk"
PathClassLoader pathClassLoader = new dalvik.system.PathClassLoader(
apkName,
ClassLoader.getSystemClassLoader());
try {
Class<?> handler = Class.forName(classPath, true, pathClassLoader);
} catch (ClassNotFoundException e) {
// catch this
}
Although the question is old, I will answer because I struggled a bit to find a clear answer for your same question for myself. First, I would like to highlight that a clear requirement in your question is to load a class from an .apk that is not already installed on the device. Therefore, calling the package manager using getPackageManager() and providing it with the package path will clearly lead to NameNotFoundException because the .apk that has the package is not installed on the device.
So, the way to go about loading classes from an .apk file that is not installed on the device (i.e. you only have the .apk stored in a directory on your SDCARD) is by using DexClassLoader as follows:
1- Make sure you have the .apk file in a directory on your SDCARD. I've mine Offloadme.apk in the Download folder on my SDCARD.
2- Add read permission in your AndroidManifest.xml to allow your app to read from the manifest.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
3- Use the following definitions to define the path of the .apk, the class name inside the apk, and method name in that class that you would like to invoke:
final String apkFile =Environment.getExternalStorageDirectory().getAbsolutePath()+"/Download/Offloadme.apk";
String className = "com.khaledalanezi.offloadme.SimpleCalculator";
String methodToInvoke = "add";
4- Use the DexClassLoader to load the .apk and call the add method in the SimpleCalculator class using reflection as follows:
final File optimizedDexOutputPath = getDir("outdex", 0);
DexClassLoader dLoader = new DexClassLoader(apkFile,optimizedDexOutputPath.getAbsolutePath(),
null,ClassLoader.getSystemClassLoader().getParent());
try {
Class<?> loadedClass = dLoader.loadClass(className);
Object obj = (Object)loadedClass.newInstance();
int x =5;
int y=6;
Method m = loadedClass.getMethod(methodToInvoke, int.class, int.class);
int z = (Integer) m.invoke(obj, y, x);
System.out.println("The sum of "+x+" and "+"y="+z);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Note that in my simple example, I added two numbers using the add method available in the SimpleCalculator class loaded from the Offloadme.apk file stored on my SDCARD and I was able to print the correct answer which is 11.
You can't do that. Even if you're able to access classes from external file, Android still does not know them. And you don't run activities directly, but by requesting Android to run them, so they have to be registered/installed into system.
Related
I have loaded a external apk through DexClassLoader.
in a method will be invoked has a GUI load from .xml layout.
But i can not view this GUI (from external apk) through invoke method !!
In conclusion, I want to load a GUI from an external APK file through invoke method.
Please help me !!
p/s: sorry about my english!
final File _optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);
String _className;
String _methodToInvoke;
String _apkfilePath;
HashMap<String, String> hmData;
//Prepare
hmData = preWorkflow();
_className = hmData.get("className");
_apkfilePath = hmData.get("apkfilePath");
_methodToInvoke = hmData.get("methodToInvoke");
DexClassLoader dLoader = new DexClassLoader(_apkfilePath,
_optimizedDexOutputPath.getAbsolutePath(),
null, ClassLoader.getSystemClassLoader().getParent());
try {
Class<?> loadedClass = dLoader.loadClass(_className);
Object obj = (Object) loadedClass.newInstance();
Method m = loadedClass.getDeclaredMethod(_methodToInvoke);
m.invoke(obj);
} catch (IllegalAccessException e) {
e.getCause().printStackTrace();
} catch (InvocationTargetException e) {
e.getCause().printStackTrace();
}catch (NullPointerException e){
e.getCause().printStackTrace();
} catch(Exception ex) {
ex.printStackTrace();
}
In Android you don't have a unit 'GUI'. You are probably talking about an Activity. If you want to launch an Activity of another package there is no need for reflection. Simply send an Intent. http://developer.android.com/training/basics/intents/index.html
In my app, I have a list of objects that are stored with a FileOutputStream in the code below. Also, I have any settings in my app stored with SharedPreferences. Whenever I updated my app on the Google Play store for the first time (for those unfamiliar with the process, I upload the new APK), all objects were deleted for anyone using the app, and all settings set to default. Why did it do this and how can I have the objects stored where they don't disappear after update?
public ObjectStorage readListFromFile()
{
ObjectStorage temp = null;
String filename = "storefileobj";
FileInputStream fis;
try {
fis = getActivity().openFileInput(filename);
ObjectInputStream is = new ObjectInputStream(fis);
temp = (ObjectStorage) is.readObject();
is.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (StreamCorruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return temp;
}
public void updateStorage()
{
String filename = "storefileobj";
FileOutputStream fos;
try {
fos = getActivity().openFileOutput(filename, Context.MODE_PRIVATE);
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(mainObjectList);
os.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
For normal updates, your users would not have lost these values.
I believe that each of your users who lost their stored SharedPreference and saved file data were forced to delete their old application in order to install the new application from the Play Store. Otherwise, some of your keys or file formats must have changed.
Signature:
You certainly used Android's Signing Your Applications documentation in order to update at the Play Store, but perhaps the users in question were using an app signed with a different signature (such as a Debug Mode signature) that you had delivered to them separately. This could explain why they were forced to uninstall before updating at the Play Store, thus erasing all of the saved information.
Data Format:
Another alternative is that perhaps the Keys used for the SharedPreference values changed OR the file format or class structure for the ObjectStorage changed, making it impossible to read the old values. This would appear to the users as if the old values disappeared. Then, as the users saved values in the new format, your app would continue to work properly for them.
Summary:
One of the following must have happened:
Your users deleted before reinstall. Or,
Your stored data format changed between versions.
code snippet
void takeSnapShot()
{
Process process = null;
try
{
process = Runtime.getRuntime().exec("/system/bin/screencap -p /sdcard/snapshot/test_2.png" );
try
{
process.waitFor();
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I am gettin an empty file saved whereas If i use the same command through adb shell, i get my screen captured.
Any help will be appreciatable
Edit: my previous answer was mistaken, any app can use the screen capture command.
It might be a permission issue. Are you sure you have permission to write to sdcard?
Check this post, which covers your topic:
How to run android system app without root permisson?
I solved this by adding the following permission to my Manifest.
<uses-permission
android:name="android.permission.READ_FRAME_BUFFER"
tools:ignore="ProtectedPermissions"/>
NOTE: This is a protected permission, my application is a system app and also signed using the platform key.
I want to react to a situation that no file has been found using FileInputStream. When i ran an app that loads a file that doesn't exist it opens an android popup with force close. I would like to react to the situation by changing a text in a text view and saying that the file has not been found. i tried changing the exceptions to change a text view and show that a file has not been found and the app still crashes.
Here is the piece of code:
FileInputStream fis = null;
String collected = null;
try {
fis = openFileInput("test");
byte[] dataArray = new byte[fis.available()];
while (fis.read(dataArray) != -1){
collected = new String(dataArray);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
tv.setText(collected);
To ensure that an Android application does not force-close, your options are: a) do not do anything that will cause an exception, b) catch your exception with your own try/catch block, c) install an application level uncaught exception handler. Option a is not too feasible, c is not very helpful, and based on your code snippet you seem to be trying b -- however there appears to be another exception that you're not catching with this. The contents of logcat will tell you what exception, and the stack trace will lead to a point in your code which needs the try/catch.
What is the best way to determine a file type in Android?
I want to check if the given file is an image, a music file, etc.
Include some mime.types files in your assets folder (for example from /etc/mime.types).
Include activation.jar (from JAF) in your build path.
Use something like this:
try {
MimetypesFileTypeMap mftm =
new MimetypesFileTypeMap(getAssets().open("mime.types"));
FileTypeMap.setDefaultFileTypeMap(mftm);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Then use FileDataSource.getContentType() to obtain file types.
public static String getMimeType(String fileUrl) {
String extension = MimeTypeMap.getFileExtensionFromUrl(fileUrl);
final String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
return (mimeType != null) ? mimeType : "unknown";
}
A common way is by their extension only, or you can parse the header, but it should be too much slower and more complex for your apps.