Passing Object over activities using putSerializable - android

I'm trying to pass my dbhelper instance from one activity to another using this code
private void onCategoriesClick() {
private DbAdapter db;
db = new DbAdapter(this);
Intent i = new Intent(this, CategoriesActivity.class);
Bundle b = i.getExtras();
b.putSerializable("db", db); //geting NullPointerException here
startActivityForResult(i, 0);
DbAdapter class implements Serializable
I'm geting confused, could anybody point my mistake?

Create the dbhelper in the activity that will use it. If you really must pass it between activities, it would be much simpler to store it in a static variable (in your application class for easy access)

getExtras() returns null, because you haven't put any extras into the Intent before you call it. This means that b == null, hence the NPE. Just put the serializable in the intent directly.
i.putExtra("db", (Serializable) db);
In general, though, avoid passing serializables over intents if you can help it.

Related

Pass 3D-array to Activity in Android

I am trying to pass a 3D-doublearray to another activity. I don know why I get NullpointerException? What is missing?
MainActivity
Intent intent = new Intent(this, DataActivity.class);
Bundle mBundle = new Bundle();
mBundle.putSerializable("list", output_data);
intent.putExtras(mBundle);
startActivity(intent);
DataActivity (reveiver)
Intent intent = new Intent();
double[][][] params = (double[][][]) intent.getExtras().getSerializable("list");
And I know for certain that the 3d-array already is allocated in the MainActivity. I have tested that!
Would be glad if someone has any solution to this and could answer why I get a NullPointerException.
(Edit: NullpointerException at the line double[][][] params = ...)
I'm pretty sure double[][][] doesn't implement Serializable. What you can do is make your own class like so:
public class MyArrayWrapper implements Serializable {
private double[][][] arr;
public MyArrayWrapper(double[][][] value) {
arr = value;
}
public double[][][] getArray() {
return arr;
}
}
You then instantiate that wrapper class and put it as a serializable instead. Then when getting the serializable you do it like this:
MyArrayWrapper wrap = (MyArrayWrapper) intent.getExtras().getSerializable("list");
double[][]][] myArray = wrap.getArray();
Hope this helps!
Maybe you need a class that implements serializable?
Passing data through intent using Serializable
In this example, they are using custom class and List<> to serialize in a bundle. You can create a class with the 3D array inside?

Why can't I make the ArrayList static?

I have the following code.
public class Start extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
ArrayList<String> aPlayerList = getIntent().getStringArrayListExtra("playerList");
static ArrayList<Integer> aScores = getIntent().getIntegerArrayListExtra("scores");
...
I get an error when trying to make the ArrayList<Integer> aScores static: Non-static method getIntent() cannot be referenced from a static context , and I don't know how to fix this.
If it helps, this is how the intent was passed:
Bundle bund = new Bundle();
bund.putStringArrayList("playerList", playerList);
bund.putIntegerArrayList("scores", scores);
Intent intent = new Intent(Players.this, Start.class);
intent.putExtras(bund);
startActivity(intent);
Any help would be appreciated, and if you could add the code that would fix it that would be great. Thanks,
Because your syntax is wrong.
You can't make a variable inside a method static what would be the use of this? Static means that the field is related to the class so you can access it without any reference (ClassName.staticField).
Variables inside methods are related to the method, so you can't access them outside it so how static could be used here?
Are you sure you don't get confused with final? Which is a valid here.
To resolve your problem, you just need to make static ArrayList<Integer> aScores as field of the class so you can access it anywhere in your code. Then edit your onCreate method to this
aScores = getIntent().getIntegerArrayListExtra("scores");
so it will save the array list inside aScores field.
That is because getIntent() is a non static method and should not be referenced to a static field.
solution:
Remove the static of your arrayList.
Static methods get created only once, so you can't reference getIntent() because Java doesn't know which instance of the method you are referencing.
Check here for more information on how static methods work.

How to reference the current or main activity from another class

