“Activity 已经泄露了原本添加在这里的窗口”

Posted

技术标签:

【中文标题】“Activity 已经泄露了原本添加在这里的窗口”【英文标题】:“Activity has leaked window that was originally added here” 【发布时间】:2017-06-02 17:19:29 【问题描述】:

我有以下代码,我想将 mysql 同步到 SQLITE。问题是当我点击同步按钮时会出现错误。数据正在从 Mysql 服务器传输到我的 SQLITE,但它不会出现在我将数据放在列表中的应用程序的活动中。

public class MainActivity extends ActionBarActivity 
    // DB Class to perform DB related operations
    DBController controller = new DBController(this);
    // Progress Dialog Object
    ProgressDialog prgDialog;
    HashMap<String, String> queryValues;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Get User records from SQLite DB
        ArrayList<HashMap<String, String>> userList = controller.getAllUsers();
        // If users exists in SQLite DB
        if (userList.size() != 0) 
            // Set the User Array list in ListView
            ListAdapter adapter = new SimpleAdapter(MainActivity.this, userList, R.layout.view_user_entry, new String[] 
                            "userId", "userName" , new int[]  R.id.userId, R.id.userName );
            ListView myList = (ListView) findViewById(android.R.id.list);
            myList.setAdapter(adapter);
        
        // Initialize Progress Dialog properties
        prgDialog = new ProgressDialog(this);
        prgDialog.setMessage("Transferring Data from Remote MySQL DB and Syncing SQLite. Please wait...");
        prgDialog.setCancelable(false);
        // BroadCase Receiver Intent Object
        Intent alarmIntent = new Intent(getApplicationContext(), SampleBC.class);
        // Pending Intent Object
        PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        // Alarm Manager Object
        AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
        // Alarm Manager calls BroadCast for every Ten seconds (10 * 1000), BroadCase further calls service to check if new records are inserted in 
        // Remote MySQL DB
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis() + 5000, 10 * 1000, pendingIntent);
    

    // Options Menu (ActionBar Menu)
    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    

    // When Options Menu is selected
    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
        // Handle action bar item clicks here. 
        int id = item.getItemId();
        // When Sync action button is clicked
        if (id == R.id.refresh) 
            // Transfer data from remote MySQL DB to SQLite on Android and perform Sync
            syncSQLiteMySQLDB();
            return true;
        
        return super.onOptionsItemSelected(item);
    

    // Method to Sync MySQL to SQLite DB
    public void syncSQLiteMySQLDB() 
        // Create AsycHttpClient object
        AsyncHttpClient client = new AsyncHttpClient();
        // Http Request Params Object
        RequestParams params = new RequestParams();
        // Show ProgressBar
        prgDialog.show();

        // Make Http call to getusers.php
        client.post("http://download.grupsapte.ro/sinc/getusers.php", params, new AsyncHttpResponseHandler() 
                @Override
                public void onSuccess(String response) 
                    // Hide ProgressBar
                    prgDialog.hide();
                    // Update SQLite DB with response sent by getusers.php
                    updateSQLite(response);
                
                // When error occured
                @Override
                public void onFailure(int statusCode, Throwable error, String content) 
                    // TODO Auto-generated method stub
                    // Hide ProgressBar
                    prgDialog.hide();
                    if (statusCode == 404) 
                        Toast.makeText(getApplicationContext(), "Requested resource not found", Toast.LENGTH_LONG).show();
                     else if (statusCode == 500) 
                        Toast.makeText(getApplicationContext(), "Something went wrong at server end", Toast.LENGTH_LONG).show();
                     else 
                        Toast.makeText(getApplicationContext(), "Unexpected Error occcured! [Most common Error: Device might not be connected to Internet]",
                                Toast.LENGTH_LONG).show();
                    
                
        );
    

    public void updateSQLite(String response)
        ArrayList<HashMap<String, String>> usersynclist;
        usersynclist = new ArrayList<HashMap<String, String>>();
        // Create GSON object
        Gson gson = new GsonBuilder().create();
        try 
            // Extract JSON array from the response
            JSONArray arr = new JSONArray(response);
            System.out.println(arr.length());
            // If no of array elements is not zero
            if(arr.length() != 0)
                // Loop through each array element, get JSON object which has userid and username
                for (int i = 0; i < arr.length(); i++) 
                    // Get JSON object
                    JSONObject obj = (JSONObject) arr.get(i);
                    System.out.println(obj.get("userId"));
                    System.out.println(obj.get("userName"));
                    // DB QueryValues Object to insert into SQLite
                    queryValues = new HashMap<String, String>();
                    // Add userID extracted from Object
                    queryValues.put("userId", obj.get("userId").toString());
                    // Add userName extracted from Object
                    queryValues.put("userName", obj.get("userName").toString());
                    // Insert User into SQLite DB
                    controller.insertUser(queryValues);
                    HashMap<String, String> map = new HashMap<String, String>();
                    // Add status for each User in Hashmap
                    map.put("Id", obj.get("userId").toString());
                    map.put("status", "1");
                    usersynclist.add(map);
                
                // Inform Remote MySQL DB about the completion of Sync activity by passing Sync status of Users
                updateMySQLSyncSts(gson.toJson(usersynclist));
                // Reload the Main Activity
                reloadActivity();
            
         catch (JSONException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
        
    

    // Method to inform remote MySQL DB about completion of Sync activity
    public void updateMySQLSyncSts(String json) 
        System.out.println(json);
        AsyncHttpClient client = new AsyncHttpClient();
        RequestParams params = new RequestParams();
        params.put("syncsts", json);
        // Make Http call to updatesyncsts.php with JSON parameter which has Sync statuses of Users
        client.post("http://download.grupsapte.ro/sinc/updatesyncsts.php", params, new AsyncHttpResponseHandler() 
            @Override
            public void onSuccess(String response) 
                Toast.makeText(getApplicationContext(), "MySQL DB has been informed about Sync activity", Toast.LENGTH_LONG).show();
            

            @Override
            public void onFailure(int statusCode, Throwable error, String content) 
                    Toast.makeText(getApplicationContext(), "Error Occured", Toast.LENGTH_LONG).show();
            
        );
    

    // Reload MainActivity
    public void reloadActivity() 
        Intent objIntent = new Intent(getApplicationContext(), MainActivity.class);
        startActivity(objIntent);
    

错误如下:

01-18 12:54:59.821: E/WindowManager(2022): android.view.WindowLeaked: Activity com.prgguru.example.MainActivity has leaked window com.android.internal.policy.PhoneWindow$DecorViewa19b187 G.E...... R.....ID 0,0-1336,384 that was originally added here
01-18 12:54:59.821: E/WindowManager(2022):  at android.view.ViewRootImpl.<init>(ViewRootImpl.java:565)
01-18 12:54:59.821: E/WindowManager(2022):  at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:326)
01-18 12:54:59.821: E/WindowManager(2022):  at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
01-18 12:54:59.821: E/WindowManager(2022):  at android.app.Dialog.show(Dialog.java:350)
01-18 12:54:59.821: E/WindowManager(2022):  at com.prgguru.example.MainActivity.syncSQLiteMySQLDB(MainActivity.java:96)
01-18 12:54:59.821: E/WindowManager(2022):  at com.prgguru.example.MainActivity.onOptionsItemSelected(MainActivity.java:83)
01-18 12:54:59.821: E/WindowManager(2022):  at android.app.Activity.onMenuItemSelected(Activity.java:3204)
01-18 12:54:59.821: E/WindowManager(2022):  at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:353)
01-18 12:54:59.821: E/WindowManager(2022):  at android.support.v7.app.ActionBarActivity.superOnMenuItemSelected(ActionBarActivity.java:244)
01-18 12:54:59.821: E/WindowManager(2022):  at android.support.v7.app.ActionBarActivityDelegateICS.onMenuItemSelected(ActionBarActivityDelegateICS.java:165)
01-18 12:54:59.821: E/WindowManager(2022):  at android.support.v7.app.ActionBarActivity.onMenuItemSelected(ActionBarActivity.java:130)
01-18 12:54:59.821: E/WindowManager(2022):  at android.support.v7.app.ActionBarActivityDelegateICS$WindowCallbackWrapper.onMenuItemSelected(ActionBarActivityDelegateICS.java:300)
01-18 12:54:59.821: E/WindowManager(2022):  at com.android.internal.policy.PhoneWindow.onMenuItemSelected(PhoneWindow.java:1259)
01-18 12:54:59.821: E/WindowManager(2022):  at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:801)
01-18 12:54:59.821: E/WindowManager(2022):  at com.android.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:155)
01-18 12:54:59.821: E/WindowManager(2022):  at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:954)
01-18 12:54:59.821: E/WindowManager(2022):  at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:944)
01-18 12:54:59.821: E/WindowManager(2022):  at android.widget.ActionMenuView.invokeItem(ActionMenuView.java:655)
01-18 12:54:59.821: E/WindowManager(2022):  at com.android.internal.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:251)
01-18 12:54:59.821: E/WindowManager(2022):  at android.view.View.performClick(View.java:5697)
01-18 12:54:59.821: E/WindowManager(2022):  at android.widget.TextView.performClick(TextView.java:10826)
01-18 12:54:59.821: E/WindowManager(2022):  at android.view.View$PerformClick.run(View.java:22526)
01-18 12:54:59.821: E/WindowManager(2022):  at android.os.Handler.handleCallback(Handler.java:739)
01-18 12:54:59.821: E/WindowManager(2022):  at android.os.Handler.dispatchMessage(Handler.java:95)
01-18 12:54:59.821: E/WindowManager(2022):  at android.os.Looper.loop(Looper.java:158)
01-18 12:54:59.821: E/WindowManager(2022):  at android.app.ActivityThread.main(ActivityThread.java:7225)
01-18 12:54:59.821: E/WindowManager(2022):  at java.lang.reflect.Method.invoke(Native Method)
01-18 12:54:59.821: E/WindowManager(2022):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
01-18 12:54:59.821: E/WindowManager(2022):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)

