Android:如何拥有单个(主)Activity 实例或检索我需要的 Activity 实例?
我正在开发一个 Android 应用程序,它执行线程并通过 GUI 活动上的 GUI 处理程序更新 GUI。 我需要当用户将应用程序置于后台时线程也运行。这样就完成了!
我的问题是,当用户通过应用程序历史记录或应用程序启动器返回之前运行的活动时,我想返回到之前运行的活动。 相反,许多 Android 经常启动一个新的 Activity 实例。
我尝试过应用程序属性“launchMode”(“SingleTop”,“SingleInstance”,“SingleTask”),但我无法实现我的目标。
另一种方法可能是我从运行线程的每个活动中打开一个通知,但是如何打开“链接”到该通知的活动呢?
我希望得到您的帮助
谢谢
编辑:示例代码 为简单起见,我将所有代码放在一个类中。
如果您尝试此应用程序,请按底部的按钮,线程就会开始。现在,如果您回家打开其他应用程序,您可以看到线程再次运行,您会看到吐司(这就是我想要的),然后如果您返回我的应用程序,或者通过单击通知或通过启动器或通过应用程序历史记录,可以创建一个新实例,并且我丢失了之前的运行。 我该如何解决这个问题?如何才能始终恢复跑步活动?
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);
}
}
}
}
布局\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>
布局\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>
值\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 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);
}
}
}
}
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>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我已经解决了!
问题是“后退”按钮破坏了活动。
然后我重写 onBackPressed 方法并询问用户是否退出或保持活动运行。此外,我已经设置了 launchMode="singleTop" 这就是我所需要的。
这比我想象的要容易;)
但是我有一个疑问:应用程序在销毁后如何运行?按下后退按钮后,吐司也可见。那什么被毁掉了呢?
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?