Issue
I would like to navigate between activities and pass an object. The object is a Tcp connection between the client (the phone) and the server (a computer). When the user presses a button, the new activity is started and the user is redirected to a new page.
I want to be able to send certain commands to the server from different activities.
However, I am having issues passing the object between activities. Whenever I try to go to the next activity, I get the following error:
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object Caused by: java.io.NotSerializableException: com.example.user.myapp.HomePage$ConnectTask$1
The Asynctask is throwing the error, I believe it not to be a duplicate of this question. All classes already implement Serializable.
In my onCreate
connecttask = new ConnectTask();
connecttask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
final Intent i = new Intent(getBaseContext(),Menu.class);
final ArrayList<TcpClient> testing = new ArrayList<TcpClient>();
In my setOnClickListener
public void onClick(View view) {
if(Username.getText().toString().equals("admin") &&
Password.getText().toString().equals("admin")) {
Toast.makeText(getApplicationContext(),
"Redirecting...",Toast.LENGTH_SHORT).show();
testing.add(mTcpClient);
i.putParcelableArrayListExtra("extra",testing);
startActivity(i);
}
The AsyncTask:
public class ConnectTask extends AsyncTask<String, String, TcpClient> implements Serializable {
@Override
protected TcpClient doInBackground(String... message) {
System.out.println("Executed call");
mTcpClient = new TcpClient(new TcpClient.OnMessageReceived() {
@Override
public void messageReceived(String message) {
try {
publishProgress(message);
if (message != null) {
System.out.println("Returned message from socket::::: >>>>>>" + message);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}, ipAddressOfServerDevice);
if (mTcpClient != null) {
mTcpClient.sendMessage("Initial message when connected with Socket Server");
}
delay();
return mTcpClient;
}
@Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
arrayList.add(values[0]);
mAdapter.notifyDataSetChanged();
}
}
Solution
Think about what "Parcellable" means. It means you can serialize it to a binary stream and potentially pass it between processes. How can you do that with a task or a thread? Not happening.
You need to do a little reading on activity lifecycle, and the various strategies for managing objects that live longer than an activity.
The scenarios you need to think about. (1) your Activity is torn down and resurrected due to something like an orientation change (short term termination of the activity). (2) Your Activity goes into the background,so android tears down your application, and resurrects it later (long term termination of the activity). (3) Your application goes into the background, and Android terminates the entire process with the intention of resurrecting it later from serialized state. Scenario (3) is the reason why Parcellables are used to serialize Activity state: the parcellable gets written to temporary storage somewhere, the process terminates, and some very long time later, gets restarted, the parcellables get read from temporary storage, and a new Activity gets started with those Parcellables supplied as appropriate arguments. Obviously your thread isn't going to survive that.
The Right Thing To Do is wrap the entire TCP connection in a service, which will survive better if Android wants to terminate your process when it goes into the background, and resurrect it later. The advantage of a service is that it can live for up to 30 minutes in background state even if your app is in the background; and forever, if you post a Notification for your service. Services are somewhat painful to manage; but a lot of the pain relates to stuff you have to do anyway. The biggest pain being that your Activity must completely remove all callbacks from the background task so that the application can be garbage collected. The Service provides a framework for doing that, along with some potentially useful lifecycle management.
You can also use a fragment (without UI) to address scenario (1). And for a short while in scenario (2). You could argue that for simply connecting, a fragment task is suitable. But my guess is that once you connect, you're going run into the same sort of problems with maintaining the connection. So Service is probably the right choice. Either that or a fragment that takes complete responsibility for shepherding async TCP operations onto a background thread and back again.
If your TCP connection is utterly disposable and you don't mind losing it, you can also use a static variable. You do need to be very careful about managing the lifecycle though, and be very careful about dangling references to Activities that have been killed.
Somewhat painful. But if you work it through it all makes sense, and most of the hard work to implement a service has to be done otherwise anyway if you go for a custom solution.
Answered By - Robin Davies
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.