Create new instance after crash - android

I have problem when my application crash in my "D-Activity".
MainActivity -> A Activity -> B-Activity -> C-Activity -> D-Activity
I create instance GlobalClass in MainActivity.
public static synchronized GlobalClass getInstance()
{
if(instance==null)
{
instance=new GlobalClass();
}
return instance;
}
For instance:
My app crashes in "D Activity". Android restart my app from B Activity but I lost my data in GlobalClass. There were a lot of fields String and ArrayList. How do I get them back?

You can convert your GlobalClass instance to JSON using Gson library for example. After GlobalClass is first time instantiated you can save it's JSON representation to the SharedPreferences and each time you need it if it is null you can try to read it from SharedPreferences.
Example of saving to SharedPreferences:
/**
* #return This session's config data.
*/
public Config getConfig() {
Config config = new Gson().fromJson(mPreferences.getString(KEY_CONFIG, null), Config.class);
return config;
}
/**
* Save this session's config data.
*
* #param config
* Config data.
*/
public void setConfig(Config config) {
mPreferences.edit().putString(KEY_CONFIG, new Gson().toJson(config)).commit();
}
Example of getting/setting global value:
/**
* #return Application configuration parameters.
*/
public static Config getConfig() {
if (sConfig == null)
sConfig = sPreferences.getConfig();
return sConfig;
}
/**
* Set application configuration parameters.
*
* #param config
* Config to set.
*/
public static void setConfig(Config config) {
sPreferences.setConfig(config);
sConfig = config;
}

Related

Callback when saving a file

