save Intent after screen rotation - android

into my application i use an intent:
private Handler mHandler = new Handler();
.
.
mServiceIntent = new Intent(this, ObdGatewayService.class);
mServiceConnection = new ObdGatewayServiceConnection();
mServiceConnection.setServiceListener(mListener);
// bind service
Log.d(TAG, "Binding service..");
bindService(mServiceIntent, mServiceConnection,
Context.BIND_AUTO_CREATE);
here my activity at onCreate start a new service. this is my onDestroy:
#Override
protected void onDestroy() {
super.onDestroy();
mServiceIntent = null;
mServiceConnection = null;
mListener = null;
mHandler = null;
}
this is mServiceConnection:
public class ObdGatewayServiceConnection implements ServiceConnection{
private static final String TAG = "com.echodrive.io.ObdGatewayServiceConnection";
private IPostMonitor service = null;
private IPostListener listener = null;
public void onServiceConnected(ComponentName name, IBinder binder) {
service = (IPostMonitor) binder;
service.setListener(listener);
}
public void onServiceDisconnected(ComponentName name) {
service = null;
Log.d(TAG, "Service disconnesso.");
}
public boolean isRunning() {
if (service == null) {
return false;
}
return service.isRunning();
}
public void addJobToQueue(ObdCommandJob job) {
if (null != service)
service.addJobToQueue(job);
}
public void setServiceListener(IPostListener listener) {
this.listener = listener;
}
mListener is a listener from interface:
public interface IPostListener {
void fineTest(DatiTest risultati);
void startAcquisizione();
void aquisizioneTerminata();
void aquisizioneInterrotta(String motivo);
void connessioneCorretta();
void gpsStato(boolean stato);
}
my problem is.. how save all this code after rotation? thanks!

The recommended way to save state across rotations is to save them on the outState. This is accomplished by overriding the onSaveInstanceState method. This method gives you a Bundle outState object that you can add Parcelable and Serializable objects to. This should work fine for your Intent object since it implements Parcelable but it may not work for say Handler because it only extends Object.
Another solution is to make these members static. However, be very careful if you decide to do this. Make sure that the value of the static member never holds on to a Context or a view hierarchy, etc, or you could easily introduce memory leaks.
If neither of these is acceptable to you, there is the option suggested by Tushar. However, unless you're careful this will make your life very difficult very fast. A large reason why activities are destroyed and re-created is so that resources can be re-loaded. So if you have layouts, strings, colors, dimens, or basically any resource specifically for landscape, or tablets, or different versions, you'll have to reload the entire UI yourself.

Related

leakcanary stacktrace hard to understand

I have the below stack trace from leak canary with which I am not sure how my Activity is getting leaked
static LGCobtextHelper.mLGContext
references LGContext.mContext
references
ResourcesContextWrapperFactory$WebViewContextWrapper.mBase
references
com.*.*.activity.MyActivity.networkMonitor
references
com.*.*.NetworkMonitor.mPendingResult
references
android.app.LoadedApk$ReceiverDispatcher$Args.this$0
references
LoadedAok$ReceiverDispathcer.mContext
leaks MyActivity instance
MyActivity extends BaseActivity, which registers onResume() and unregisters onPause(), so not sure which leaks the activity
NetworkMonitor.java
public class NetworkMonitor extends BroadcastReceiver {
private final WebSocketClient webSocketClient;
private final ArmingHelper armingHelper;
private final ShutdownManager shutdownManager;
private final CameraThumbnailCache cameraThumbnailCache;
private final CameraAccessManager cameraAccessManager;
private final JoustLogger joustLogger;
private Activity registeredActivity;
private String currentNetworkName;
private List<NetworkStatusChangeListener> networkChangeListeners;
public interface NetworkStatusChangeListener {
void onNetworkUp();
void onNetworkDown();
}
public NetworkMonitor(WebSocketClient webSocketClient, ArmingHelper armingHelper, ShutdownManager shutdownManager, CameraThumbnailCache cameraThumbnailCache, CameraAccessManager cameraAccessManager, JoustLogger joustLogger) {
this.webSocketClient = webSocketClient;
this.armingHelper = armingHelper;
this.shutdownManager = shutdownManager;
this.cameraThumbnailCache = cameraThumbnailCache;
this.cameraAccessManager = cameraAccessManager;
this.joustLogger = joustLogger;
networkChangeListeners = new ArrayList<>();
}
// Activities *must* call this method in onResume() in order for
// the app to watch for network changes
public void startListeningForNetworkChanges(Activity registeringActivity) {
if (!(registeringActivity instanceof NetworkStatusChangeListener)) {
throw new IllegalArgumentException("Registering Activity must implement NetworkStatusChangeListener");
}
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intentFilter.addAction(GlobalConstants.ANDROID_NET_WIFI_WIFI_STATE_CHANGED);
registeringActivity.registerReceiver(this, intentFilter);
this.registeredActivity = registeringActivity;
registerListenerForNetworkChanges((NetworkStatusChangeListener)registeringActivity);
}
// Activities *must* call this method in onPause() in order to properly
// unregister the receiver that was set in onResume()
public void stopListeningForNetworkChanges(Activity registeringActivity) {
registeringActivity.unregisterReceiver(this);
unregisterListenerForNetworkChanges((NetworkStatusChangeListener)registeringActivity);
registeredActivity = null;
}
// Fragments can use this method to register for Network change updates, call in onResume()
public void registerListenerForNetworkChanges(NetworkStatusChangeListener listener) {
networkChangeListeners.add(listener);
}
// Fragments need to unregister in onPause()
public void unregisterListenerForNetworkChanges(NetworkStatusChangeListener listener) {
networkChangeListeners.remove(listener);
}
#Override
public void onReceive(Context context, Intent intent) {
checkNetworkConnection();
}
public void checkNetworkConnection() {
if (registeredActivity != null) {
final ConnectivityManager connectivityManager = (ConnectivityManager) registeredActivity.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnectedOrConnecting()) {
String newNetworkName = networkInfo.getTypeName();
if (currentNetworkName == null || !currentNetworkName.equals(newNetworkName)) {
Timber.d("Network(%s) Connected", newNetworkName);
// Our network was down, but now it's up. Validate the Websocket
currentNetworkName = newNetworkName;
cameraThumbnailCache.clearInternalURLPreferences();
webSocketClient.reopenWebsocketIfPossible();
cameraAccessManager.onNetworkUp();
if (ActivityBehaviorHelper.needsSecurityCountdown(registeredActivity)) {
armingHelper.startTimerIfReady();
}
for (NetworkStatusChangeListener listener : networkChangeListeners) {
listener.onNetworkUp();
}
joustLogger.onNetworkUp();
}
} else {
Timber.w("Network Down");
currentNetworkName = null;
cameraAccessManager.onNetworkDown();
joustLogger.onNetworkDown();
shutdownManager.onNetworkDown();
for (NetworkStatusChangeListener listener : networkChangeListeners) {
listener.onNetworkDown();
}
}
}
}
}
BaseActivity.java
#Override
protected void onResume() {
super.onResume();
networkMonitor.startListeningForNetworkChanges(this);
}
#Override
protected void onPause() {
networkMonitor.stopListeningForNetworkChanges(this);
super.onPause();
}
It looks like you probably don't need to be holding a reference to the Activity in that NetworkMonitor class. That's probably the source of your memory leak - the Activity reference is likely being held after the Activity is destroyed. Looks like you could just pass the context in as a parameter to the methods that need it.
Also, For a few of the spots where Activity context is being used here, like context.getSystemService(Context.CONNECTIVITY_SERVICE), you could use Application context instead and possibly avoid needing an Activity reference altogether.

