I am trying to pass a large string-array of maybe 2 or 3 MB to another activity. The chunk is not passed and the only thing I can see in the logcat about what happend is ...
!!! FAILED BINDER TRANSACTION !!!
I tried to create my own class which implements Serializable, put a mutator there in which I put my String, then pass the Object reference to intent.putExtra(key, Serializable obj)
the code:
MyClass mc = new MyClass();
Intent intent = new Intent(MainActivity.this, CalculationsActivity.class);
intent.putExtra("mc", mc);
Is there an easy solution to this issue - that is to pass a large string-array to another activity?
class MyClass implements Serializable {
private String[] str;
public void setString(String[] str) {
this.str = str;
}
public String[] getString() {
return this.str;
}
}
I thought that passing just a reference wouldnt cause this. Reference is not more than a memoryaddress
As others have stated, using Local files (in your sandboxed directory) or database entries will probably be the way to go. However, if you want to do remote (e.g. ftp) hosting of the file and still load them when the application loads you should use a Service. (see the Docs).
I have two previous answers explaining services in more depth that you should look at.
How to use threads and services. Android
and
Android Service with multiple Threads
Essentially though there are two types, a bound thread (which lives with either an activity or the application) and intent services (which can always be active, or only active when the app is open). What you would want is probably the former which would look something like what is in the first link.
here is a snippet
public class BoundService extends Service {
private final BackgroundBinder _binder = new BackgroundBinder();
//Binding to the Application context means that it will be destroyed (unbound) with the app
public IBinder onBind(Intent intent) {
return _binder;
}
//TODO: create your methods that you need here (or link actTwo)
// Making sure to call it on a separate thread with AsyncTask or Thread
public class BackgroundBinder extends Binder {
public BoundService getService() {
return BoundService.this;
}
}
}
Related
So, from what I understand, I should use intents to update the UI of an activity from a service.
But, I am a bit concerned about the efficiency of what I want to do.
Theoretically, it shouldn't really pose a serious problem, but I don't want to have a situation in which I wake up my phone, and have 20-30 onTick-s running at once, each time generating a new intent.
What I want (if possible) is for each onTick's intent to overwrite the previous one, so that the receiver only has process one at a time.
I know pending intents have FLAG_UPDATE_CURRENT, but doing things the right way means (if I understand correctly) using a local broadcast, which doesn't work with pending intents.
This turned out to be surprisingly simple.
A pskink suggested in his comment, all you need is a binder:
Create a class that extends the Binder class. Keep in mind that the Binder class implements IBinder, which is why you can pass an instance of your new class as an instance of IBinder (which is something you will be doing later on)
In this class, implement various functions you want your main activity to be able to use. If you want your service to be able to run things from the activity as well, you can pass an instance of an inner class of the activity, which has the relevant functions in it, or even pass a pointer to the activity (this).
Keep in mind: Do these things at your own risk. If, for example, your activity is destroyed - for example, due a change of orientation, the pointers might become worthless and errors will be thrown
I recommend implementing an invalidate inside your new class to help you handle these situations, but perhaps there are better practices I am not personally familiar with.
Now, you have a two-way communication channel with your activity.
Below is some sample code. Note that if you don't want your service to be destroyed when the activity is, you need (as far as I understand it) to also start it using startService (in addition to using bindService)
public class MyService extends Service {
private IBinder yourBinder; //A class that will connect your service and your activity
#Override
public void onCreate() {
super.onCreate();
yourBinder = new YourBinderClass();
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return yourBinder;
}
Your binder class:
public class YourBinderClass extends Binder {
private Service thisService;
public YourBinderClass(Service myService)
{
thisService = myService;
}
public Service getService()
{
return thisService;
}
And finally, inside your main activity:
private YourBinderClass yourBinderClass;
private Service yourService;
private ServiceConnection sc = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
yourBinderClass = (YourBinderClass) service;
yourService = yourBinderClass.getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
And to launch the service:
getBaseContext().bindService(new Intent(getBaseContext(),MyService.class), sc, 0)
Note that the service won't be ready immediately, so if your activity is dependent on it, you might want to signal that you are ready to do stuff in onServiceConnected
I have class called ClientManager where i load the dictionary from the server. Once the dictionary is loaded a Service is started (which runs in its own process) that will use the data from this dictionary. However, when i access the dictionary through the class singleton it is null. Dictionary object resides in class DataManager. Please see the code below:
ClientManager.java
DataManager mDataManager = DataManager.getInstance();
public void apiCompleted(ApiResult apiResult){
....
DataDictionary dataDict = (DataDictionary) apiResult.valueObject;
//dictionary loads OK from server since i can see the contents by iterating through it
mDataManager.addDictionary(dataDict);
if (!serviceRunning(MyService.class)){
Intent intent = new Intent(mContext, MyService.class);
mContext.startService(intent);
}
MyService.java
public class MyService extends Service {
...
DataManager mDataManager;
#Override
public void onCreate(){
mDataManager = DataManager.getInstance();
if(!mDataManager.containsDataDictionary()){
//toast dictionary is null
}
}
DataManager.java
public class DataManager {
private static DataManager instance = null;
private DataDictionary mDataDictionary = null;
public static DataManager getInstance(){
if (instance == null){
instance = new DataManager();
}
return instance;
}
public boolean containsDataDictionary() {
if ( m_dataDictionary == null ){
return false;
}
return true;
}
public DataDictionary getDataDictionary() {
return mDataDictionary;
}
public void addDataDictionary(DataDictionary p_dataDictionary) {
mDataDictionary = p_dataDictionary;
}
}
You said it yourself: "which runs in its own process"
Each process has its own VM. You cannot access variables etc from one process to another.
The obvious question to follow up this would be, do you absolutely need to run the service in its own process?
Data sharing between processes are done through Intents or ContentProviders.
If the data is only shared within your application package you may also use SQLite or SharedPreferences but both these uses non-volatile memory and are significantly slower than data sharing through IPC.
The Singleton Pattern is allowed to share data within the application not between applications. Any instances of an app its lifespan is restricted within scope of the app life. It seems that you are trying to extend the scope of singleton object outside the app, which is not possible at all..
Note : Each process runs in own VM, so target processes doesn't have singleton instance of source processes.
There are two ways of communication for an app...
1) Active Communication (IPC) : When both source and target app is running (i.e.. processes) you need source/sender should act as a server and target/receiver should act as a client and both should communicate with Remote Object which both side has same remote object signature.Example : AIDL implementation....
2) Passive Communication (Accessing Source's Resource) : When Source is not alive and target is trying to access the data of source which is stored in any kind of storage can be accessed via Intents/Content Provider.
If you want to share data between process then you go with AIDL implementation only...
I need to find a solution that holds and accesses large chunks of complex global data and methods. It has to be accessible from within activities and normal instance variables of various data classes.
This is how I have done it. I would just like to know if there is anything wrong with it or if there is a better/cleaner way.
First I extend Application like recommended many times...
public class MainDataManager extends Application{
public ... large chunks of data in arrays, lists, sets,....
//static variable for singleton access from within instance variables of other classes
public static MainDataManager mainDataManager;
//create and init the global data, and store it in the static variable of the class
#Override
public void onCreate() {
super.onCreate();
//in case it should get called more than once for any reason
if (mainDataManager == null) {
init();
mainDataManager = this;
}
}
Now accessing it from within activities like everywhere recommended...
MainDataManager mainDataManager = (MainDataManager)getApplicationContext();
And since I need to access it from normal instances of data classes ...
public class MyDataClass {
public MainDataManager mainDataManager;
public String name;
public MyDataClass(String namex) {
this.name = namex;
//this is why I defined the static variable within MainDataManager, so
//one has access to it from within the instance of MyDataClass
this.mainDataManager = MainDataManager.mainDataManager;
}
public void examplesForAccessing() {
//some examples on how to access the global data structure and associated methods
mainDataManager.someMethodAccess();
xyz = mainDataManager.someDataAccess;
mainDataManager.someIndirectMethodAccess.clear();
mainDataManager.someOtherData = false;
}
}
Since I have not done this so far, I would like to know if there is anything wrong with this. Memory, efficiency, ...
Thanks very much!
May I add a little sidenote?
I could also have just used a class MainDataClass and access by MainDataClass.var or MainDataClass.method(). Is there any REAL disadvantage?
Is the data in both cases held in heap/stack?
You haven't given much detail about your "large chunks of data" but keep in mind that the onCreate method is the first things that runs when your application is starting and it runs on the main/UI thread. This means that if you do long tasks in your init() method your UX will be poor, not to mention that you are risking an ANR exception.
The solution for that is simple:
Keep your onCreate short
Create a BG thread and use it to run all initialization code
Show a "Splash"/"Welcome" screen with the a proper progressbar while the BG thread is running.
I need to get a reference to the main Activity from a Service.
This is my design:
MainActivity.java
public class MainActivity extends Activity{
private Intent myIntent;
onCreate(){
myIntent=new Intent(MainActivity.this, MyService.class);
btnStart.setOnClickListener(new OnClickListener(){
public void onClick(View V){
startService(myIntent);
});
}}
MyService.java
class MyService extends Service{
public IBinder onBind(Intent intent) {
return null;
}
onCreate(){
//Here I need to have a MainActivity reference
//to pass it to another object
}
}
How can I do this?
[EDIT]
Thanks to all for the answers!
This app is a web server, that at this moment works only with threads, and I want to use a service instead, to make it work also in the background.
The problem is that I have a class that is responsible for getting the page from assets, and to do this operation I need to use this method:
InputStream iS =myActivity.getAssets().open("www/"+filename);
At this moment my project has only one Activity and no services, so I can pass the main activity's reference directly from itself:
WebServer ws= new DroidWebServer(8080,this);
So, in order to make this app work with a service, what should I change in my design?
You didn't explain why you need this. But this is definitely bad design. Storing references to Activity is the first thing you shouldn't do with activities. Well, you can, but you must track Activity lifecycle and release the reference after its onDestroy() is called. If you are not doing this, you'll get a memory leak (when configuration changes, for example). And, well, after onDestroy() is called, Activity is considered dead and is most likely useless anyway.
So just don't store the reference in Service. Describe what you need to achieve instead. I'm sure there are better alternatives out there.
UPDATE
Ok, so you do not actually need reference to Activity. Instead you need reference to Context (which in your case should be ApplicationContext to not keep reference to Activity or any other component for that matter).
Assuming you have a separate class that handles WebService request:
class WebService
{
private final Context mContext;
public WebService(Context ctx)
{
//The only context that is safe to keep without tracking its lifetime
//is application context. Activity context and Service context can expire
//and we do not want to keep reference to them and prevent
//GC from recycling the memory.
mContext = ctx.getApplicationContext();
}
public void someFunc(String filename) throws IOException
{
InputStream iS = mContext.getAssets().open("www/"+filename);
}
}
Now you can create & use WebService instance from Service (which is recommended for such background tasks) or even from Activity (which is much trickier to get right when web service calls or long background tasks are involved).
An example with Service:
class MyService extends Service
{
WebService mWs;
#Override
public void onCreate()
{
super.onCreate();
mWs = new WebService(this);
//you now can call mWs.someFunc() in separate thread to load data from assets.
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
To communicate between your service and activity you should use AIDL.
More info on this link:
EDIT: (Thanks Renan Malke Stigliani)
http://developer.android.com/guide/components/aidl.html
The AIDL is overkill unless the activity and service are in seperate apks.
Just use a binder to a local service.
(full example here: http://developer.android.com/reference/android/app/Service.html)
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
Agree with inazaruk's comments. But, In terms of communicating between an Activity and a Service, you have a few choices - AIDL (as mentioned above), Messenger, BroadcastReicever, etc. The Messenger method is similar to AIDL but doesn't require you to define the interfaces. You can start here:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.html
According to what i have learnt from passing data using Intents is that when you pass Object O from Activity A to Activity B via intents, activity B receives a COPY of object O. The way things work is that The object O gets serialized (converted to a sequence of bytes) and that sequence of bytes is then passed to Activity B. Then activity B recreates a copy of object O at the moment it was serialized.
I would like to know if it would be efficient if one extends the Intent class to create a custom Intent and have references to the objects that are required by the other activities and pass the data to the other activities. For example:
public class CustomIntent extends Intent {
private Object o;
public CustomIntent() {
super();
// TODO Auto-generated constructor stub
}
public Object getObject () {
return o;
}
public void setObject(Object object) {
this.o = object;
}
}
In the receiving activity i get the intent and cast the intent to the CustomIntent type and retrieve the object required by the activity. Would this improve the efficiency by reducing the need for Serialization? Kindly throw some light on this. Thanks in advance.
No. Intents are dispatched by the Android system and are always serialized as they can be sent to any activity, service, etc in the system.
For your problem you could probably workaround this issue by creating an Application class and storing your data in it:
class CustomApplication extends Application {
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
You activate it by updating AndroindManifest.xml setting the android:name property on the application tag you your class name.
To use in your activities:
CustomApplication app = (CustomApplication) getApplicationContext();
app.setData(yourDataObject);
I think it would be better if you let the android handle everything for you. Do not customize it, if it is not very essential.
If you want to have the reference of the object in another activity then there are other ways too.
You can make your object static and directly access it from other activity.
You can make a new object of same type and replace it after coming again back to the first activity(in onActivitResult() method.).
or there may be many more ways to do it.
Thanks.