I often find myself needing to access methods that require referencing some activity. For example, to use getWindowManager, I need to access some Activity. But often my code for using these methods is in some other class that has no reference to an activity. Up until now, I've either stored a reference to the main activity or passed the context of some activity to the class. Is there some better way to do this?
If you already have a valid context, just use this:
Activity activity = (Activity) context;
Passing context is better way for refrence Activity.
You can pass Context to another class.
IN Activity ::
AnotherClass Obj = new AnotherClass(this);
IN Another Class
class AnotherClass{
public AnotherClass(Context Context){
}
}
You can implement the necessary methods in your activity and implement a Handler. Then, simply pass a handler instance to your classes, where you can obtain a message for handler and send it to target.
You can make you application instance a singleton, and use it when you need a Context
An example is in this question:
Android Application as Singleton
This way, when you need a Context, you can get it with
Context context = MyApplication.getInstance()
This might not be the cleanest solution, but it has worked well for me so far
I found a way to get the Activity to a non-activity class that I have not seen discussed in forums. This was after numerous failed attempts at using getApplicationContext() and of passing the context in as a parameter to constructors, none of which gave Activity. I saw that my adapters were casting the incoming context to Activity so I made the same cast to my non-activity class constructors:
public class HandleDropdown extends Application{
...
public Activity activity;
...
public HandleDropdown() {
super();
}
public HandleDropdown(Activity context) {
this.activity = context;
this.context = context;
}
public void DropList(View v,Activity context) {
this.activity = context;
this.context = context;
...
}
After doing this cast conversion of Context to Activity I could use this.activity wherever I needed an Activity context.
I'm new to android so my suggestion may look guffy but what if you'll just create a reference to your activity as private property and assign that in OnCreate method? You can even create your CustomActivity with OnCreate like that and derive all your activities from your CustomActivity, not the generic Activity provided by android.
class blah extends Activity{
private Activity activityReference;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityReference = this;
}
}
after that you could use that the way you want, i.e. in
Intent i = new Intent(activityReference, SomeOtherActivity.class)
etc
There are many ways for Activities communication.
you can use:
the startActivityForResult method
a system of broadcast message and receiver (you can broadcast an event from the actual activity, and register a receiver in the target activity. Remember that the target activity must be previously initialized and non finished)
as you say, store a reference of the target activity wherever you need.
We built a framework for this. We have a BaseActivity class that inherits from Activity and it overrides all the lifecycle methods and has some static (class) variables that keep track of the activity stack. If anything wants to know what the current activity is, it just calls a static method in BaseActivity that returns the activity on top of our privately-managed stack.
It is kinda hacky, but it works. I'm not sure I would recommend it though.
Handle the Intent in the class you want to do these methods, and send your information to it in a Bundle like so:
Intent i = new Intent("android.intent.action.MAIN");
i.setComponent(new ComponentName("com.my.pkg","com.my.pkg.myActivity"));
Bundle data = new Bundle();
i.putExtras(data);
startActivityForResult(i);
Then use an OnActivityResultListener to grab the new data.
I solved this by making a singleton class has an instance of the class below as a member.
public class InterActivityReferrer <T> {
HashMap<Integer, T> map;
ArrayList<Integer> reserve;
public InterActivityReferrer() {
map = new HashMap<>();
reserve = new ArrayList<>();
}
public synchronized int attach(T obj) {
int id;
if (reserve.isEmpty()) {
id = reserve.size();
}
else {
id = reserve.remove(reserve.size() - 1);
}
map.put(id, obj);
return id;
}
public synchronized T get(int id) {
return map.get(id);
}
public synchronized T detach(int id) {
T obj = map.remove(id);
if (obj != null) reserve.add(id);
return obj;
}
}
This class can get a T object and return a unique integer assigned to the object by attach(). Assigned integers will not collide with each other unless HashMap fails. Each assigned integer will be freed when its corresponding object is detached by detach(). Freed integers will be reused when a new object is attached.
And from a singleton class:
public class SomeSingleton {
...
private InterActivityReferrer<Activity> referrer = new InterActivityReferrer<>();
...
public InterActivityReferrer<Activity> getReferrer() {return referrer;}
}
And from an activity that needs to be referred:
...
int activityID = SomeSingleton.getInstance().getReferrer().attach(this);
...
Now with this, a unique integer corresponding to this activity instance is returned. And an integer can be delivered into another starting activity by using Intent and putExtra().
...
Intent i = new Intent(this, AnotherActivity.class);
i.putExtra("thisActivityID", activityID);
startActivityForResult(i, SOME_INTEGER);
...
And from the another activity:
...
id refereeID = getIntent().getIntExtra("thisActivityID", -1);
Activity referredActivity = SomeSingleton.getInstance().getReferrer().get(refereeID);
...
And finally the activity can be referred. And InterActivityReferrer can be used for any other class.
I hope this helps.
public static Activity getLaunchActivity()
{
final Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
final Method methodApp = activityThreadClass.getMethod("currentApplication");
App = (Application) methodApp.invoke(null, (Object[]) null);
Intent launcherIntent = App.getPackageManager().getLaunchIntentForPackage(App.getPackageName());
launchActivityInfo = launcherIntent.resolveActivityInfo(App.getPackageManager(), 0);
Class<?> clazz;
try
{
clazz = Class.forName(launchActivityInfo.name);
if(clazz != null)
return Activity.class.cast(clazz.newInstance());
}
catch (Exception e)
{}
return null;
}
Just a guess since I haven't done this but it might work.
1) Get your applicationContext by making your Android Application class a Singleton.
2) Get your ActivityManager class from the context.
3) Get a list of RunningTaskInfos using getRunningTasks() on the ActivityManager.
4) Get the first RunningTaskInfo element from the list which should be the most recent task launched.
5) Call topActivity on that RunningTaskInfo which should return you the top activity on the activity stack for that task.
Now, this seems like a LOT more work than any of the other methods mentioned here, but you can probably encapsulate this in a static class and just call it whenever. It seems like it might be the only way to get the top activity on the stack without adding references to the activities.

