I'm investigating how to pass and save across configuration changes large amounts of data.
Basically I have a ActivityA (holding FragmentA) and a ActivityB (holding FragmentB). The target is to pass a very large String from the FragmentA to the FragmentB.
With the following method I create the string:
public class StringHelper {
public static String generateRandomLongString() {
Random rand = new Random();
StringBuilder builder = new StringBuilder();
builder.append(rand.nextInt(1000));
builder.append("_");
for (int i = 0; i < 10000000; i++) {
builder.append((char) (rand.nextInt('z' - ' ') + ' '));
}
return builder.toString();
}
}
I try to follow these steps:
FragmentA starts ActivityB with intent extras
ActivityB pass the data to the FragmentB
FragmentB handles the instance state management
Testing different solutions I noticed the following situations (in my devices and emulators):
IMPORTANT: Only the relevant code is shown in each piece of code
1. Passing the data to the ActivityB:
Intent intent = new Intent(v.getContext(), ActivityB.class);
String fakedData = StringHelper.generateRandomLongString();
Log.e("test", "Launching ActivityB, data = " + fakedData);
intent.putExtra("test", fakedData);
startActivity(intent);
the ActivityB:
public class ActivityB extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Log.e("Received "," "+getIntent().getStringExtra("test"));
}
}
This causes an Failure from system, since the bundle exceeds the max size for IPC.
java.lang.RuntimeException: Failure from system
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1514)
at android.app.Activity.startActivityForResult(Activity.java:3917)
at android.app.Activity.startActivityForResult(Activity.java:3877)
at android.support.v4.app.FragmentActivity.startActivityFromFragment(FragmentActivity.java:813)
at android.support.v4.app.FragmentActivity$HostCallbacks.onStartActivityFromFragment(FragmentActivity.java:871)
at android.support.v4.app.Fragment.startActivity(Fragment.java:916)
This should not be a problem since there are other approach that can be used to pass the data to the activity, shared memory, singletons, static classes, etc (see THIS_LINK for more info)
2. Passing the data to the FragmentB:
Using the Fragment arguments I don't get crashes and the data arrives correctly
public class ActivityA extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
Fragment fragment;
//fragment = new MainActivityFragment();
String fakedData = StringHelper.generateRandomLongString();
Log.e("TEST", "Activity String=" + fakedData);
fragment = FragmentB.newInstance(fakedData);
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content,fragment, "fragmentTag").commit();
}
}
}
public class FragmentB extends Fragment {
public static FragmentB newInstance(String data) {
FragmentB result = new FragmentB();
Bundle args = new Bundle();
args.putString("data", data);
result.setArguments(args);
return result;
}
....
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.e("TEST", "DATA_FROM_ARGS=" + getArguments().getString("data"));
}
}
3. Saving the instance state in the FragmentB:
Amusing the FragmentB can modify the data and you want to keep the updated one. Also seems to work correctly with no crashes.
public class FragmentB extends Fragment {
private String myData;
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null) {
myData = getArguments().getString("data");
} else {
myData = savedInstanceState.getString("savedData");
}
Log.e("TEST", "DATA=" + myData);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("savedData", myData);
}
}
Questions
Why do I only get crashes in the step 1.
Is there some case where the IPC enters in scene in the steps 2 or 3 generating a crash due to the bundle limit?
Would it be correct to replace the step 1 with a shared memory communication and let the rest of the step as is?
Related
I have two activities. When an item in a RecyclerView is selected, it takes the user to the second activity and fills in the details with the related RecyclerView item.
In the second RecyclerView activity, there is a Spinner. Depending on the item selected in the spinner, different RecyclerViews become visible/invisible to the user on the second activity.
How do I make it work so that the information is sent from the first activity to command the spinner on what to do?This is how Second Activity looks
SecondActivity.java
public class SecondActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
private TextView tv_title, tv_description;
private ImageView PartiesThumbnailImg,PartiesCoverImg;
private RecyclerView RvPartyMembers;
private PartyMembersAdapter partyMembersAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_parties_detail);
Spinner spinner = findViewById(R.id.spnConstituencies);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.constituencies, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(this);
//ini views
iniViews();
//Setting up members list
setupRvPartyMembers();
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String text = parent.getItemAtPosition(position).toString();
Toast.makeText(parent.getContext(), text, Toast.LENGTH_SHORT).show();
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
void iniViews() {
RvPartyMembers = findViewById(R.id.rv_party_members);
String partiesTitle = getIntent().getExtras().getString("title");
String partiesDescription = getIntent().getExtras().getString("description");
int imageResourceId = getIntent().getExtras().getInt("imgURL");
int imagecover = getIntent().getExtras().getInt("imgCover");
PartiesThumbnailImg = findViewById(R.id.detail_members_img);
Glide.with(this).load(imageResourceId).into(PartiesThumbnailImg);
PartiesThumbnailImg.setImageResource(imageResourceId);
PartiesCoverImg = findViewById(R.id.detail_members_cover);
Glide.with(this).load(imagecover).into(PartiesCoverImg);
tv_title = findViewById(R.id.tvPartyTitle);
tv_title.setText(partiesTitle);
tv_description = findViewById(R.id.tvPartyDesc);
tv_description.setText(partiesDescription);
}
void setupRvPartyMembers(){
List<PartyMembers> mdata = new ArrayList<>();
mdata.add(new PartyMembers("name",R.drawable.members_brendangriffin_fg));
mdata.add(new PartyMembers("name",R.drawable.members_brendangriffin_fg));
mdata.add(new PartyMembers("name",R.drawable.members_brendangriffin_fg));
partyMembersAdapter = new PartyMembersAdapter(this,mdata);
RvPartyMembers.setAdapter(partyMembersAdapter);
RvPartyMembers.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false));
}
These are the important pieces of code of the FirstActivity.java
#Override
public void onPartiesItemClick(PartiesOireachtas partiesOireachtas, ImageView partiesImageView) {
Intent intent = new Intent(getContext(),PartiesDetailActivity.class);
intent.putExtra("title", partiesOireachtas.getTitle());
intent.putExtra("description",partiesOireachtas.getDescription());
intent.putExtra("imgURL", partiesOireachtas.getThumbnail());
intent.putExtra("imgCover",partiesOireachtas.getCoverPhoto());
and
Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//RecyclerView Setup
//int data
lstPartiesOireachtas = new ArrayList<>();
lstPartiesOireachtas.add(new PartiesOireachtas("Fianna Fáil", "example1", R.drawable.fianna_fail_logo,R.drawable.fianna_fail_cover));
lstPartiesOireachtas.add(new PartiesOireachtas("Sinn Féin", "example2", R.drawable.sinn_fein_logo,R.drawable.sinn_fein_cover));
lstPartiesOireachtas.add(new PartiesOireachtas("Fine Gael", "example4", R.drawable.fine_gael_logo,R.drawable.fine_gael_cover));
lstPartiesOireachtas.add(new PartiesOireachtas("Green Party", "example9", R.drawable.green_party_logo,R.drawable.green_party_cover));
lstPartiesOireachtas.add(new PartiesOireachtas("Social Democrats", "example3", R.drawable.soc_dems_logo,R.drawable.soc_dems_cover));
lstPartiesOireachtas.add(new PartiesOireachtas("Independent", "example2", R.drawable.independent_party_logo,R.drawable.independent_party_cover));
As you probably noticed an activity doesn't have a default constructor, but uses an onCreate(Bundle savedInstanceState) method. This means you should pass your information using this Bundles, or use something like SharedPreferences / SQLite. Since SharedPreferences / SQLite is a bit overkill for this in my opinion, you can add the object upon creating an intent.
Intent intent = new Intent();
intent.putExtra("name", parcelableObject);
If you want to pass a custom object you have to implement the Parcelable interface, this is pretty straightforward since you can basically auto generate all code for this in Android studio (just hit alt-enter a few times). For more information you can check out the following link.
https://developer.android.com/reference/android/os/Parcelable
In the receiving activity you can do the following:
Bundle bundle = getIntent().getExtras();
Object object = bundle.getObject("name"); //Don't forget to cast!
I have an Activity A with a fragment frag2. Inside the fragment I have a RecyclerView and Adapter to show a list of custom class objects. Adding objects to the adapter is handled programmatically. I have a button inside TwoFragment that opens a FragmentDialog. I'd like to add an object to my Adapter by confirming this dialog, but it seems that the adapter is null when called from the FragmentDialog.
The same adapter is not null, and works if I call it from the fragment OnClick.
Moreover the adapter is null only after screen rotation, it works fine before rotating.
To communicate between the two Fragments I implement a communicator class in activity A.
Activity A
public void respond(String type) {
frag2.addSupport(type);
}
frag2
public RecyclerView rv;
public ArrayList<support> supports;
public myAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supports = new ArrayList<>();
adapter = new myAdapter(supports);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View layout = inflater.inflate( R.layout.fragment_two, container, false);
layout.setId(R.id.frag2);
if (savedInstanceState!=null)
{
supports = savedInstanceState.getParcelableArrayList("supports");
}
rv = (RecyclerView) layout.findViewById(R.id.rv);
adapter = new myAdapter(supports);
rv.setAdapter(myAdapter);
rv.setLayoutManager(new LinearLayoutManager(getActivity()));
rv.setItemAnimator(new DefaultItemAnimator());
#Override
public void onClick(View v) {
int id = v.getId();
switch (id){
case R.id.button1:
addSupport(type); // THIS WORKS ALWAYS, even after screen rotate
break;
case R.id.button2:
showDialog();
break;
}
}
public void showDialog(){
FragmentManager manager = getFragmentManager();
myDialog dialog = new myDialog();
dialog.show(manager, "dialog");
}
public void addSupport(String type){
adapter.addItem(new support(type)); // this line gives null pointer on adapter, but only if called after screen rotate and only if called from the dialog
}
dialog
communicator comm;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog, null);
comm = (myCommunicator) getActivity();
create = (Button) view.findViewById(R.id.button_ok);
create.setOnClickListener(this);
return view;
}
#Override
public void onClick(View v) {
if(v.getId()==R.id.button_ok)
{
// some controls to set type
comm.respond(type)
dismiss();
}
else {
dismiss();
}
myAdapter
public class myAdapter extends RecyclerView.Adapter<myAdapter.VH> {
private LayoutInflater inflater;
private ArrayList<support> data = new ArrayList<>();
// settings for viewholder
public myAdapter (ArrayList<support> data)
{
this.data=data;
}
public void addItem(support dataObj) {
data.add(dataObj);
notifyItemInserted(data.size());
}
}
logcat
FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'myAdapter.addItem(myObject)' on a null object reference
I hope there are no mistakes, I shortened the code for better understanding. Keep in mind that everything works if I never rotate the screen.
I'm a beginner with android and I'm stuck with this for several days now. Please, help.
To understand the problem, it's as you say:
.. everything works if I never rotate the screen
So firstly to understand what happens on rotation, this is a quote from the Android Developer website:
Caution: Your activity will be destroyed and recreated each time the user rotates the screen. When the screen changes orientation, the system destroys and recreates the foreground activity because the screen configuration has changed and your activity might need to load alternative resources (such as the layout).
Ok, now to understand the error:
FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'myAdapter.addItem(myObject)' on a null object reference
Essentially, in your dialog class, you have created a strong dependency by declaring :
comm = (myCommunicator) getActivity();
because comm references objects which would have been destroyed on rotation, hence the NullPointerException.
To further understand runtime changes, such as orientation changes, I'd recommend going through Handling Runtime Changes.
Update
Thank you for your answer, what would you recommend instead of comm = (myCommunicator) getActivity(); ?
The solution comes in 3 parts:
Make sure the onCreate of Activity A has the following:
#Override
public void onCreate(Bundle savedInstanceState) {
......
// find the retained fragment on activity restarts
FragmentManager fm = getFragmentManager();
frag2 = (Frag2) fm.findFragmentByTag(“frag2”);
// create frag2 only for the first time
if (frag2 == null) {
// add the fragment
frag2 = new Frag2();
fm.beginTransaction().add(frag2 , “frag2”).commit();
}
......
}
Add setRetainInstance(true) to the onCreate of frag2.
Remove the implicit referencing i.e. comm = (myCommunicator) getActivity();, and implement something more loosely coupled for dialog.
dialog
public interface Communicator {
void respond(String type);
}
Communicator comm;
....
public void addCommunicator(Communicator communicator) {
comm = communicator;
}
public void removeCommunicator() {
comm = null;
}
#Override
public void onClick(View v) {
if((v.getId()==R.id.button_ok) && (comm!=null))
{
// some controls to set type
comm.respond(type);
}
// Regardless of what button is pressed, the dialog will dismiss
dismiss();
}
This allows you do the following in frag2 (or any other class for that matter):
frag2
<pre><code>
public class Frag2 extends Fragment implements dialog.Communicator {
........
public void showDialog() {
FragmentManager manager = getFragmentManager();
myDialog dialog = new myDialog();
dialog.addCommunicator(this);
dialog.show(manager, "dialog");
}
#Override
public void respond(String type){
adapter.addItem(new support(type));
}
}
I have a RecyclerView and a Fragment. I want to pass a JSONObject from RecyclerView to Fragment. So, I created an Interface and implemented it on Fragment and on RecyclerView. I initialized the variable and access the method in the fragment passing the JSONObject to it, however I am getting a NPE when trying to access the method:
public class ProductResultsListFragment extends Fragment implements ProductResultAdapterInterface{
//code here
#Override
public void showResultsInMap(JSONObject mapObject)
{
openMapFragment(mapObject);
}
In my RV class I have the following:
public class ProductSearchAdapter extends RecyclerView.Adapter<ProductSearchAdapter.ViewHolder> {
public ProductResultAdapterInterface mProductResultsListener;
.....
if (mapObjects.length()>0)
{
mProductResultsListener.showResultsInMap(mapObjects);
}
The if statement is inside my viewHolder in my RecyclerView class but the instance is made public.
I have tried casting my mProductResultsListener but dont know what class to cast it into.
public ProductResultAdapterInterface mProductResultsListener=((ProductResultAdapterInterface ) ?????);
Just a quick comment: the mapObject (JsonObject) is created on a onClick method inside a button in ViewHolder, I cannot pass bundle on OnCreate method.
this.btnMarkItemMap.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
switch (v.getId())
{
case (R.id.markProductinMap):
String mapObj=null;
if (mapObjects !=null)
{
String positionDescription=String.valueOf(position);
String productLatLngValue=txtProductPrice.getText().toString()+";"+ txtItemLanLong.getText().toString();
//tring mapPosition=
mapObj=mapObjects.optString(String.valueOf(position));
if (mapObj!="") //Button not click remove from map
{
btnMarkItemMap.setColorFilter(Color.rgb(0, 0, 0));
btnMarkItemMap.setTag(Color.rgb(0, 0, 0));
mapObjects.remove(positionDescription);
}
else //Button Click add to Map
//
{
btnMarkItemMap.setColorFilter(Color.rgb(255, 51, 102));
btnMarkItemMap.setTag(position);
try {mapObjects.put(positionDescription, productLatLngValue);}catch (JSONException ex){}
if (mapObjects.length()>0)
{
mProductResultsListener.showResultsInMap(mapObjects);
}
}
}else{
mapObjects=new JSONObject();
}
break;
}
}
});
where have you assigned the variable mProductResultsListener?
you need to assign this to the object of the fragment by passing it into the adapter. Otherwise its value is null and you will get a NullPointeException if you call a function on a null object.
If you have, then please update the question with that code.
You can pass the data using bundles like this
SomeFragment someFragment = new SomeFragment();
Bundle bundle = new Bundle();
bundle.putString("value1", "2");
bundle.putString("value2", "23");
bundle.putString("value3", "276");
bundle.putString("value4", "27");
bundle.putBoolean("flag", true);
someFragment.setArguments(bundle);
And in SomeFragment
Bundle bundle = getArguments();
strValue1 = bundle.getString("value1");
strValue2 = bundle.getString("value2");
and so on for getting all the passed data from bundle
ProductResultsListFragment yourFragment = new ProductResultsListFragment();
Bundle bundle = new Bundle();
bundle.putString("yourJsonObject", yourJsonObject.toString);
yourFragment.setArguments(bundle);
public class ProductResultsListFragment...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle savedInfo = this.getArguments();
String savedJsonString = savedInfo.getString("yourJsonObject");
JSONObject myJsonObject = new JSONObject(savedJsonString);
//....
On my Fragment adapter init, I added:
ProductResultAdapterInterface mProInterface=(ProductResultAdapterInterface) this;
mAdapter = new ProductSearchAdapter(R.layout.product_results_cardlist,getActivity(),productListFeed,mProInterface);
on my adapter constructor I changed the signature to accept the Interface as a parameter passed from the Fragment
public ProductSearchAdapter (int rowLayout, Context context,List<Product> feedList,ProductResultAdapterInterface mProductResultsListener) {
this.feedItemLists=feedList;
this.rowLayout = rowLayout;
this.mContext = context;
this.mProductResultsListener=mProductResultsListener;
}
Then I was able to access the method and not getting NPE. thanks all
I'm new in Android App developing via Java. I'm using Eclipse. If I create an Activity, Eclipse automatically generates a Placeholderfragment Class and Fragment.xml. Can I disable this function? Or is it not advisable to do that? I delete those files because I find it more complicated to use than just write in one xml file at the moment.
Second question is how do I implement a "starting Page" for my App? For example some sort of a logopage which automatically disables after a few seconds and switches to a new activity. Create a separate Activity for it or do I use something else?
Actually you need two activities, one startup Activity which is used to show your logo or some guide,the other is a MainActivity which should be started by the startUp Activity.
In short You can do something like this:
public class MainActivity extends FragmentActivity {
Fragment fragment;
String className;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d("MainActivity", "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Store the name of the class
className=MainActivity.class.getSimpleName();
//First fragment should be mounted on oncreate of main activity
if (savedInstanceState == null) {
/*fragment=FragmentOne.newInstance();
getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment).addToBackStack(className).commit();
*/
Fragment newFragment = FragmentOne.newInstance();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.container, newFragment).addToBackStack(null).commit();
Log.d("FRAGMENT-A", "fragment added to backstack");
}
}
}
FragmentOne.java
public class FragmentOne extends Fragment{
String className;
public static FragmentOne newInstance(){
Log.d("FragmentOne", "newInstance");
FragmentOne fragment = new FragmentOne();
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("FragmentOne", "onCreateView");
View view=inflater.inflate(R.layout.fragment_one, container, false);
//Store the name of the class
className=FragmentOne.class.getSimpleName();
return view;
}
}
Let me know if you need any more info
Well, in a Single Activity setup, the way I did this was the following:
public class SplashFragment extends Fragment implements View.OnClickListener
{
private volatile boolean showSplash = true;
private ReplaceWith activity_replaceWith;
private Button splashButton;
public SplashFragment()
{
super();
}
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try
{
activity_replaceWith = (ReplaceWith) activity;
}
catch (ClassCastException e)
{
Log.e(getClass().getSimpleName(), "Activity of " + getClass().getSimpleName() + "must implement ReplaceWith interface!", e);
throw e;
}
startSwitcherThread();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_splash, container, false);
splashButton = (Button) rootView.findViewById(R.id.fragment_splash_button);
splashButton.setOnClickListener(this);
return rootView;
}
public void startSwitcherThread()
{
Thread splashDelay = new Thread()
{
#Override
public void run()
{
try
{
long millis = 0;
while (showSplash && millis < 4000)
{
sleep(100);
millis += 100;
}
showSplash = false;
switchToFirstScreen();
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
splashDelay.start();
}
private void switchToFirstScreen()
{
activity_replaceWith.replaceWith(new FirstFragment());
}
#Override
public void onClick(View v)
{
if(v == splashButton)
{
if(showSplash == false)
{
switchToFirstScreen();
}
}
};
}
Where the ReplaceWith interface is the following:
import android.support.v4.app.Fragment;
public interface ReplaceWith
{
public void replaceWith(Fragment fragment);
}
And the replace function is implemented like so:
#Override
public void replaceWith(Fragment fragment)
{
getSupportFragmentManager()
.beginTransaction().replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit();
}
Now, most people will say this is not a good approach if you're using multiple activities, and/or using multiple orientations and aren't just simply displaying a single Fragment in a single Activity no matter what. And they are completely right in saying so.
Multiple orientations would require the Activity to be responsible for knowing what is the "next" Fragment at a given replace call, and where to place it (which container, or to start it in a new Activity). So this is a valid approach only if you are certain that you only have one container and there is one Fragment shown at a given time.
So basically, if this does not apply to you, then you need to utilize the same approach (make a specific delay before you replace the current Fragment or Activity with another one, this specific code allows you that once the splash has been shown once, then clicking the button will automatically take you to the next screen - typical game splash setup, really), but use activity callbacks specific to the Fragment in order to swap one out for the other.
A Fragment setup I recommend and isn't relying on this special case can be seen here: Simple Android Project or its equivalent on Code Review: Fragment Start-Up Project
I'm trying to pass a bundle with an integer value from a fragment Activity page to a new activity page. However the integer doesn't seem to be passed on and thus its value is always at 0. What is causing this problem? Am I missing something?
This is the Fragment Activity:
public class TravelogueSEOPageFragment extends SherlockFragment {
//method here that creates the bundle to be passed on to the new activity
public void Map(View v){
Intent offlineMapView = new Intent(v.getContext(), TOfflineMapView.class);
Bundle b = new Bundle();
b.putInt("id", travelogueID);
offlineMapView.putExtras(b);
startActivity(offlineMapView);
}
}
This is the new Activity class:
public class TOfflineMapView extends Activity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle b = getIntent().getExtras();
int tID = b.getInt("id");
...//remove unnecessary codes
}
}
So my tID is forever 0. Is this the wrong way to do it?