我是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:了解发生了什么:
启动应用程序时显示的屏幕是调用负责更新片段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中的一些错误,它在某个时间处于请求的中间?
2:我是否正确理解了内存分配和GC?
查看代码,你认为在某些地方我可能会泄露上下文吗? 我在内存分析器截图中看到的是非泄漏应用程序的预期良好行为?我的意思是,我应该看到内存被填满,即使没有泄漏(前提是它会被垃圾收集)?
我不知道如何更好地把这个放进去,但是当一切正常时,我希望看到这种图表吗?正如您所看到的,第一次,仅在内存完全填满时调用GC,但之后GC会更快地触发,此时仍有一些可用内存(我仍然在旋转设备)。这正常吗?
<小时/> 尽管上面有错误(但仍然可能与它们有关,以防内存泄漏实际发生):我不确定是否必须将视图和上下文都传递给AsyncTask对象。我可以通过只有其中一个并从中推断另一个,以便尽可能地减少我传递的参考?
代码:
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;
}
}
}
}
答案 0 :(得分:0)
@ corsair992建议(这只是一个猜测)“可能是你超出了Apache的DefaultHttpClient或其他地方允许的并发连接的最大限制。”
我建议她/他将上述评论放在一个正确的答案中(连同他/她做的其他观察),但他/她说这只是一个猜测,而不是一个完整的答案。
仍然对我来说这是最合理的假设,我认为它应该得到充分的知名度和关注,所以我将其保存为自己的答案,以便可以进一步评论和扩展(请在我的问题上提出原始的@ corsair992评论) )。