Extending the Intent class ... received ClassCastException

I am attempting to create an enhanced Intent class (I call it DataIntent) by giving it the ability to hold a "payload" of type Object (versus using it's built-in facility for Uri's). DataIntent extends Android's Intent class.
My Activity creates the extended object without any problems and the invocation of the startActivityForResult() goes off without any problems also. But, in my responding Activity when I call the getIntent() method, and attempt to cast it to my DataIntent, I'll throw the ClassCastException.
I realize this is probably a very dumb question - a 1,000 appologies in advance - but does anyone know why I cannot cast it to the DataIntent since that's what was used to start the new Activity, and DataIntent is a child of Intent?
DataIntent dataIntent = (DataIntent)getIntent();
// invoked inside the responding Activity instance - throws a ClassCastException
You can't do that, sorry. You need to place your data inside of the Intent. The Intent object is moved across processes and thus the one you get back is not the same instance as the one you created.
I did the same thing CirrusFlyer. I also looked for the final keyword before I started to implement it. Google should mark Intent class as final.
You can and should extend an Intent, but you must understand the purpose of an Intent.
#1 an intent must be parcelable to support persisting the intent-data across an app restart (collapsed due to memory limitations, trimMemory etc).
#2 Understand that the Intent constructed by the caller, is not the Intent provided to the activity. This is due to item #1. So any object references would be lost and are a bad idea -- it needs to be parcelable remember.
#3 Intents should only contain data-context for the activity (or whatever). So you should not place pages of data into an intent, rather, it should contain ids or keys, or whatever contextual data is necessary to re-obtain the data for the activity (or whatever).
Now... Why should you extend an Intent? For good contracts!
Intents by themselves are terrible contracts, way too loose. Some people create static method helpers but there is a better way.
If an ABCActivity requires "A" "B" and "C" to perform properly. A generic intent cannot describe that, we'd rely on documentation that no one will read.
Instead we can create a ABCIntent whose constructor demands A,B & C. This creates a clear contract on what is required to load the activity. We can do that with a static method, but a an ABCIntent can also provide getters for A B C making it a clean packaged contract for describing requirements to load the activity and how to obtain the data.
There is one caveat, we need a private constructor to construct our ABCIntent from a generic intent to inherit the extras.
public ABCActivity extends Activity {
private ABCIntent intent;
public static ABCIntent extends Intent {
private ABCIntent(Intent intent) {
super(intent);
}
public ABCIntent(A a, B b, C c) {
putExtra("EXTRA_A", A.serialize(a));
putExtra("EXTRA_B", B.serialize(b));
putExtra("EXTRA_C", C.serialize(c));
}
public A getA() { return A.deserialize(getExtra("EXTRA_A")); }
public B getB() { return B.deserialize(getExtra("EXTRB_B")); }
public C getC() { return C.deserialize(getExtra("EXTRC_C")); }
}
#Override
protected ABCIntent getIntent() {
return intent == null ? (intent = new ABCIntent(super.getIntent())) : intent;
}
#Override
protected void onCreate( ... ) {
A a = getIntent().getA();
B b = getIntent().getB();
C c = getIntent().getC();
// TODO: re-obtain activity state based on A, B, C then render
}
}
Notice that we construct ABCIntent from intent.
The ABCIntent inherits the intent extras.
Now you have a nicely packaged class who's job it is to define the contract for the activity and to provide the contractual data to the activity.
If you're a new engineer on the project, there is no way for you to misunderstand how to use this. No docs to read.

