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.
Related
I'm trying to create an android application in which I've to verify the user's email address by sending a verification code and re-directing the user to enter the code in the mobile application.
In this case, can you please explain how I can maintain a record that the user has verified his mail id. The problem what I'm facing is that when I store these details in Shared Preferences, the details get erased when the user exits the application. Therefore user is prompted to enter the verification code many times
Also whenever the user comes out of the my application, the user is automatically logged out. I'm storing the user details in Shared Preferences. Therefore whenever the application is started it asks for a login. Can you please explain how I can make the user stay logged-in until the user he himself opts to log-out.
Is there any way I can achieve these details without using Sqlite database???
Save the status in cache file. For that
Create InternalStorage.java
public final class InternalStorage{
private InternalStorage() {}
public static void writeObject(Context context, String key, Object object) throws IOException {
FileOutputStream fos = context.openFileOutput(key, Context.MODE_PRIVATE);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(object);
oos.close();
fos.close();
}
public static Object readObject(Context context, String key) throws IOException,
ClassNotFoundException {
FileInputStream fis = context.openFileInput(key);
ObjectInputStream ois = new ObjectInputStream(fis);
Object object = ois.readObject();
return object;
}
}
Now After the verification for the first time, create a file with status in cache
try {
// Save the status like verified
InternalStorage.writeObject(this, "cache_fileName", "verified");
} catch (IOException e) {
Log.e(TAG, e.getMessage());
} catch (ClassNotFoundException e){
Log.e(TAG, e.getMessage());
}
And in the MainActivity's OnCreate Method, check for the cache file value, If the file has value = "verified", then start the activity to which you want to go
/*Check For status From Cache File*/
try {
// Retrieve the file value
verify= (String) InternalStorage.readObject(this, "cache_fileName");
} catch (IOException e) {
Log.e("TAG", e.getMessage());
} catch (ClassNotFoundException e) {
Log.e("TAG", e.getMessage());
}
try{
if(verify.equals("verified")){
startActivity("specify_your_activity");
}
}catch (NullPointerException e){
}
And Put this in AndroidManifest file
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
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.
I can receive my mails with Imap with this code sample :
URLName server = new URLName("imaps://" + username + ":"+ password + "#imap.gmail.com/INBOX");
Session session = Session.getDefaultInstance(new Properties(), null);
Folder folder = session.getFolder(server);
if (folder == null)
{
System.exit(0);
}
folder.open(Folder.READ_ONLY);
Message[] messages = folder.getMessages();
But sometimes Imap doesn't give any service and at those times I want to use Pop but I couldn't use it with my code. It is different the other codes for using receive mail. But in Android only this code is working.
What should I change in this code to work with Pop?
First, there's a nice URLName constructor that takes all the component pieces as separate parameters, so you don't have to do string concatenation.
Switch from IMAP to POP3 requires changing the protocol name as well as the host name. See the JavaMail FAQ for examples. The protocol name is "pop3s" and the host name is "pop.gmail.com".
Finally, you should use Session.getInstance instead of Session.getDefaultInstance. Compare the javadocs for the two methods to understand why.
How about this one.Really worked for me!!(Source:here)
String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
Properties pop3Props = new Properties();
pop3Props.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY);
pop3Props.setProperty("mail.pop3.socketFactory.fallback", "false");
pop3Props.setProperty("mail.pop3.port", "995");
pop3Props.setProperty("mail.pop3.socketFactory.port", "995");
URLName url = new URLName("pop3", "pop.gmail.com", 995, "","youremailid#gmail.com",yourpassword);
Session session = Session.getInstance(pop3Props, null);
Store store = new POP3SSLStore(session, url);
try {
store.connect();
} catch (MessagingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Folder folder = null;
try {
folder = store.getDefaultFolder();
folder = folder.getFolder("INBOX");
} catch (MessagingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (folder == null) {
System.exit(0);
}
try {
folder.open(Folder.READ_ONLY);
} catch (MessagingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Try retreiving folder via store object.And also mention that the folder you wish to retreive is INBOX!Also note that in settings,port number is 995 form pop.(You may leave the first six lines as they are.)
I'm aware of the performance differences between Parcelable (fast) and Serializable (slow). However, I need to store certain application information persistently, not just within one lifecycle, thus onSaveInstanceState and associated methods utilising Parcelable objects aren't appropriate.
So I turned my attention to Serializable. Primarily I have AbstractList types to store - which is fine, since they implement Serializable. However, many of the types I store inside these are Parcelable but not Serializable, e.g. RectF.
I thought "no problem", since I can easily generate a Parcel via Parcelable.writeToParcel(parcel, flags) then call marshall() on it to create a byte[] which I can serialize and deserialize. I figured I'd use generics; create a SerializableParcelable<Parcelable> implements Serializable class, allowing a one-fit solution for all Parcelable types I wish to serialize. Then I would e.g. store each RectF inside this wrapper within ArrayList, and lo-and-behold the list and its Parcelable contents are serializable.
However, the API docs state that marshall() mustn't be used for persistent storage:
public final byte[] marshall ()
Returns the raw bytes of the parcel.
The data you retrieve here must not be placed in any kind of persistent storage (on local disk, across a network, etc). For that, you should use standard serialization or another kind of general serialization mechanism. The Parcel marshalled representation is highly optimized for local IPC, and as such does not attempt to maintain compatibility with data created in different versions of the platform.
So now I'm stuck. I can either ignore this warning and follow the route I've outlined above, or else circumvent the issue by extending each individual Parcelable I want to serialize and creating bespoke serialization methods, which seems extremely wasteful of time and effort.
Does anyone know of a 'correct' shortcut to serialize a Parcelable object without using marshall()? Or should I plough on without heeding the warning specified? Perhaps an SQLite database is the way to go, but I'm unsure and would like your advice.
Many thanks.
For any object you need to serialize you can use objectOutPutStream .By using this you can write objects into the file system of the device.So this can used to save Parcelable objects also.
Below is the code to save object to File System.
public static void witeObjectToFile(Context context, Object object, String filename) {
ObjectOutputStream objectOut = null;
FileOutputStream fileOut = null;
try {
File file = new File(filename);
if(!file.exists()){
file.createNewFile();
}
fileOut = new FileOutputStream(file,false);
objectOut = new ObjectOutputStream(fileOut);
objectOut.writeObject(object);
fileOut.getFD().sync();
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}finally {
if (objectOut != null) {
try {
objectOut.close();
} catch (IOException e) {
// do nowt
}
}
if (fileOut != null) {
try {
fileOut.close();
} catch (IOException e) {
// do nowt
}
}
}
}`
Inorder to read the Object use ObjectInputStream . Find the below code.
public static Object readObjectFromFile(Context context, String filename) {
ObjectInputStream objectIn = null;
Object object = null;
FileInputStream fileIn = null;
try {
File file = new File(filename);
fileIn = new FileInputStream(file);//context.getApplicationContext().openFileInput(filename);
objectIn = new ObjectInputStream(fileIn);
object = objectIn.readObject();
} catch (FileNotFoundException e) {
// Do nothing
}catch (NullPointerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
if (objectIn != null) {
try {
objectIn.close();
} catch (IOException e) {
// do nowt
}
}
if(fileIn != null){
try {
fileIn.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return object;
}`
Regards,
Sha
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.