在销毁和重新创建活动时,处理程序仍在旧的,已销毁的活动实例中运行

时间:2017-04-09 18:26:16

标签: android android-activity thread-safety android-lifecycle android-handler

我试图让我的应用程序显示连续值,即使是通过活动破坏和娱乐。我使用后台线程来维护FPS,基本上只是将消息发送给处理程序。当销毁并重新创建活动时,将创建一个新的处理程序并将其发送到后台线程的新实例。但是,当收到消息时,它们似乎正由原始处理程序接收,在“已销毁”活动中运行。如果有比我更有经验的人能够参与其中,我真的很感激!

我的代码(我知道它不止一点,但几乎所有相关内容都发生在onCreate上):

public class MainActivity extends FragmentActivity {

final static int UPDATE_DISPLAY = 1;

//View Pager declarations
private static final int NUM_PAGES = 4; //Number of viewPager pages
private ViewPager mPager;
private ScreenSlidePagerAdapter mPagerAdapter;
private MainThread thread;
Fragment[] fragmentMap = new Fragment[NUM_PAGES];
//End view pager declarations

globalData gd;

private Handler handler = new Handler()
{
    @Override
    public void handleMessage(Message msg)
    {
        //TESTING-------------
        System.out.println("Received message from background thread.");
        System.out.println("Handler receiving message is: "+this.toString());
        System.out.println("Handler is running in activity: "+MainActivity.this.toString());
        //---------------

        if (msg.what == UPDATE_DISPLAY){
            ((TextView)findViewById(R.id.totalGas)).
                    setText("Gas: "+UnitValuePair.convertNumberToString(gd.getGasOwned()));
            ((TextView)findViewById(R.id.totalRock)).
                    setText("Rock: "+UnitValuePair.convertNumberToString(gd.getRockOwned()));
            ((TextView)findViewById(R.id.totalMetal)).
                    setText("Metal: "+UnitValuePair.convertNumberToString(gd.getMetalOwned()));
        }
        super.handleMessage(msg);
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    System.out.println("CREATING MainActivity");
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_screen_slide);

    Button resetButton = (Button)findViewById(R.id.resetButton);
    resetButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            resetData();
        }
    });

    //Instantiate ViewPager and PagerAdapter
    mPager = (ViewPager)findViewById(R.id.pager);
    mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
    mPager.setAdapter(mPagerAdapter);
    mPager.setOffscreenPageLimit(5);

    //Attach tablayout to view pager
    TabLayout tabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
    tabLayout.setupWithViewPager(mPager);

    //Init variable
    gd = globalData.getInstance();

    //Check if new game, and if so, init all base and actual values
    Context context = getApplicationContext();
    SharedPreferences sharedPref = context.getSharedPreferences(
            getString(R.string.preference_file_key), Context.MODE_PRIVATE);

    //If first start, then init vals and mark that it has been started in shared preferences
    boolean startedBefore = sharedPref.getBoolean("startedBefore", false);
    Log.d("TEST", "Value of startedBefore boolean is:" + Boolean.toString(startedBefore));
    if (!startedBefore)
    {
        gd.initVals();
        gd.storeData();
        gd.setNewGame(false);
        SharedPreferences.Editor editor = sharedPref.edit();
        editor.putBoolean("startedBefore", true);
        editor.commit();
    }
    else
    {
        System.out.println("Restoring data");
        gd.restoreData();
    }

    //TESTING---------
    System.out.println("Handler seen by activity is: "+handler.toString());
    System.out.println("New Activity is : "+this.toString());
    //--------------------

    //Start main update thread
    thread = new MainThread((ViewGroup)findViewById(R.id.mainContainer), gd, handler, getApplicationContext());
    thread.setRunning(true);
    thread.start();
}

@Override
public void onPause()
{
    System.out.println("PAUSING MainActivity");
    super.onPause();
    gd.storeData();
}

@Override
public void onResume()
{
    super.onResume();
    System.out.println("RESUMING MainActivity");
}

