Android:如何拥有单个(主)Activity 实例或检索我需要的 Activity 实例?

发布于 2024-12-06 12:24:13 字数 8118 浏览 1 评论 0原文

我正在开发一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

旧梦荧光笔 2024-12-13 12:24:13

我已经解决了!

问题是“后退”按钮破坏了活动。
然后我重写 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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文