I have an android app. I have a few users who have a recurring problem: When the app shuts down, every file the app saved is gone. Every folder created is gone. Everything is completely wiped back to square one.
I am carefully saving the game data during every transition and game event, so I am very confident that this is not a case of the user crashing out before the data can be written. Somehow, the data that IS being written but then it's just not persisting after the app is removed from memory.
So-- has anyone had this situation and solved it? The only thing I can imagine is that there's some kind of "filesystem.commit" command I need to call after writing the files, but I can't find that documented anywhere.
Please help!
(Edit) I'm using native code to read and write files. The code I use to write a file is this:
bool WriteFile(char *theFilename, char *theDataPtr, int theLen)
{
FILE* aFile=fopen(theFilename,"w+");
if(!aFile) {Alert("unable to create file %s with error %d", theFilename, errno);return false;}
if(aFile) fclose(aFile);
aFile=fopen(theFilename,"w+b");
if(!aFile) {Alert ("unable to open file %s", theFilename);return false;}
if (aFile)
{
fwrite(theDataPtr, 1, theLen,aFile);
fclose(aFile);
return true;
}
return false;
}
Note:No customers are reporting any alert popups, which are just normal Android message boxes. Also note that this code works on almost every other system-- there's just a few customers that get the wiped data, so I was wondering if it's some weird security or some extra step I need to do to be 100% compatible with all systems.
(Edit) One more piece of information... this is the Java code I use to get the storage path for the app... all files that I try to write are put in this folder.
private void SetFilePath()
{
String storagePath = getFilesDir().getAbsolutePath();
// SDCARD
try {
String storageState = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(storageState))
storagePath = getExternalFilesDir(null).getAbsolutePath();
} catch (Exception e) {
Log.v(IDS.LOG,
"No permission to access external storage, missing android.permission.WRITE_EXTERNAL_STORAGE");
}
SetFilePathNative(storagePath); // Tells the native code the path
mStorageDir = storagePath;
}
I want to monitor /proc/net/tcp6 file and to do that efficiently with FileObserver, however for some unknown reason onEvent() callback never called.
observerTcp6 = new FileObserver("/proc/net/tcp6", FileObserver.ALL_EVENTS) {
#Override
public void onEvent(int event, String path) {
Log.i("TAG", "onEvent");
}
};
observerTcp6.startWatching();
With regular File class parsing this files works perfect.
Could anyone help me here? :)
The reason of your failure is that /proc/net/tcp6 is not really a file ;-)
It only looks like a file (ex., you can "open" and "read" it), but actually whole /proc/* entries are an interfaces to various kernel statistics\data, represented as "pseudo-files" only for simplifying access to them.
So, you can not use any other file methods on them, except "open" and "read".
P.S.
Your question is Linux related one, not actually Android.
I'm using the book "Embedded Android".
I'm making a new System Service using AOSP(4.0.3_r1).
I want my system service to be registered in frameworks/base/core/java/android/content/app/ContextImpl.java so that I can use it through getSystemService() method.
The problem is, I can't find the app folder under content:androidroot/frameworks/base/core/java/android/content/app/ContextImpl.java
But, I found it in:androidroot/frameworks/base/core/java/android/app/ContextImpl.java
Are these 2 files the same? or is it just missing(the content/app folder)?
Any idea on what to do?
Karim wrote his book mostly orienting on Android 2.3.4 version. Something can be changed from this time. This is an example what has been changed.
Are these 2 files the same? or is it just missing(the content/app folder)?
These are the same files.
Any idea on what to do?
As I said the implementation has been changed. I looked into the code and here what you can change to make your code working (I can only suppose because I did not actually build my code). In the static block of ContextImpl class you need to add the following code:
registerService(ACCOUNT_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(OPERSYS_SERVICE);
IOpersysService service = IOpersysService.Stub.asInterface(b);
return new OpersysManager(service);
}});
You need to use SystemServer which holds all system services' names.
You should check this link out:
http://processors.wiki.ti.com/index.php/Android-Adding_SystemService
I am using InstrumentationTestCase to unit test a component of my application.
The component persists data to the internal storage and uses Context::fileList(); to retrieve the persisted files.
I experience the following problem: Using this method in the app (on the device) works perfectly fine. But when I try to (Android-)Unit-Test (also on the Device) with use of InstrumentationTestCase I get a NullPointerException inside the fileList() method. I digged into the android source and found out that getFilesDir() (see source here) returns null and causes this error.
The code to reproduce is the following:
public class MyTestCase extends InstrumentationTestCase
{
public void testExample() throws Exception
{
assertNotNull(getInstrumentation().getContext().getFilesDir()); // Fails
}
}
My questions are: Is this behaviour intended? What can I do to circumvent this issue? Am I using InstrumentationTestCase right or should I use something different?
I found this question but I'm not sure if this covers the same problem I have.
I think that you are right with keeping your test data separate from tested application.
You can fix problem with Null by creating files directory for Instrumentation app by executing the following commands
adb shell
cd /data/data/<package_id_of_instrumentation_app>
mkdir files
You can do above only on emulator or rooted device.
Then test from your question will not fail. I did it and also uploaded file named tst.txt to files dir, all below tests were successful:
assertNotNull(getInstrumentation().getContext().getFilesDir());
assertNotNull(getInstrumentation().getContext().openFileInput("tst.txt"));
assertNotNull(getInstrumentation().getContext().openFileOutput("out.txt", Context.MODE_PRIVATE));
But I think more convenient way to provide data to test project is to use assets of test project where you can simply save some files and open them:
assertNotNull(getInstrumentation().getContext().getAssets().open("asset.txt"));
or if you want to save some results of tests to the file you can use ExternalStorage:
File extStorage = Environment.getExternalStorageDirectory();
assertNotNull(extStorage);
#Blackbelt mentioned in his comment to use getTargetContext() instead of getContext(). I missed the comment, and after a few hours, of trying to figure out how to Realm.init() from an Android instrumented tests, I find out that I need the Context from getTargetContext()...(along the way, i tried to context.filesDir.mkdirs())
package com.github.ericytsang
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Test
class InstrumentedTest {
private val context = InstrumentationRegistry.getInstrumentation().targetContext
#Test
fun can_mkdirs() {
assert(context.filesDir.mkdirs())
}
}
Is there any way to make an Android application to download and use a Java library at runtime?
Here is an example:
Imagine that the application needs to make some calculations depending on the input values. The application asks for these input values and then checks if the required Classes or Methods are available.
If not, it connects to a server, downloads the needed library, and loads it at runtime to calls the required methods using reflection techniques. The implementation could change depending on various criteria such as the user who is downloading the library.
Sorry, I'm late and the question has already an accepted answer, but yes, you can download and execute external libraries. Here is the way I did:
I was wondering whether this was feasible so I wrote the following class:
package org.shlublu.android.sandbox;
import android.util.Log;
public class MyClass {
public MyClass() {
Log.d(MyClass.class.getName(), "MyClass: constructor called.");
}
public void doSomething() {
Log.d(MyClass.class.getName(), "MyClass: doSomething() called.");
}
}
And I packaged it in a DEX file that I saved on my device's SD card as /sdcard/shlublu.jar.
Then I wrote the "stupid program" below, after having removed MyClass from my Eclipse project and cleaned it:
public class Main extends Activity {
#SuppressWarnings("unchecked")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try {
final String libPath = Environment.getExternalStorageDirectory() + "/shlublu.jar";
final File tmpDir = getDir("dex", 0);
final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader());
final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("org.shlublu.android.sandbox.MyClass");
final Object myInstance = classToLoad.newInstance();
final Method doSomething = classToLoad.getMethod("doSomething");
doSomething.invoke(myInstance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
It basically loads the class MyClass that way:
create a DexClassLoader
use it to extract the class MyClass from "/sdcard/shlublu.jar"
and store this class to the application's "dex" private directory (internal storage of the phone).
Then, it creates an instance of MyClass and invokes doSomething() on the created instance.
And it works... I see the traces defined in MyClass in my LogCat:
I've tried on both an emulator 2.1 and on my physical HTC cellphone (which is running Android 2.2 and which is NOT rooted).
This means you can create external DEX files for the application to download and execute them. Here it was made the hard way (ugly Object casts, Method.invoke() ugly calls...), but it must be possible to play with Interfaces to make something cleaner.
Wow. I'm the first surprised. I was expecting a SecurityException.
Some facts to help investigating more:
My DEX shlublu.jar was signed, but not my app
My app was executed from Eclipse / USB connection. So this is an unsigned APK compiled in DEBUG mode
Shlublu's anwser is really nice. Some small things though that would help a beginner:
for library file "MyClass" make a separate Android Application project which has the MyClass file as only file in the src folder (other stuff, like project.properties, manifest, res, etc. should also be there)
in library project manifest make sure you have:
<application android:icon="#drawable/icon"
android:label="#string/app_name">
<activity android:name=".NotExecutable"
android:label="#string/app_name">
</activity>
</application>
(".NotExecutable" is not a reserved word. It is just that I had to put something here)
For making the .dex file, just run the library project as android application (for the compiling) and locate .apk file from the bin folder of the project.
Copy the .apk file to your phone and rename it as shlublu.jar file (an APK is actually a specialization of a jar, though)
Other steps are the same as described by Shlublu.
Big thanks to Shlublu for cooperation.
Technically should work but what about Google rules?
From: play.google.com/intl/en-GB/about/developer-content-policy-print
An app distributed via Google Play may not modify, replace or update
itself using any method other than Google Play’s update mechanism.
Likewise, an app may not download executable code (e.g. dex, JAR, .so
files) from a source other than Google Play. This restriction does not
apply to code that runs in a virtual machine and has limited access to
Android APIs (such as JavaScript in a WebView or browser).
I am not sure if you can achieve this by dynamically loading java code. May be you can try embedding a script engine your code like rhino which can execute java scripts which can be dynamically downloaded and updated.
sure, it is possible. apk which is not installed can be invoked by host android application.generally,resolve resource and activity's lifecircle,then,can load jar or apk dynamically.
detail,please refer to my open source research on github: https://github.com/singwhatiwanna/dynamic-load-apk/blob/master/README-en.md
also,DexClassLoader and reflection is needed, now look at some key code:
/**
* Load a apk. Before start a plugin Activity, we should do this first.<br/>
* NOTE : will only be called by host apk.
* #param dexPath
*/
public DLPluginPackage loadApk(String dexPath) {
// when loadApk is called by host apk, we assume that plugin is invoked by host.
mFrom = DLConstants.FROM_EXTERNAL;
PackageInfo packageInfo = mContext.getPackageManager().
getPackageArchiveInfo(dexPath, PackageManager.GET_ACTIVITIES);
if (packageInfo == null)
return null;
final String packageName = packageInfo.packageName;
DLPluginPackage pluginPackage = mPackagesHolder.get(packageName);
if (pluginPackage == null) {
DexClassLoader dexClassLoader = createDexClassLoader(dexPath);
AssetManager assetManager = createAssetManager(dexPath);
Resources resources = createResources(assetManager);
pluginPackage = new DLPluginPackage(packageName, dexPath, dexClassLoader, assetManager,
resources, packageInfo);
mPackagesHolder.put(packageName, pluginPackage);
}
return pluginPackage;
}
your demands is only partly of function in the open source project mentioned at the begining.
If you're keeping your .DEX files in external memory on the phone, such as the SD card (not recommended! Any app with the same permissions can easily overwrite your class and perform a code injection attack) make sure you've given the app permission to read external memory. The exception that gets thrown if this is the case is 'ClassNotFound' which is quite misleading, put something like the following in your manifest (consult Google for most up to date version).
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
</manifest>
I think #Shlublu answer is correct but i just want to highlight some key points.
We can load any classes from external jar and apk file.
In Any way, we can load Activity from external jar but we can not start it because of the context concept.
To load the UI from external jar we can use fragment. Create the instance of the fragment and embedded it in the Activity. But make sure fragment creates the UI dynamically
as given below.
public class MyFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup
container, #Nullable Bundle savedInstanceState)
{
super.onCreateView(inflater, container, savedInstanceState);
LinearLayout layout = new LinearLayout(getActivity());
layout.setLayoutParams(new
LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT));
Button button = new Button(getActivity());
button.setText("Invoke host method");
layout.addView(button, LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
return layout;
}
}