@Override
public void onBackPressed(){
    if (mPager.getCurrentItem() == 0){
        //If the user is currently looking at the first step, allow the system to handle
        //back button. Calls finish() on activity and pops the back stack
        super.onBackPressed();
    } else {
        mPager.setCurrentItem(mPager.getCurrentItem() - 1);
    }
}

public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter
{
    public ScreenSlidePagerAdapter(FragmentManager fm)
    {
        super(fm);
    }

    @Override
    public Fragment getItem (int position)
    {
        switch (position) {
            case 0:
                CreationFragment newFragment0 = CreationFragment.newInstance(0);
                fragmentMap[0] = newFragment0;
                return newFragment0;

            case 1:
                UniverseFragment newFragment1 = UniverseFragment.newInstance(1);
                fragmentMap[1] = newFragment1;
                return newFragment1;

            default:
                return ScreenSlidePageFragment.create(position);
        }
    }
    @Override
    //Provides the titles of the fragments for use in tabLayout
    public CharSequence getPageTitle(int position) {
        String title=" ";
        switch (position){
            case 0:
                title="Create";
                break;
            case 1:
                title="Universe";
                break;
            case 2:
                title="Upgrades";
                break;
            case 3:
                title="Stats";
                break;
        }

        return title;
    }

    @Override
    public int getCount()
    {
        return NUM_PAGES;
    }

}

@Override
protected void onDestroy(){
    System.out.println("DESTROYING MainActivity");
    //handler = null;
    super.onDestroy();
}

public void resetData()
{
    gd.initVals();
    gd.setVisibilityChangedDisplay(true);
    gd.setVisibilityChangedInv(true);
    gd.setChangedDisplay(true);
    gd.setChangedInv(true);
}

}

logcat:

原始处理程序6402d8e开始在活动108d9af中运行。活动暂停,销毁并重新创建为活动3112cf7。创建一个新的处理程序70420f6并将其发送到新的后台线程。后台线程开始发送消息,并且它们被接收,但接收它们的处理程序说它是原始的6402d8e,仍然在活动108d9af中运行!

