AsyncTask *偶尔会遇到连接超时/错误问题,潜在的内存泄漏或Android Studio错误?

时间:2015-02-05 17:12:10

标签: android memory-leaks android-asynctask android-studio android-context

我是Android和Java开发的新手我已经整理了一个简单的演示应用程序来开始学习。它由:

组成
  • 扩展ActionBarActivity的主要活动,其中
  • 实例化了ViewPager
  • FragmentPagerAdapter负责出现......
  • ......在给定时间内三分之一Fragment
  • 其中一个片段,在创建时,只是为了尝试,执行AsyncTask(由另一个类定义),触发HTTP request完成后(onPostExecute)... < / LI>
  • 会在解雇它的片段中填充TableLayout

我还尝试保持与较旧Android平台的兼容性,因此我在必要时使用支持库。

我看到的问题,或者我应该说我正在看(继续阅读..), 不时 我得到了

  

org.apache.http.conn.HttpHostConnectException:与http://123.123.123.123:80/notes.json的连接被拒绝

与大约40秒的漫长等待后的超时错误消息一起。 当然已经添加了互联网许可。

这是随机发生的,它通常在首次运行垃圾收集器后发生。

在花了几天试图调试之后,我终于重新启动了系统,这种行为完全消失了。

但是,鉴于我花了很多时间(认为我正在泄漏某些东西),我仍然希望:

  1. 了解正在发生的事情
  2. 了解我是否正确分析内存泄漏

  3. 1:了解发生了什么:

    启动应用程序时显示的屏幕是调用负责更新片段UI本身的AsyncTask的片段(它被分配给第一个Action Bar选项卡)。

    一旦应用程序启动,我就开始连续旋转屏幕以查看内存会发生什么。下面的屏幕截图来自Android Studio内存分析器。

    第一次连接超时错误通常在首次运行垃圾收集器之后发生,因为我通过多次旋转设备来填满所有可用内存。

    此时发生了片段UI因AsyncTask 而更新失败(处理显然无响应连接时)。 Web服务器根本不会收到HTTP请求,即使我再次旋转屏幕 - 为了让Activity和Fragment重新启动 - 后续的AsyncTask也不起作用,也不会发出新的HTTP请求。

    当然我已经捕获了所有异常,并且在onPostExecute()的开头我不得不做if (arrayOfJSONobjs == null) { return; }以避免将null对象提供给后续片段UI&#39; s建筑方法。

    那么,您认为如何使连接像这样工作会发生什么?怎么说重启后我再也没有看到这个? 我已尝试禁用防病毒,防火墙和已检查,如果路由器或网络服务器对连续请求过多应用某种保护(我的设备连接到来自互联网的网络服务器,使用我的公共IP)。没有什么工作,除了重启。我唯一想到的是......可能 Android Studio中的一些错误,它在某个时间处于请求的中间?

    enter image description here

    2:我是否正确理解了内存分配和GC?

    查看代码,你认为在某些地方我可能会泄露上下文吗? 我在内存分析器截图中看到的是非泄漏应用程序的预期良好行为?我的意思是,我应该看到内存被填满,即使没有泄漏(前提是它会被垃圾收集)?

    我不知道如何更好地把这个放进去,但是当一切正常时,我希望看到这种图表吗?正如您所看到的,第一次,仅在内存完全填满时调用GC,但之后GC会更快地触发,此时仍有一些可用内存(我仍然在旋转设备)。这正常吗?

    <小时/> 尽管上面有错误(但仍然可能与它们有关,以防内存泄漏实际发生):我不确定是否必须将视图和上下文都传递给AsyncTask对象。我可以通过只有其中一个并从中推断另一个,以便尽可能地减少我传递的参考?
    清理: 关于TableLayout是否适合我尝试构建的布局的子问题已移至another question。< p>


    代码:

    MainActivity.java

    package com.mydom.demoapp;
    
    import android.os.Bundle;
    import android.support.v4.app.FragmentTransaction;
    import android.support.v4.app.NavUtils;
    import android.support.v4.view.ViewPager;
    import android.support.v7.app.ActionBar;
    import android.support.v7.app.ActionBarActivity;
    import android.util.Log;
    import android.view.Menu;
    import android.view.MenuItem;
    
    import com.mydom.demoapp.adapter.TabsPagerAdapter;
    
    public class MainActivity extends ActionBarActivity implements ActionBar.TabListener {
    
        private ViewPager viewPager;
        private TabsPagerAdapter mAdapter;
        private ActionBar actionBar;
    
        private String[] tabs = { "Music", "Movies", "News"};
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
    
            viewPager = (ViewPager) findViewById(R.id.pager);
            mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
            viewPager.setAdapter(mAdapter);
    
            actionBar = getSupportActionBar();
            actionBar.setDisplayHomeAsUpEnabled(false);
            actionBar.setHomeButtonEnabled(true);
            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    
            // Adding Tabs
            for (String tab_name : tabs) {
                actionBar.addTab(actionBar.newTab().setText(tab_name).setTabListener(this));
            }
    
            // on swiping the viewpager make respective tab selected
            viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    
                @Override
                public void onPageSelected(int position) {
                    actionBar.setSelectedNavigationItem(position);
                }
    
                @Override
                public void onPageScrolled(int arg0, float arg1, int arg2) {
                }
    
                @Override
                public void onPageScrollStateChanged(int arg0) {
                }
            });
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.menu_main, menu);
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle action bar item clicks here
            int id = item.getItemId();
    
            if (id == R.id.action_settings) {
                return true;
            }
    
            if (item.getItemId() == android.R.id.home) {
                NavUtils.navigateUpFromSameTask(this);
                return true;
            }
    
            return super.onOptionsItemSelected(item);
        }
    
        @Override
        public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
        }
    
        @Override
        public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
            // on tab selected show appropriate fragment
            viewPager.setCurrentItem(tab.getPosition());
        }
    
        @Override
        public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
        }
    
        @Override
        public void onStop(){
            super.onStop();
            Log.d("mytag", "MainActivity: onStop entered");
        }
    }
    

    <小时/> 的 TabsPageAdapter.java

    package com.mydom.demoapp.adapter;
    
    import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentManager;
    import android.support.v4.app.FragmentPagerAdapter;
    import com.mydom.demoapp.MusicFragment;
    import com.mydom.demoapp.MoviesFragment;
    import com.mydom.demoapp.NewsFragment;
    
    // This adapter provides fragment views to tabs.
    
    public class TabsPagerAdapter extends FragmentPagerAdapter{
    
        public TabsPagerAdapter(FragmentManager fm) {
            super(fm);
        }
    
        @Override
        public Fragment getItem(int index) {
    
            switch (index) {
                case 0:
                    return new MusicFragment();
                case 1:
                    return new MoviesFragment();
                case 2:
                    return new NewsFragment();
            }
            return null;
        }
    
        @Override
        public int getCount() {
            // get item count - equal to number of tabs
            return 3;
        }
    
    }
    

    MusicFragment.java (负责实例化和启动AsyncTask)

    package com.mydom.demoapp;
    
    import android.net.Uri;
    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    import com.mydom.demoapp.async_task.AsyncTaskRunner;
    
    
    public class MusicFragment extends Fragment {
    
        private static final String ARG_PARAM1 = "param1";
        private static final String ARG_PARAM2 = "param2";
    
        private String mParam1;
        private String mParam2;
    
        private OnFragmentInteractionListener mListener;
    
        public static MusicFragment newInstance(String param1, String param2) {
            MusicFragment fragment = new MusicFragment();
            Bundle args = new Bundle();
            args.putString(ARG_PARAM1, param1);
            args.putString(ARG_PARAM2, param2);
            fragment.setArguments(args);
            return fragment;
        }
    
    
        public MusicFragment() {
            // Required empty public constructor
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            if (getArguments() != null) {
                mParam1 = getArguments().getString(ARG_PARAM1);
                mParam2 = getArguments().getString(ARG_PARAM2);
            }
    
            Log.d("mytag", "MusicFragment: onCreate entered");
    
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    
            // Inflate the layout for this fragment
            Log.d("janfry", "MusicFragment: onCreateView entered");
            return inflater.inflate(R.layout.fragment_music, container, false);
        }
    
    
        @Override
        public void onViewCreated(View view, Bundle savedInstanceState) {
    
            super.onViewCreated(view, savedInstanceState);
    
            Log.d("mytag", "MusicFragment: onViewCreated entered");
    
            runner = new AsyncTaskRunner();
            runner.execute(this.getActivity(), view);               
            // I am passing it the context (by getting the activity) and the view so that it will know where to update the UI.
    
        }
    
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
        }
    
        // TODO: Rename method, update argument and hook method into UI event
        public void onButtonPressed(Uri uri) {
            if (mListener != null) {
                mListener.onFragmentInteraction(uri);
            }
        }
    
        public interface OnFragmentInteractionListener {
            // TODO: Update argument type and name
            public void onFragmentInteraction(Uri uri);
        }
    
        @Override
        public void onStop(){
            super.onStop();
            Log.d("mytag", "MusicFragment: onStop entered");
         }
    }
    

    AsyncTaskRunner.java

    package com.mydom.demoapp.async_task;
    
    import android.content.Context;
    import android.graphics.Color;
    import android.os.AsyncTask;
    import android.util.Log;
    import android.view.View;
    import android.widget.TableLayout;
    import android.widget.TableRow;
    import android.widget.TextView;
    import com.mydom.demoapp.R;
    import com.mydom.demoapp.Utils;
    import org.apache.http.HttpResponse;
    // import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.methods.HttpGet;
    // import org.apache.http.conn.ConnectTimeoutException;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.params.BasicHttpParams;
    import org.apache.http.params.HttpConnectionParams;
    import org.apache.http.params.HttpParams;
    import org.apache.http.util.EntityUtils;
    import org.json.JSONArray;
    // import org.json.JSONException;
    import org.json.JSONObject;
    
    // import java.io.IOException;
    // import java.net.SocketTimeoutException;
    // import java.util.concurrent.TimeoutException;
    
    
    public class AsyncTaskRunner extends AsyncTask<Object, String, JSONArray> {
    
        // These will be set by doInBackground() according to what the fragment passed to it
        // I am declaring them as instance variables because I'll need them in the onPostExecute method too, so to have a ref to the frag to update. 
        // By the way, can I infer one from the other someway?
    
        Context contextRef;
        View viewRef;
    
        @Override
        protected void onPreExecute(){
            Log.d("janfry", "AsyncTaskRunner: onPreExecute entered");
        }
    
        @Override
        protected JSONArray doInBackground(Object... params){
    
            Log.d("mytag", "AsyncTaskRunner: doInBackground entered");
    
            contextRef = (Context) params[0]; 
            viewRef = (View) params[1];
    
            HttpResponse response;
            String str = "";
            JSONArray arrayOfJSONObjects = null;
    
            final HttpParams httpParams = new BasicHttpParams();
            HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
            HttpClient myClient = new DefaultHttpClient(httpParams);
            HttpGet myConnection = new HttpGet("http://123.123.123.123:80/notes.json");
    
            try {
                response = myClient.execute(myConnection);
                str = EntityUtils.toString(response.getEntity(), "UTF-8");
            } catch (Throwable t) {
                t.printStackTrace();
            }
    
            try{
                arrayOfJSONObjects = new JSONArray(str);
            } catch ( Throwable t) { t.printStackTrace(); }
    
    
            try {
                Log.d("mytag", arrayOfJSONObjects.getString(0));
            } catch (Throwable t) {
                t.printStackTrace();
            }
            return arrayOfJSONObjects;
        }
    
    
        @Override
        protected void onProgressUpdate(String... notused){
    
        }
    
        @Override
        protected void onPostExecute(JSONArray arrayOfJSONobjs) {
    
    
            Log.d("mytag", "AsyncTaskRunner: onPostExecute entered");
    
            TableLayout tab_lay = (TableLayout) viewRef.findViewById(R.id.musicTableLayout);
            tab_lay.removeAllViews();
    
            TextView[] arrayOfTextViews;
            arrayOfTextViews = new TextView[arrayOfJSONobjs.length()];    
    
            for(int pos = 0; pos < arrayOfJSONobjs.length(); pos++) {             
                // and let's populate it with textviews...
                TextView textViewForObjName = new TextView(contextRef);
                try {
                    JSONObject oneJsonObj;   // will hold the parsed JSON for one obj
                    oneJsonObj = arrayOfJSONobjs.getJSONObject(pos);
                    textViewForObjName.setText(oneJsonObj.getString("name"));
    
                } catch (Throwable t) {
                    t.printStackTrace();
                }
    
                textViewForObjName.setHeight(Utils.dip(contextRef, 30));
                textViewForObjName.setBackgroundColor(Color.parseColor("#ABCABC"));
    
                // let's add the text_view we built to the Array
                arrayOfTextViews[pos] = textViewForObjName;  
    
    
            }    // we now have an array of textviews, that has not been added to the UI yet.
    
    
            // I want to populate the array with 3 textviews per row.
            // Is it a good idea to use this layout for this way of laying out content?
            // Would you have done that differently?
    
            TableRow table_row = new TableRow(contextRef);
            int col_counter = 0;
            for (TextView aTextView : arrayOfTextViews) {
    
                table_row.addView(aTextView);
                col_counter++;
    
                if (col_counter == 3) {
                    tab_lay.addView(table_row);
                    table_row = new TableRow(contextRef);
                    col_counter = 0;
                }
            }
    
        }
    
    
    }
    

1 个答案:

答案 0 :(得分:0)

@ corsair992建议(这只是一个猜测)“可能是你超出了Apache的DefaultHttpClient或其他地方允许的并发连接的最大限制。

我建议她/他将上述评论放在一个正确的答案中(连同他/她做的其他观察),但他/她说这只是一个猜测,而不是一个完整的答案。

仍然对我来说这是最合理的假设,我认为它应该得到充分的知名度和关注,所以我将其保存为自己的答案,以便可以进一步评论和扩展(请在我的问题上提出原始的@ corsair992评论) )。