I'm a newbie and I want to use CameraView library, the "toFile" function has two parameters. What does "callback" mean here?
camera.addCameraListener(new CameraListener() {
#Override
public void onPictureTaken(PictureResult result) {
result.toFile(file, **callback**);
}
});
camera.takePicture();
It will notify you when the file has been written.
Extract from the source code:
/**
* Receives callbacks about a file saving operation.
*/
public interface FileCallback {
/**
* Notifies that the data was succesfully written to file.
* This is run on the UI thread.
* Returns a null object if an exception was encountered, for example
* if you don't have permissions to write to file.
*
* #param file the written file, or null
*/
#UiThread
void onFileReady(#Nullable File file);
}

Memory leak for static declaration of context and INSTANCE , how do I alter it?

Currently in my codebase I have the following class(part of it) where it shows me 2 memory leaks with the message " Do not place Android context classes in static fields (static reference to Myclass which has field context pointing to Context); this is a memory leak (and also breaks Instant Run)"
I am not sure what the alternative is. Is this a 100% memory leak ? I get the leak warning on "INSTANCE;" and "static" declaration for context. Any idea how to go about fixing it?
public enum Myclass {
INSTANCE;
public static final boolean TLS_ENABLED = true;
private static final String TAG = Myclass.class.getSimpleName();
private static final String SP = "My_class";
private static Context context;
public void init(Context context, String appKey, String appSecret) {
init(context, null, appKey, appSecret);
}
/**
* Initialize class
*
* #param context Application level context.
* #param apiUrl API url of backend server
* #param appKey Application key
* #param appSecret Application secret
* #throws IllegalArgumentException If activity instance will be passed as the context
* #throws IllegalArgumentException If application key is empty or null
* #throws IllegalArgumentException If application secret is empty or null
*/
public void init(Context context, String apiUrl, String appKey, String appSecret) {
if (null == context) { throw new NullPointerException(); }
if (!(context instanceof Application)) { throw new IllegalArgumentException("Supply my class with application context"); }
// if (TextUtils.isEmpty(apiUrl)) { throw new IllegalArgumentException("Api url can't be null or empty string"); }
if (TextUtils.isEmpty(appKey)) { throw new IllegalArgumentException("App key can't be null or empty string"); }
if (TextUtils.isEmpty(appSecret)) { throw new IllegalArgumentException("App secret can't be null or empty string"); }
this.apiUrl = apiUrl;
this.appKey = appKey;
this.appSecret = appSecret;
this.sp = context.getSharedPreferences(SP, Context.MODE_PRIVATE);
MyClass.context = context;
initManagers();
}
/**
* Initializes managers. This method must be called after constructor
* returns, as the managers during own initialization may use myclass.get()
* method.
*/
private void initManagers() {
accountManager = new AccountManager();
myclassApi = new MyclassApi(context, apiUrl);
contactManager = new ContactManager();
connectionManager = new ConnectionManager();
meetingListManager = new MeetingListManager();
}
/**
* Returns {#link Context} that was passed to
* {#link myclass#init(Context, String, String)}.
*
* #return
*/
public static Context getContext() {
return context;
}
/**
* Returns {#link SharedPreferences} instance.
*
* #return SharedPreferences
*/
public SharedPreferences getSp() {
return this.sp;
}
public static class Event<T> {
private State state = State.SUCCESS;
private Throwable t;
private T data;
private String errorMessage;
/**
* Event state. If event related to network request/response
* operations - state indicates the physical (not logical)
* success or fail of request.
*/
public enum State {
/**
* Indicates that attempt to get data or perform task successful
*/
SUCCESS,
/**
* Indicates that attempt to get data or perform task fails,
* and reason of fail is the incorrect request data
*/
FAIL,
/**
* Indicates that attempt to get data or perform task encounter an error
* mostly due to connection problem
*/
ERROR,
/**
* Indicates that attempt to get data or perform task was ignored
* according to internal state of event producer.
*/
IGNORED
}
}
It's safe to store application context in a static field, you can simple call context.getApplicationContext() on any context reference you get before storing it in a static field.
The application context is a singleton anyway and you cannot leak it.
This seems like warning from IDE if you are making sure that context will store only ApplicationContext, not Activity context you can suppress this warning using annotation.
#SuppressLint("StaticFieldLeak")
If you want to understand more about memory leaks you can check my only blog :) here

Consecutive Android Junit tests do not reflect the real data in the underlying database

Additional Information:
To clarify, the app under test uses a ContentProvider to access the database.
Edit:
If anyone is willing and able to help me debug this. The full project is available here. In the issue107-contentprovider branch, BaseballCardListAddCardsTest.
Question:
When I run two of my Android JUnit tests separately, they pass just fine. However, when I run them together, the first one passes and the second one fails. The problem appears to be that the first test run adds a row to the underlying database. tearDown() correctly deletes the database, but the second test still starts with the dirty data displayed in the ListView although the database does not contain the extra row. (I confirmed this using adb shell.) Does anyone have any ideas how I can fix this problem?
The Activity class being tested can be found here.
Here is my test code:
/**
* Tests for the {#link BaseballCardList} activity when the database contains
* data.
*/
public class BaseballCardListWithDataTest extends
ActivityInstrumentationTestCase2<BaseballCardList> {
/**
* Create instrumented test cases for {#link BaseballCardList}.
*/
public BaseballCardListWithDataTest() {
super(BaseballCardList.class);
}
/**
* Set up test fixture. This consists of an instance of the
* {#link BaseballCardList} activity, its {#link ListView}, and a populated
* database.
*
* #throws Exception
* If an error occurs while chaining to the super class.
*/
#Override
public void setUp() throws Exception {
super.setUp();
this.inst = this.getInstrumentation();
// Create the database and populate table with test data
InputStream cardInputStream = this.inst.getContext().getAssets()
.open(BBCTTestUtil.CARD_DATA);
BaseballCardCsvFileReader cardInput = new BaseballCardCsvFileReader(
cardInputStream, true);
this.allCards = cardInput.getAllBaseballCards();
cardInput.close();
this.dbUtil = new DatabaseUtil(this.inst.getTargetContext());
this.dbUtil.populateTable(this.allCards);
// Start Activity
this.activity = this.getActivity();
this.listView = (ListView) this.activity
.findViewById(android.R.id.list);
this.newCard = new BaseballCard("Code Guru Apps", 1993, 1, 50000, 1,
"Code Guru", "Code Guru Devs", "Catcher");
}
/**
* Tear down the test fixture by calling {#link Activity#finish()} and
* deleting the database.
*
* #throws Exception
* If an error occurs while chaining to the super class.
*/
#Override
public void tearDown() throws Exception {
this.dbUtil.deleteDatabase();
super.tearDown();
}
/**
* Check preconditions which must hold to guarantee the validity of all
* other tests. Assert that the {#link Activity} to test and its
* {#link ListView} are not <code>null</code>, that the {#link ListView}
* contains the expected data, and that the database was created with the
* correct table and populated with the correct data.
*/
public void testPreConditions() {
Assert.assertNotNull(this.activity);
BBCTTestUtil.assertDatabaseCreated(this.inst.getTargetContext());
Assert.assertTrue(this.dbUtil.containsAllBaseballCards(this.allCards));
Assert.assertNotNull(this.listView);
BBCTTestUtil.assertListViewContainsItems(this.inst, this.allCards,
this.listView);
}
/**
* Test that the {#link ListView} is updated when the user adds a new card
* which matches the current filter.
*
* #throws Throwable
* If an error occurs while the portion of the test on the UI
* thread runs.
*/
public void testAddCardMatchingCurrentFilter() throws Throwable {
this.testYearFilter();
Activity cardDetails = BBCTTestUtil.testMenuItem(this.inst,
this.activity, R.id.add_menu, BaseballCardDetails.class);
BBCTTestUtil.addCard(this, cardDetails, this.newCard);
BBCTTestUtil.clickCardDetailsDone(this, cardDetails);
this.expectedCards.add(this.newCard);
BBCTTestUtil.assertListViewContainsItems(this.inst, this.expectedCards,
this.listView);
}
/**
* Test that the {#link ListView} is updated when the user adds a new card
* after an active filter was cleared.
*
* #throws Throwable
* If an error occurs while the portion of the test on the UI
* thread runs.
*/
public void testAddCardAfterClearFilter() throws Throwable {
this.testClearFilter();
Activity cardDetails = BBCTTestUtil.testMenuItem(this.inst,
this.activity, R.id.add_menu, BaseballCardDetails.class);
BBCTTestUtil.addCard(this, cardDetails, this.newCard);
BBCTTestUtil.clickCardDetailsDone(this, cardDetails);
this.allCards.add(this.newCard);
BBCTTestUtil.assertListViewContainsItems(this.inst, this.allCards,
this.listView);
}
private List<BaseballCard> allCards;
private List<BaseballCard> expectedCards;
private Instrumentation inst = null;
private Activity activity = null;
private DatabaseUtil dbUtil = null;
private ListView listView = null;
private BaseballCard newCard = null;
private static final int TIME_OUT = 5 * 1000; // 5 seconds
private static final String TAG = BaseballCardListWithDataTest.class
.getName();
}
It appears that a ContentProvider's lifecycle is tied to that of an Application not of the Activity that acesses it. Also, from what I can tell, ActivityInstrumentationTestCase2 creates a single Application for all the tests; only the Activity is destroyed and restarted for each test. This means that the each test will share the same ContentProvider. This means that the database file is opened with the first access by the ContentProvider and closed only after all test methods in the ActivityInstrumentationTestCase2 have finished. Since the database file remains open between test cases, the data can be accessed even after the file is deleted from the underlying file system. My solution was to delete the rows of the database individually rather than deleting the entire database.

How to keep certain part of my app common in entire application

I want to have a list of certain important things(which I am fetching from server every 15 seconds) which I want to have constant(or common) in my entire application. So when I move to next activity by Intents(or any other methods) I should have the list all the time. Is it possible in android ??
I want different solutions which requires as less work as possible.
Please Help..
EDIT: I think I havent made myself clear. I am not worried about how to store data..I am asking as to how can I achieve a view in which only half of the screen changes(as we move from activity to activity) while other half remains constant(doesnt move). Can it be possible ??
Your application class instance is always accesible from any activity.
All you need to do is create the application class like this:
public class YourApp extends Application {
....
}
And then modify the following line in your app AndroidManifest.xml :
<application
android:name="your.package.YourApp"
Now you can access this class everywhere:
YourApp appInstance = (YourApp)getApplication();
Use the PreferencesManager like the one below, create your POJO to access the PreferencesManager.
// TODO: Auto-generated Javadoc
/**
* The Class PreferenceManager.
*/
public class PreferenceManager {
/** The Constant TAG. */
private static final String TAG = PreferenceManager.class.getSimpleName();
/** The default shared preferences. */
private static SharedPreferences defaultSharedPreferences = null;
/**
* Inits the.
*
* #param context the context
*/
public static final void init(Context context){
defaultSharedPreferences = android.preference.PreferenceManager.getDefaultSharedPreferences(context);
log("Initialize PreferenceManager!");
UserSettings.init(context);
}
/**
* Save.
*
* #param name the name
* #param value the value
*/
static final void save(String name,String value){
if( value != null ){
Editor edit = defaultSharedPreferences.edit();
edit.remove(name);
edit.putString(name, value);
edit.commit();
}else{
Editor edit = defaultSharedPreferences.edit();
edit.remove(name);
edit.commit();
}
}
/**
* Gets the.
*
* #param name the name
* #param defaultValue the default value
* #return the string
*/
public static final String get(String name,String defaultValue){
return defaultSharedPreferences.getString(name, defaultValue);
}
/**
* Save state.
*
* #param name the name
* #param state the state
*/
public static final void saveState(String name,Bundle state){
if( state != null && state.size() > 0 ){
Parcel parcel = Parcel.obtain();
parcel.writeBundle(state);
String encodeToString = Base64.encodeToString(parcel.marshall(), Base64.DEFAULT);
PreferenceManager.save(name, encodeToString);
}else{
PreferenceManager.save(name, null);
}
log("Saved state "+name);
}
/**
* Gets the state.
*
* #param name the name
* #return the state
*/
public static final Bundle getState(String name){
log("Get state "+name);
String encryptedValue = "";
try {
encryptedValue = PreferenceManager.get(name, "");
} catch (NullPointerException e) {
return new Bundle();
}
if( "".equals(encryptedValue) ){
return new Bundle();
}else{
byte[] decode = Base64.decode(encryptedValue, Base64.DEFAULT);
Parcel parcel = Parcel.obtain();
parcel.unmarshall(decode, 0, decode.length);
parcel.setDataPosition(0);
return parcel.readBundle();
}
}
/**
* Log.
*
* #param msg the msg
*/
private static final void log(String msg){
Log.d(TAG, msg);
}
}
/**
* The Class Settings.
*/
public class UserSettings {
/** The settings bundle. */
private final Bundle settingsBundle = new Bundle(1);
/**
* Save.
*/
public final void save() {
PreferenceManager.saveState(SETTINGS_STATE_NAME, settingsBundle);
}
/**
* Restore.
*/
final public void restore() {
settingsBundle.clear();
Bundle state = PreferenceManager.getState(SETTINGS_STATE_NAME);
if (state.size() == 0) {
settingsBundle.putAll(getDefaultValuesSettings());
} else {
settingsBundle.putAll(state);
}
}
final void reset() {
settingsBundle.clear();
}
/**
* Gets the settings.
*
* #return the settings
*/
public static UserSettings getSettings() {
return settings;
}
/**
* Inits the.
*
* #param ctx the ctx
*/
public static final void init(Context ctx) {
settings.restore();
setDeviceUniqueId(ctx, settings);
}
}
Example usage:
public class YourApplication extends Application {
....
onCreate(){
....
PreferenceManager.init(getBaseContext());
}
}
Where you need your data to be stored and retrieved use the methods like below.
UserSettings.getSettings().setUser(responseVal);
UserSettings.getSettings().save();
String response = UserSettings.getSettings().getUser();
If it's large amount of data, you can store your data using Shared Preferences or SQLite DB. If it is less amount of data then you can go for static variables. If you use static variables, when any crash occurs in the app that data may lost. Hence static variables usage is less preferable.
There are lot of ways to do this :
You can store them and put in your application's SQLite database (Data is Persistent till you delete the application or delete through your application code )
See the SQLite usage here
You use Cache them in the phone memory till the time your app runs.
See Cache usage here here
You can use SQLite database to store this data and then create singleton helper to read it.
Or you can use save your data in XML or JSON format as files, then parse them to read.
Or you can create class-container for one entity of your data, make it serializable and store in SharedPreferences as ArrayList<YourDataContainer>

Returning an AIDL interface implementation by reference across processes

I have a little Android project going on which involves some IPC where client Activities bind to my service.
I'm using AIDL for IPC and RPC which works pretty good, but I'm having trouble returning a service-side instantiated AIDL interface implementation to the clients:
When the client is running in the same process as the service -- meaning running the service locally -- everything works just fine.
But when client and service are seperated in different processes the method startLogSession, which is defined in ILogDroidBinder.aidl always returns null.
The other method implemented in this interface -- getSessionIds -- which returns a List containing ints, always works (locally and cross-process).
I'm taking a wild guess and suppose my ILogDroidSession implementation should also implement Parcelable, but that wouldn't work, because I can't parcel an object containg a reference to an SQLiteDatabase (or can I?).
Here is the relevant code.
I'd really be glad if someone could help me out here. Maybe I'm just missing a point somewhere, since this is my first Android project and I'm not quite involved yet.
ILogDroidSession.aidl (An implementation of this is what I want to return to the client):
package net.sourceforge.projects.logdroid;
interface ILogDroidSession {
/**
* Logs the given text to the error message channel of the current logging
* session.
* #param text Text to log.
*/
void logError(in String text);
}
ILogDroidBinder.aidl (The IBinder interface passed to the client's onServiceConnected):
package net.sourceforge.projects.logdroid;
import net.sourceforge.projects.logdroid.ILogDroidSession;
interface ILogDroidBinder {
/**
* Starts a new LogDroid session which handles all logging events.
* #param sessionName The name of the session.
* #return An instance of ILogDroidSession.
*/
ILogDroidSession startLogSession(in String sessionName);
/**
* Gets a list with all available LogSession ids.
*/
List getSessionIds();
}
LogDroidService.java (Relevant code from my service):
public class LogDroidService extends Service {
/**
* The binder interface needed for Activities to bind to the
* {#code LogDroidService}.
*/
private final ILogDroidBinder.Stub binder = new ILogDroidBinder.Stub() {
/**
* Starts a new LogDroidSession.
*/
public ILogDroidSession startLogSession(String sessionName) {
return LogDroidService.this.createSession(sessionName);
}
/**
* Gets all available session ids.
*/
public List<Integer> getSessionIds() {
return LogDroidService.this.getSessionIds();
}
};
/**
* The database connection to be used for storing and retrieving log entries.
*/
private LogDroidDb database;
#Override
public void onCreate() {
super.onCreate();
database = new LogDroidDb(getApplicationContext());
try {
database.open(); // opens as writable database
} catch ( SQLException ignorefornow ) {
}
}
#Override
public IBinder onBind(Intent ignore) {
return binder;
}
/**
* Creates a new LogDroidSession which will be returned to the user as a
* AIDL remote object.
* #param sessionName Name of the session.
* #return A new instance of ILogDroidSession
*/
ILogDroidSession createSession(String sessionName) {
LogDroidSession session = new LogDroidSession(database, sessionName);
session.addLoggingOccurredListener(this);
return session;
}
/**
* Retrieves all session ids.
* #return Array containing all LogDroidSession ids.
*/
ArrayList<Integer> getSessionIds() {
return database.getSessionIds();
}
}
MainActivity.java (Relevant client code):
public class MainActivity extends Activity {
private ILogDroidSession session;
private ILogDroidBinder binder;
private ServiceConnection con = new ServiceConnection() {
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
binder = ILogDroidBinder.Stub.asInterface(arg1); // always works
try {
// works locally but always returns null when cross-process
session = binder.startLogSession("TestSession");
// always works
List<Integer> ids = binder.getSessionIds();
} catch ( Exception ex) {
// no exceptions are thrown either when running locally or cross-process
Toast.makeText(getApplicationContext(), ex.getMessage(),
Toast.LENGTH_LONG).show();
}
}
public void onServiceDisconnected(ComponentName arg0) {
}
};
}
ILogDroidSession can be defined as just interface in java file, it shouldn't be in AIDL.
If the client and LogDroidService are running in different processes, LogDroidSession should be parcelable to send/receive over IPC.
Data that is exchanged across the processes should just be stream of bytes that both sender and receiver understands through a protocol.
I'm taking a wild guess and suppose my ILogDroidSession implementation should also implement Parcelable, but that wouldn't work, because I can't parcel an object containg a reference to an SQLiteDatabase (or can I?).
LogDroidSession can't be parceled here, add new functions to ILogDroidBinder that returns session related information (in the form of plain data types).

Categories

Resources