我是一名新的Android程序员。我的第一个主要任务是创建一个Http Post请求,该请求在片段中的AsyncTask中运行。
我花了一百多个小时来学习AsyncTask的细节及其许多缺陷和怪癖。我已阅读了数百页,执行了数百次Google搜索并阅读了数万个单词以尝试解决此问题。我已经检查了每一个。单。小。方面。我的代码,删除和替换它的小块,通常逐行,并调试,试图弄清楚这里发生了什么。我所发现的只是对Android及其背后的方法的厌恶。
我创建了一个活动。让我们称之为“登录”。然后我创建一个片段。我们称之为“LoginTaskFragment”。当用户点击按钮时,片段运行AsyncTask,AsyncTask执行对url的请求。如果响应需要很长时间(我在服务器端的脚本中添加一个睡眠来模拟这个),并且用户反复旋转屏幕,在某些时候,应用程序将崩溃,返回Out of Memory错误
如果我在无UI片段或UI片段中运行任务,就会发生这种情况。即使我调用setRetainInstance(true)也会发生这种情况。我已经检查过,并且我100%肯定AsyncTask不会在屏幕旋转时重新运行。现在,如果我完全删除Http Post请求,我就没有问题(或者内存泄漏太小而无法产生效果,即使经过一百多次轮换,我已经尝试过)。即使我在onStop中调用asyncTask.cancel(false)...或true ...,问题仍然存在。使用在许多教程中经常与AsyncTasks一起提到的WeakReference技巧没有任何区别。
我还在活动中使用savedInstanceState Bundle来保存活动本身的某些相关数据,我不完全清楚这是否会影响片段的asynctask生存期。但我的理解不是。
老实说,我觉得这很荒谬,不应该成为一个问题。完全没有。由于Android在屏幕旋转时会愉快地破坏你的对象,所以它也应该对GC负责,但当然,我们都知道,事实并非如此。这似乎是一个错综复杂,乐队主义的笑话。
以下是我在活动中初始化片段的一些代码:
LoginTaskFragment loginFieldsTask = new LoginTaskFragment();
getSupportFragmentManager().beginTransaction().add(loginFieldsTask,"loginFieldsTask").commit();
这是相关的片段代码。如果我删除所有内容,只是离开这个,我就会遇到问题,无论我的代码中是否还有其他内容:
...
protected JsonObject doInBackground(Void... params) {
String postUrl="<some nice url here>";
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair("username", "un"));
nameValuePairs.add(new BasicNameValuePair("password", "pw"));
HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, 10000);
HttpConnectionParams.setSoTimeout(httpParameters, 30000);
DefaultHttpClient httpClient = new DefaultHttpClient(httpParameters);
HttpPost httpPost = new HttpPost(postUrl);
try {
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpClient.execute(httpPost);
httpClient.getConnectionManager().shutdown();
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
String json = reader.readLine();
JsonElement jElement = new JsonParser().parse(json);
JsonObject jObject = jElement.getAsJsonObject();
return jObject;
} catch (ClientProtocolException e) {
httpClient.getConnectionManager().shutdown();
} catch (IOException e) {
httpClient.getConnectionManager().shutdown();
} catch (Exception e) {
httpClient.getConnectionManager().shutdown();
}
return null;
}
我知道如果我将套接字超时设置为较低的数字,它似乎会杀死该线程,我可以旋转到我心中的内容。但是我想在上面的代码中将超时保留在30秒,以避免出现较旧的电话网络连接问题。
我的理解是,由于线程在一个片段中,所以当屏幕旋转时它不应该被重新播放。这似乎是真的。 onCreate绝对不会被轮换。
当然,这是所有事情都依赖的界限:
HttpResponse response = httpClient.execute(httpPost);
我简直不敢相信没有人遇到过这个问题,但是经过一百多个小时的搜索并将我的头撞在墙上,我已经受够了。我需要一个比我更好的人来告诉我发生了什么。
编辑:添加android:configChanges =“orientation | keyboardHidden | screenSize”不是一个好主意,并且非常不鼓励将此用作不良做法。请参阅[在此处理配置更改]。事实上,这是首先使用无UI片段的重点。在内部任务完成之前,不会销毁AsyncTask片段。因此,尽管潜在的活动被一遍又一遍地重建,但它从未被重新创建。
答案 0 :(得分:0)
您可以使用
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
在您的片段类中限制旋转,以便在旋转发生时不会重新执行代码。
答案 1 :(得分:0)
如果你不想在每次轮换(配置更改)中重新创建异步任务,那么在清单文件中
机器人:configChanges = “取向| keyboardHidden |屏幕尺寸”
<activity
android:name="com.app.MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize" >
OR
使用dinamic片段时,必须使用帧布局,并将片段放入其中。
<com.myproyect.TopMenu
android:id="@+id/menu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<!-- this was the problem
<fragment
android:id="@+id/fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.myproyect.HomeFragment"
android:layout_below="@id/menu"
/> -->
<FrameLayout android:id="@+id/fragment"
android:layout_width="match_parent" android:layout_height="match_parent"
android:layout_below="@id/menu"/>
<LinearLayout
...
>
</LinearLayout>
</RelativeLayout>
并在FrameLayout中实例化片段。
fragmentTransaction.replace(R.id.fragment, f);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();