How do I write an android JUnit test when my activity relies on extras passed through an Intent?

I am writing an android Junit test for a class that relies on extras passed to it through an Intent. I was able to get the class working properly, but I would still like to know how to write a unit test for such a class, as the test still fails.
public class AddClassEvent extends Activity{
private String eventType;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
final String cNo = extras.getString("CourseNum");
// create a model instance
final StudentDBModel model = new StudentDBModel(this);
setContentView(R.layout.add_class_event);
.....
.....
}
}
The test class looks like...
public class AddClassEventTest extends ActivityInstrumentationTestCase2<AddClassEvent>{
private StudentDBModel model = null;
private RenamingDelegatingContext context = null;
public AddClassEventTest() {
super("com.UI", AddClassEvent.class);
}
/**
* This method is called before each test.
*/
#Override
public void setUp() {
context = new RenamingDelegatingContext(getActivity(), "test_");
model = new StudentDBModel(context);
}
/*
* This function will test addNewClassEvent() from StudentDBModel
*/
public void testAddNewClassEvent(){
ContentValues courseValues = new ContentValues();
courseValues.put("CourseId", "60-415");
courseValues.put("CourseName", "Advanced Database Design");
courseValues.put("Section", "1");
courseValues.put("Location", "Erie");
courseValues.put("Credit", "3");
courseValues.put("ProfEmail", "rfortier#uwindsor.ca");
courseValues.put("Website", "cs.uwindsor.ca");
model.addNewCourses(courseValues);
int numEventsBefore = model.getNumClassEvents();
ContentValues values = new ContentValues();
values.put("EventName", "Assignment 1");
values.put("CourseId", "60-415");
values.put("EventType", "Assignment");
values.put("EventWeight", "8");
values.put("DueDate", "10/20/2010");
model.addNewClassEvent(values);
int numEventsAfter = model.getNumClassEvents();
assertEquals(numEventsBefore + 1, numEventsAfter);
}
}
The problem is, the extra that I am passing to the class AddClassEvent is a PK for my DB that is created in another class and passed to AddClassEvent through an Intent. Whenever I run the test I get a NULL Pointer Exception on the on the line:
final String cNo = extras.getString("CourseNum");
How do I create the info from the extra in the Junit Test? Is there a way to get this test to work? I have searched extensively and can't find an answer. Is there some way to falsely create the extras in the Junit test so that it thinks it is being created by the other class? If so, could someone please show me how?
OK so I have tried to take your advice and I have changed my setUp function to:
#Override
public void setUp() {
context = new RenamingDelegatingContext(getActivity(), "test_");
model = new StudentDBModel(context);
Intent addEvent = new Intent();
addEvent.setClassName("com.UI", "com.UI.AddClassEvent");
addEvent.putExtra("CourseNum", "60-415");
setActivityIntent(addEvent);
getActivity();
}
but I am still getting a NULL Pointer exception. Is my syntax wrong? Any suggestions?
The class you inherit, ActivityInstrumentationTestCase2, allows you to mock Intents. From the documentation:
You can inject custom Intents into your Activity (see setActivityIntent(Intent)).
The documentation for setActivityIntent() further clarifies:
Call this method before the first call
to getActivity() to inject a
customized Intent into the Activity
under test.
If you do not call this, the default
intent will be provided. If you call
this after your Activity has been
started, it will have no effect.
So you should be able to place a call to this method inside your setUp() before your call to getActivity(). You can pass in a mocked Intent into setActivityIntent like you mentioned -- just build a fake Intent with extras that you'd expect the Activity to see.
OK, I figured out my mistake! The code for setUp was just in the wrong order. It should look like:
#Override
public void setUp() {
Intent addEvent = new Intent();
addEvent.setClassName("com.UI", "com.UI.AddClassEvent");
addEvent.putExtra("CourseNum", "60-415");
setActivityIntent(addEvent);
context = new RenamingDelegatingContext(getActivity(), "test_");
model = new StudentDBModel(context);
}
I was calling getActivity() twice and the first call was ahead of the Intent. By using the correct order, the test runs fine. Thanks for the help McStretch.

Categories

Resources