我知道我应该把 this.finish() 放在某个地方,但我不知道确切的位置。希望有人帮助我。

【问题讨论】:

看到这个...***.com/questions/21817888/… Activity has leaked window that was originally added的可能重复 Activity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@44f72ff0 that was originally added here的可能重复 您已经创建了一个进度对话框,但您从未关闭它。当您的Activity 完成时,对话框仍然处于活动状态,因此您会收到此“活动已泄漏窗口”错误。链接的问题包含一些有用的信息。 【参考方案1】:
public void syncSQLiteMySQLDB() 
   // Create AsycHttpClient object
   AsyncHttpClient client = new AsyncHttpClient();
   // Http Request Params Object
   RequestParams params = new RequestParams();
   // Show ProgressBar
   prgDialog = new ProgressDialog(MainActivity.this);
   prgDialog.setMessage("Transferring Data from Remote MySQL DB and Syncing SQLite. Please wait...");
   prgDialog.setCancelable(false);
   prgDialog.show();

   // Make Http call to getusers.php
   client.post("http://download.grupsapte.ro/sinc/getusers.php", params, new AsyncHttpResponseHandler() 
           @Override
           public void onSuccess(String response) 

               // Update SQLite DB with response sent by getusers.php
               updateSQLite(response);
           
           // When error occured
           @Override
           public void onFailure(int statusCode, Throwable error, String content) 
               // TODO Auto-generated method stub

               if (statusCode == 404) 
                   Toast.makeText(getApplicationContext(), "Requested resource not found", Toast.LENGTH_LONG).show();
                else if (statusCode == 500) 
                   Toast.makeText(getApplicationContext(), "Something went wrong at server end", Toast.LENGTH_LONG).show();
                else 
                   Toast.makeText(getApplicationContext(), "Unexpected Error occcured! [Most common Error: Device might not be connected to Internet]",
                           Toast.LENGTH_LONG).show();
               
           

            @Override
            public void onFinish()
                // Hide ProgressBar
                if(prgDialog.isShowing())
               prgDialog.hide();
            
   );