04-09 20:17:41.471 29339-29339/com.kessler.alexi.stellargrowth I/System.out: Received message from background thread.
04-09 20:17:41.471 29339-29339/com.kessler.alexi.stellargrowth I/System.out: Handler receiving message is: Handler (com.kessler.alexi.stellargrowth.MainActivity$1) {6402d8e}
04-09 20:17:41.471 29339-29339/com.kessler.alexi.stellargrowth I/System.out: Handler is running in activity: com.kessler.alexi.stellargrowth.MainActivity@108d9af
04-09 20:17:41.502 29339-29339/com.kessler.alexi.stellargrowth I/System.out: PAUSING MainActivity
04-09 20:17:41.502 29339-29339/com.kessler.alexi.stellargrowth D/TEST: Storing data
04-09 20:17:41.741 29339-29339/com.kessler.alexi.stellargrowth I/System.out: DESTROYING MainActivity
04-09 20:17:41.759 29339-29339/com.kessler.alexi.stellargrowth I/System.out: CREATING MainActivity
04-09 20:17:41.759 29339-29339/com.kessler.alexi.stellargrowth W/FragmentManager: moveToState: Fragment state for CreationFragment{a0042e2 #1 id=0x7f0d006d} not updated inline; expected state 1 found 0
04-09 20:17:41.759 29339-29339/com.kessler.alexi.stellargrowth W/FragmentManager: moveToState: Fragment state for UniverseFragment{66e2681 #2 id=0x7f0d006d} not updated inline; expected state 1 found 0
04-09 20:17:41.775 29339-29339/com.kessler.alexi.stellargrowth D/TEST: Value of startedBefore boolean is:true
04-09 20:17:41.775 29339-29339/com.kessler.alexi.stellargrowth I/System.out: Restoring data
04-09 20:17:41.775 29339-29339/com.kessler.alexi.stellargrowth D/TEST: Retrieving data
04-09 20:17:41.775 29339-29339/com.kessler.alexi.stellargrowth I/System.out: Handler seen by activity is: Handler (com.kessler.alexi.stellargrowth.MainActivity$1) {70420f6}
04-09 20:17:41.775 29339-29339/com.kessler.alexi.stellargrowth I/System.out: New Activity is : com.kessler.alexi.stellargrowth.MainActivity@3112cf7
04-09 20:17:41.775 29339-29339/com.kessler.alexi.stellargrowth I/System.out: New thread created. Handler seen by thread is: Handler (com.kessler.alexi.stellargrowth.MainActivity$1) {70420f6}
04-09 20:17:41.805 29339-29339/com.kessler.alexi.stellargrowth I/System.out: RESUMING MainActivity
04-09 20:17:41.811 29339-29339/com.kessler.alexi.stellargrowth I/System.out: Received message from background thread.
04-09 20:17:41.811 29339-29339/com.kessler.alexi.stellargrowth I/System.out: Handler receiving message is: Handler (com.kessler.alexi.stellargrowth.MainActivity$1) {6402d8e}
04-09 20:17:41.811 29339-29339/com.kessler.alexi.stellargrowth I/System.out: Handler is running in activity: com.kessler.alexi.stellargrowth.MainActivity@108d9af

---- UPDATE ------ 新活动启动新线程,但发送消息的那个具有应该被销毁的旧ID的ID

04-10 10:43:41.149 19846-19846/com.kessler.alexi.stellargrowth I/System.out: New Activity is : com.kessler.alexi.stellargrowth.MainActivity@49e83d5
04-10 10:43:41.149 19846-19846/com.kessler.alexi.stellargrowth I/System.out: New thread created with id: 23410
04-10 10:43:41.149 19846-19846/com.kessler.alexi.stellargrowth I/System.out: Handler seen by new thread is: Handler (com.kessler.alexi.stellargrowth.MainActivity$1) {cf3308c}
04-10 10:43:41.149 19846-19846/com.kessler.alexi.stellargrowth I/System.out: Starting thread with id: 23410
04-10 10:43:41.180 19846-19878/com.kessler.alexi.stellargrowth I/System.out: Thread sending message is: 23405
04-10 10:43:41.188 19846-19846/com.kessler.alexi.stellargrowth I/System.out: RESUMING MainActivity
04-10 10:43:41.194 19846-19846/com.kessler.alexi.stellargrowth I/System.out: Received message from background thread.
04-10 10:43:41.194 19846-19846/com.kessler.alexi.stellargrowth I/System.out: Handler receiving message is: Handler (com.kessler.alexi.stellargrowth.MainActivity$1) {6402d8e}
04-10 10:43:41.194 19846-19846/com.kessler.alexi.stellargrowth I/System.out: Handler is running in activity: com.kessler.alexi.stellargrowth.MainActivity@108d9af

----- ------ ANSWER 事实证明,这只是将onDestroy更改为

的问题
@Override
protected void onDestroy(){
    System.out.println("DESTROYING MainActivity");
    thread.setRunning(false);
    super.onDestroy();
}

线程运行代码(如下所示)获取了锁,基本上从不关闭,因为布尔运行没有自动更改。当新线程启动时,它尝试获取锁定,失败,并且旧的线程再次启动。

@Override
public void run() {
    System.out.println("Thread "+this.getId()+" started.");
    if (MainThread.lock.tryLock()) {
        System.out.println("Thread "+this.getId()+" acquired lock");
        try {
            long tickCount = 0L;
            while (running) {
                tickCount++;
                controlFPS(tickCount);
                update();
            }
        } finally {
            MainThread.lock.unlock();
        }
    }
}

1 个答案:

答案 0 :(得分:0)

将线程声明为类中的私有字段,以便类中的所有方法都可以访问它,然后:

在onDestroy()方法

使用

              thread.Abort()