Android: How to have a single (main) Activity instance or retrieve the Activity instance that I need?
I am developing an Android app that execute a thread and update the GUI via a GUI Handler on GUI Activity. I need that the thread runs also when the user put the app in background. This is done!
My problem is that I want to return to the Activity that was running before when the user return to it by app history or by apps launcher. Instead many often Android launch a new Activity instance.
I have tried with application property "launchMode" ("SingleTop", "SingleInstance", "SingleTask"), but I can't obtain my goal.
An alternative method could be that I open a notification from every Activity that run a thread, but how can I do to open the Activity "linked" to that notification?
I hope in your help
Thank you
Edit: sample code for simplicity I have put all code in a single class.
If you try this app, press the button on the bottom and the thread starts. Now if you go to home open other apps, you can see that the thread is again running, you see the toast (this is what I want), Then if you return on my app, or by clicking notification or by launcher or by app history, a new instance can be created and I lost the previous running. How can I solve this? How can I return always on running activity?
TestThreadActivity.java
package tld.test;
import java.util.ArrayList;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.ToggleButton;
public class TestThreadActivity extends Activity {
private static final int NOTIFY_THREAD_IS_RUNNING = 1;
private MyRunnable myRunnable;
private Thread myThread;
// List of all message
private ArrayList<String> responseList = new ArrayList<String>(10);
// ListView adapter
private ArrayAdapter<String> responseListAdapter;
/**
* Called when the activity is first created.
**/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
responseListAdapter = new ArrayAdapter<String>(this,
R.layout.list_item, responseList);
ListView listViewResponse = (ListView) findViewById(R.id.listViewResponse);
listViewResponse.setAdapter(responseListAdapter);
ToggleButton toggleButton = (ToggleButton) findViewById(R.id.startStopBtn);
toggleButton.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
if (isChecked)
startThread();
else
stopThread();
}
});
}
/**
* Create and start the thread
**/
private void startThread() {
// Clear listview
responseList.clear();
responseListAdapter.notifyDataSetChanged();
if (myRunnable == null)
myRunnable = new MyRunnable(guiHandler);
myThread = new Thread(myRunnable, "myThread-" + System.currentTimeMillis());
myThread.start();
notifyThread();
Toast.makeText(this, "Thread Started", Toast.LENGTH_SHORT).show();
}
/**
* Stop the thread
**/
private void stopThread() {
myRunnable.stop();
cancelNotifyThread();
Toast.makeText(this, "Thread Stopped", Toast.LENGTH_SHORT).show();
}
/**
* Crea una notifica sulla barra di stato
*/
private void notifyThread() {
int icon = R.drawable.icon; // default icon from resources
CharSequence tickerText = "Thread is running"; // ticker-text
long when = System.currentTimeMillis(); // notification time
Context context = getApplicationContext(); // application Context
CharSequence contentTitle = getString(R.string.app_name); // expanded
// message
// title
CharSequence contentText = "Thread is running..."; // expanded message
// text
Intent notificationIntent = new Intent(this, this.getClass());
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
// the next two lines initialize the Notification, using the
// configurations above
Notification notification = new Notification(icon, tickerText, when);
notification.flags |= Notification.FLAG_ONGOING_EVENT;
notification.setLatestEventInfo(context, contentTitle, contentText,
contentIntent);
NotificationManager notificationManager = ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE));
notificationManager.notify(NOTIFY_THREAD_IS_RUNNING, notification);
}
/**
* Clear previous notification
*/
private void cancelNotifyThread() {
NotificationManager notificationManager = ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE));
notificationManager.cancel(NOTIFY_THREAD_IS_RUNNING);
}
// My GUI Handler. Receive message from thread to put on Activity's listView
final private Handler guiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
String newMsg = (String) msg.obj;
// Add message to listView
responseList.add(newMsg);
responseListAdapter.notifyDataSetChanged();
// and show a toast to view that is running also when it is not in
// foreground.
Toast.makeText(TestThreadActivity.this, newMsg, Toast.LENGTH_SHORT)
.show();
super.handleMessage(msg);
}
};
/**
* Simple runnable. Only wait WAIT_INTERVAL milliseconds and send a message
* to the GUI
**/
public class MyRunnable implements Runnable {
public static final int WHAT_ID = 1;
private static final int WAIT_INTERVAL = 5000;
private boolean isRunning;
private int counter;
private Handler guiHandler;
public MyRunnable(Handler guiHandler) {
super();
this.guiHandler = guiHandler;
}
public void stop() {
isRunning = false;
}
@Override
public void run() {
counter = 0;
isRunning = true;
while (isRunning) {
// Pause
try {
Thread.sleep(WAIT_INTERVAL);
} catch (InterruptedException e) {
}
// Notify GUI
Message msg = guiHandler.obtainMessage(WHAT_ID,
"Thread is running: " + (++counter) + " loop");
guiHandler.sendMessage(msg);
开发者_C百科 }
}
}
}
layout\main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView android:layout_width="match_parent" android:id="@+id/listViewResponse"
android:layout_height="0dip" android:layout_weight="1.0" android:transcriptMode="normal"></ListView>
<ToggleButton android:id="@+id/startStopBtn"
android:layout_height="wrap_content" android:layout_width="match_parent" android:textOn="@string/toggleBtnStop" android:textOff="@string/toggleBtnStart"></ToggleButton>
</LinearLayout>
layout\list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
android:textSize="16sp" >
</TextView>
values\strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">TestThread</string>
<string name="toggleBtnStart">Start Thread</string>
<string name="toggleBtnStop">Stop Thread</string>
</resources>
I have solved!
The problem was the Back button that destroy the activity. Then I have override onBackPressed method and ask user if quit or leave activity running. Moreover I have set launchMode="singleTop" that is what I need.
It was easier than I thought ;)
But I have a doubt: How could app running after destroy? The toast was visible also after thath back button was pressed. What is destroyed then?
精彩评论