Pass data from activity to fragment on receiving a broadcast

There are lots of discussions about data exchange between activities and fragments, but I am still struggeling with a design flaw.
I have an activity that needs to receive some data from an service. When this data has changed, the service sends a local broadcast and the activity needs to get the latest dataset.
public class ActivityChannelConfig extends Activity implements FragmentChannelList.FragmentChanneListInferface, FragmentCustomChannelList.FragmentCustomChannelListInterface {
#Override
protected void onCreate(Bundle savedInstanceState) {
// Bind to Bluetooth Service
bindService(new Intent(this, ServiceBluetoothConnection.class),
mServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
ActivityChannelConfig activity = mActivity.get();
if(activity == null)
return;
ServiceBluetoothConnection.BTConnServiceBinder binder = (ServiceBluetoothConnection.BTConnServiceBinder) service;
mService = binder.getService();
mIsBound = true;
// Put current list into channel list fragment
FragmentCustomChannelList customChannelList = (FragmentCustomChannelList)activity.getFragmentManager().findFragmentByTag(activity.TAG_FRAGMENT_CUSTOMIZE);
if(customChannelList != null) {
customChannelList.setChannelList(activity.mServiceConnection.getService().getmDBMeasurement().getSortedMeasChannels(DBMeasurement.MEAS_CHANNEL_FLAG.MEAS_CHANNEL_ENABLED));
}
FragmentChannelList channelList = (FragmentChannelList)activity.getFragmentManager().findFragmentByTag(activity.TAG_FRAGMENT_LIST);
if(channelList != null) {
channelList.setChannelList(activity.mServiceConnection.getService().getmDBMeasurement().getSortedMeasChannels(DBMeasurement.MEAS_CHANNEL_FLAG.MEAS_CHANNEL_ENABLED));
}
}
}
#Override
public void onReceive(Context context, Intent intent) {
final ActivityChannelConfig activity = mActivity.get();
// Bail out if the ActivityMarwisApp is gone.
if (activity == null)
return;
// Channel list has changed
if(intent.getAction().equals(DBMeasurement.INTENT_DB_CHANNEL_LIST_CHANGED)) {
// Put current list into channel list fragment
FragmentCustomChannelList customChannelList = (FragmentCustomChannelList)activity.getFragmentManager().findFragmentByTag(activity.TAG_FRAGMENT_CUSTOMIZE);
if(customChannelList != null) {
customChannelList.setChannelList(activity.mServiceConnection.getService().getmDBMeasurement().getSortedMeasChannels(DBMeasurement.MEAS_CHANNEL_FLAG.MEAS_CHANNEL_ENABLED));
}
FragmentChannelList channelList = (FragmentChannelList)activity.getFragmentManager().findFragmentByTag(activity.TAG_FRAGMENT_LIST);
if(channelList != null) {
channelList.setChannelList(activity.mServiceConnection.getService().getmDBMeasurement().getSortedMeasChannels(DBMeasurement.MEAS_CHANNEL_FLAG.MEAS_CHANNEL_ENABLED));
}
}
}
}
Now my problem is that I don't know, how to pass this data to the fragments inside my activity initial and dynamically. There always is the problem, that the fragment might ne be created when the service is connected or the broadcast is received.
Do I need to implement a two way interaction between the fragment and the activity?
activity.updateList -> fragment
fragment.getCurrentList -> activity
Or should I register a new braodcast receiver in each fragment, which might lead to lots of redundant code?
I know that I could also pass the data using the onCreate Bundle. But I am not a fan of making all that stuff parcable, which I also belive is very slow and inefficient.
You can use local broadcast manager to communicate between multiple fragments and intents
//Send Broadcast
private void sendLocationBroadcast(Intent intent, Context context, String Message){
intent.putExtra("Message", Message);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
//In this snippet you point out your fragment or activity by given sample
if(Origin == Enum_Fragments.Task.getInteger())
{
intent = new Intent("Task");
sendLocationBroadcast(intent, context, "Task");
}
// What I have done here is made a enum for fragments and trigger service with related Identity (which was assigned by me to each fragment) after condition identifies the Argument (Origin) it will send data to desired framgment
Here is enum class for ref
public enum Enum_Fragments{
Task(1), Frag Notes(2), Default(0);
Enum_Fragments(int s) {
INTEGER_FOR_MODULE = s;
}
private int INTEGER_FOR_MODULE;
public int getInteger() {
return INTEGER_FOR_MODULE;
}
public void setInteger(int iNTEGER_FOR_MODULE) {
INTEGER_FOR_MODULE = iNTEGER_FOR_MODULE;
}
public static Enum_Fragments value(String i) {
try {
return valueOf(i);
} catch (Exception ex) {
return Default;
}
}
this will send your broadcast for provided intent with some particular data after job done you may receive it at the activity as follows
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
String Message = intent.getStringExtra("Message");
//Todo
Log.d("Local BroadCast", "Recieved " + Message);
}
};