首先你应该声明本地进度条对象并使用 MainActivity 初始化。这也 AsyncHttpClient 有一个方法 onFinish() 将在成功或失败后调用,所以你只需要在 onfinsh 方法中隐藏你的进度条,你总是应该检查进度条是否显示所以它不会导致产生异常

【讨论】:

你应该解释你做了什么以及为什么。仅代码答案通常没有帮助。 @AxelH 感谢您的建议 :) 好的..所以错误消失了,但我不知道为什么我获取数据的列表没有被填充。【参考方案2】:

试试看, 在显示进度对话框之前。检查它是否已经显示,如果是,关闭它并显示对话框。

在 syncSQLiteMySQLDB 中,您正在显示进度对话框。首先检查您是否已经有一个 prgDialog 实例及其显示。然后关闭现有的 prgDialog 并显示。并在显示之前检查活动是否未完成

public void syncSQLiteMySQLDB() 
....
if( !isFinishing()) 
    if(prgDialog !=null) 
        if(prgDialog.isShowing()) 
              prgDialog.dismiss()
        
        prgDialog.show();
    

  

【讨论】:

为什么?什么时候 ?在哪里 ?请通过编辑您的答案来解释您的解决方案 @AxelH 感谢您的建议,伙计:)【参考方案3】:

你需要dismiss() progressDialog

@Override
            public void onSuccess(String response) 
                // dismiss ProgressBar
                if (prgDialog != null) 
                prgDialog.dismiss(); //not hide try dismiss the dialog
                // Update SQLite DB with response sent by getusers.php
                updateSQLite(response);
            
        

也在你的onFailure方法dismiss()dialog

【讨论】:

【参考方案4】:

您正在重新加载活动而不关闭对话框,因此您应该先关闭。

public void reloadActivity() 
           // dissmiss dialog first then reload activity.
            if(prgDialog.isShowing()) 
              prgDialog.dismiss();
              Intent objIntent = new Intent(getApplicationContext(), MainActivity.class);
              startActivity(objIntent);
             

    

【讨论】:

【参考方案5】:

花了几分钟才发现这个内存泄漏,不需要在准备对话框构建器中使用.show

  @Override
  protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) 

        builder
           .setIcon(getIconImage(context))
           .setTitle("title)
           .setMessage("message")
           .setPositiveButton("ok",
                  new DialogInterface.OnClickListener() 
                      public void onClick(DialogInterface dialog, int which) 
                         dialog.cancel();
                          start intent
                      
                  )
           .setNegativeButton("cancel",
                  new DialogInterface.OnClickListener() 
                      public void onClick(DialogInterface dialog, int which) 
                         dialog.cancel();

                     
                  )
            .show();

  
  else
         do something else
   

【讨论】:

以上是关于“Activity 已经泄露了原本添加在这里的窗口”的主要内容,如果未能解决你的问题,请参考以下文章