Android: calling a method of an outer class from her inner static class

I have an activity class (outer class), a static broadcastreceiver class (inner static class) and a service class. The service and the activity communicate with messages and handlers. When an action that the service is monitoring is triggered, the broadcastreceiver is called. After it's done I want to call a method inside the service to remove the element processed from the "items_to_be_processed_queue". To do that I thought to use the method that I have in my MainActivity that sends a message to the service triggering the remove method (I have this method in MainActivity because it's possible to remove manually an item from the "items_to_be_processed_queue" by pressing a button). The thing is I keep getting two kind of errors depending on what I do (I'll show you a bit of code first):
public class MainActivity extends Activity {
Messenger messenger;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
messenger = new Messenger(binder);
}
public void onServiceDisconnected(ComponentName className) {
messenger = null;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
....
}
//Button click implementation
public void removeItem(View view) {
Message msg = Message.obtain(null, MyService.REMOVE_ITEM);
msg.replyTo = new Messenger(new ResponseHandler());
Bundle b = new Bundle();
b.putInt("data", Integer.valueOf(etNumber.getText().toString()));
msg.setData(b);
try {
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void removeItem(int i) {
Message msg = Message.obtain(null, MyService.REMOVE_ITEM);
msg.replyTo = new Messenger(new ResponseHandler());
Bundle b = new Bundle();
b.putInt("data", i);
msg.setData(b);
try {
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
protected static class ResponseHandler extends Handler {
Boolean result;
#Override
public void handleMessage(Message msg) {
int respCode = msg.what;
switch(respCode) {
case MyService.ADD_ITEM: {
result = msg.getData().getBoolean("respData");
}
case MyService.REMOVE_ITEM: {
result = msg.getData().getBoolean("respData");
}
}
}
}
public static class MyBroadcastReceiver extends BroadcastReceiver {
.........
#Override
public void onReceive(Context context, Intent intent) {
........
Case 0: new MainActivity().removeItem(id); //Where id is the position of the item
Case 1: MainActivity.this.removeItem(id);
}
......
So in the case 0 I get no compiling errors but at run time I get a NullPointerException at messenger.send(msg) inside removeItem(int i) method. In case 1 I get the compiling error "No enclosing instance of the type MainActivity is accessible in scope".
What am I doing wrong and what could I do? I even tried to put the removeItem methond inside the broadcastreceiver but I still got run time errores. Thanks in advance for any answer.
Case 0:
You should never create an object of Activity class It will give you a null context. Look at #Raghav Sood's answer here Creating an object of Activity class
Case 1:
You can not call a non-static method from an inner static class. If you want to call removeItem in MyBroadcastReceiver make it static. Since it seems you are not using any instance variables that should not be a problem.
You cannot create activity like that because Android is handling Activity lifeCycle so it won't be anygood..
I can suggest diffrent approach.. Maybe i am missing something because i didn't fully understood your code but this is more architecture problem and less code
Lets say that you hold your DataList In a static way... In that case you can access from Activity and From service as well.. You can't access an activity in the way you want.
class Utils {
private static List<Integer> myList;
static {
myList<Integer> = new Vector<Integer>();//Create new instance of vectore which is thread safe
}
public void getMyList()..;
public List<Integer> setMyList..;
}
In this way you will have direct access to your data structure an you won't have to deal to much with sync between those 2.
EDIT: You can add methods to remove and add or something.. This can be used by
Utils.getMeyList();//Or all other method you need
Hope that make sense..

how to use a service in multiple activities?

I'm having a problem writing a service, that should work with multiple activities.
I wrote a simple service and a mediator class the makes the bind and can return a service object. this is the simple service class:
public class ServerConnectionService extends Service{
private static final String TAG = "ServerConnectionService";
private final Binder binder=new LocalBinder();
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy(){
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public class LocalBinder extends Binder {
ServerConnectionService getService() {
return ServerConnectionService.this;
}
}
}
this is the mediator class:
public class ServiceConnectionBinder{
private ServerConnectionService m_SrvConnection=null;
private ServiceConnection m_OnService;
private boolean m_IsBound;
private Activity m_Client;
public ServiceConnectionBinder(Activity i_Activity)
{
m_IsBound = false;
this.m_Client = i_Activity;
this.m_OnService=new ServiceConnection() {
public void onServiceConnected(ComponentName className,IBinder rawBinder) {
m_SrvConnection=((ServerConnectionService.LocalBinder)rawBinder).getService();
}
public void onServiceDisconnected(ComponentName className) {
m_SrvConnection=null;
}
};
doBindService();
Log.d("ServiceConnectionBinder", "finished Ctor");
}
private void doBindService() {
if(!m_IsBound)
{
m_Client.bindService(new Intent(m_Client, ServerConnectionService.class), m_OnService, Context.BIND_AUTO_CREATE);
m_IsBound = true;
}
if(m_SrvConnection == null)
{
Log.d("ServiceConnectionBinder",".doBindService cannot bind " + ServerConnectionService.class.toString() + " to " + this.toString());
}
}
public void doUnbindService() {
if (m_IsBound) {
// Detach our existing connection.
m_Client.unbindService(m_OnService);
m_IsBound = false;
}
}
public ServerConnectionService getServerConnectionService()
{
if(m_IsBound)
{
Log.d("ServiceConnectionBinder", "getServerConnectionService m_IsBound = " + m_IsBound);
}
return m_SrvConnection;
}
}
The client Activity has the following data members:
private ServiceConnectionBinder m_SrvcConnectionBinder=null;
private ServerConnectionService m_SrvConnection=null;
And in onCreate() the following code:
m_SrvcConnectionBinder = new ServiceConnectionBinder(this);
m_SrvConnection = m_SrvcConnectionBinder.getServerConnectionService();
problem is that after the onCreate(), the m_SrvConnection is always null.
If you have any other ways to implement this you are more than welcome to share..
problem is that after the onCreate(), the m_SrvConnection is always null.
Of course. The binding request will not even begin until the main application thread gets control again (i.e., you return control to the OS).
You cannot use m_SrvConnection until onServiceConnected() is called.
Resurrecting the old post as I had the similar question, but there is no clear answer here.
One of the ways to address this is like this:
protected void onCreate(Bundle savedInstanceState){
//... some stuff #1...
new AsyncTask<Void, Void, Integer>(){
protected void onPreExecute() { }
protected Integer doInBackground(Void... params) {
while(m_SrvConnection==null);
return new Integer(1);
}
protected void onPostExecute(Integer result) {
// service is up, m_SrvConnection is set
// what you wanted to do with the service in onCreate() goes here
}
}.execute();
//...some stuff #2...
}
Note that "some stuff #1" will run right when onCreate() is called, "some stuff #2" will be executed almost right after that, but what you put in onPostExecute() will be run much later.
The reason for doing it this way and not just putting the code into onServiceConnected() is that the ServiceConnectionBinder can now be put outside of the Activity (in some singleton, or Application for example) and be used by multiple activities without the need for each of them to bind to the service.
Note, it may not be obvious, but things in onPostExecute() may (will) actually be run after all other standard callbacks (like onResume() etc.).

How to Create/Run AsyncTask in Service without runOnUiThread()

I have a Service that creates AsyncTasks for downloading files. In activities, we create Runnables or Threads that we pass to Activity.runOnUiThread(). I can't access that method from a service, so how do I use AsyncTask correctly, (do heavy work without blocking the UI Thread)?
If your service is only called from your application, and you can make it a singleton, then try this:
public class FileDownloaderService extends Service implements FileDownloader {
private static FileDownloaderService instance;
public FileDownloaderService () {
if (instance != null) {
throw new IllegalStateException("This service is supposed to be a singleton");
}
}
public static FileDownloaderService getInstance() {
// TODO: Make sure instance is not null!
return instance;
}
#Override
public void onCreate() {
instance = this;
}
#Override
public IBinder onBind(#SuppressWarnings("unused") Intent intent) {
return null;
}
#Override
public void downloadFile(URL from, File to, ProgressListener progressListener) {
new Thread(new Runnable() {
#Override
public void run() {
// Perform the file download
}
}).start();
}
}
Now you can directly call methods on your service. So just call downloadFile() to put the service to work.
About your real question of how to update the UI. Notice that this method receives a ProgressListener instance. It could look like this:
public interface ProgressListener {
void startDownloading();
void downloadProgress(int progress);
void endOfDownload();
void downloadFailed();
}
Now you just update the UI from the activity (not from the service, which remains unaware of how the UI looks like).

Categories

Resources