我一直在寻找一些时间来描述Dalvik VM垃圾收集器架构的详细设计文档,但还没有发现太多。考虑到GC运行的性能影响,我真的希望更好地理解5个具体问题: 1.究竟是什么触发了Android中的GC?我见过的其他VM实现通常允许在GC收到运行信号之前将一定比例的系统内存分配给应用程序。然而,扫描以下LogCat似乎表明Dalvik GC至少部分运行 -
12-14 11:34:57.753: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 735 objects / 54272 bytes
in 90ms
12-14 11:34:57.893: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 256 objects / 12240 bytes
in 61ms
12-14 11:34:57.943: I/jPCT-AE(279): Loading Texture...
12-14 11:34:57.993: D/dalvikvm(279): GC_FOR_MALLOC freed 65 objects / 2840 bytes in
52ms
12-14 11:34:58.013: I/dalvikvm-heap(279): Grow heap (frag case) to 5.039MB for
1048592-byte allocation
12-14 11:34:58.073: D/dalvikvm(279): GC_FOR_MALLOC freed 1 objects / 40 bytes in 59ms
12-14 11:34:58.243: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 11 objects / 432 bytes in
55ms
12-14 11:34:58.283: I/jPCT-AE(279): Loading Texture...
12-14 11:34:58.333: D/dalvikvm(279): GC_FOR_MALLOC freed 10 objects / 416 bytes in 46ms
12-14 11:34:58.344: I/dalvikvm-heap(279): Grow heap (frag case) to 6.040MB for
1048592-byte allocation
12-14 11:34:58.423: D/dalvikvm(279): GC_FOR_MALLOC freed 2 objects / 80 bytes in 75ms
12-14 11:34:58.563: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 10 objects / 384 bytes in
47ms
12-14 11:34:58.603: I/jPCT-AE(279): Loading Texture...
12-14 11:34:58.653: D/dalvikvm(279): GC_FOR_MALLOC freed 11 objects / 464 bytes in 44ms
12-14 11:34:58.663: I/dalvikvm-heap(279): Grow heap (frag case) to 7.040MB for
1048592-byte allocation
12-14 11:34:58.743: D/dalvikvm(279): GC_FOR_MALLOC freed 2 objects / 80 bytes in 75ms
12-14 11:34:58.973: I/System.out(279): started document!
...
12-14 11:43:05.393: I/jPCT-AE(279): Memory usage before compacting: 5867 KB used out
of 6215 KB
12-14 11:43:05.453: D/dalvikvm(279): GC_EXPLICIT freed 2560 objects / 145712 bytes in
61ms
12-14 11:43:05.503: D/dalvikvm(279): GC_EXPLICIT freed 295 objects / 21448 bytes in
51ms
12-14 11:43:05.717: I/jPCT-AE(279): Memory usage after compacting: 5705 KB used out of
6215 KB
...
12-14 11:43:05.792: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 105 objects / 6152 bytes
in 56ms
12-14 11:43:05.855: D/dalvikvm(279): GC_FOR_MALLOC freed 3 objects / 80 bytes in 51ms
...
12-14 11:43:12.863: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 864 objects / 1099072
bytes in 70ms
12-14 11:43:13.053: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 45 objects / 1760 bytes
in 55ms
12-14 11:43:14.533: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 49 objects / 2376 bytes
in 58ms
12-14 11:43:14.933: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 34 objects / 1408 bytes
in 55ms
12-14 11:43:15.423: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 13 objects / 504 bytes in
58ms
12-14 11:43:15.953: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 13 objects / 520 bytes in
56ms
...
12-14 11:43:31.203: I/jPCT-AE(279): Visibility lists disposed!
12-14 11:43:31.203: I/jPCT-AE(279): All texture data unloaded from gpu!
12-14 11:43:31.203: I/jPCT-AE(279): Renderer disposed!
12-14 11:43:31.203: I/jPCT-AE(279): Static references cleared...
...
12-14 11:43:36.943: E/dalvikvm-heap(279): 2964320-byte external allocation too large
for this process.
12-14 11:43:36.953: E/GraphicsJNI(279): VM won't let us allocate 2964320 bytes
12-14 11:43:36.953: D/AndroidRuntime(279): Shutting down VM
12-14 11:43:36.953: W/dalvikvm(279): threadid=1: thread exiting with uncaught
exception (group=0x4001d800)
12-14 11:43:36.973: E/AndroidRuntime(279): FATAL EXCEPTION: main
12-14 11:43:36.973: E/AndroidRuntime(279): android.view.InflateException: Binary XML
file line #33: Error inflating class <unknown>
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.createView(LayoutInflater.java:513)
12-14 11:43:36.973: E/AndroidRuntime(279): at
com.android.internal.policy.impl.PhoneLayoutInflater.
onCreateView(PhoneLayoutInflater.java:56)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:563)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.rInflate(LayoutInflater.java:618)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.rInflate(LayoutInflater.java:621)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.inflate(LayoutInflater.java:407)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.inflate(LayoutInflater.java:320)
12-14 11:43:36.973: E/AndroidRuntime(279): at
com.ai.ultimap.views.Manual.onItemClick(Manual.java:467)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.widget.AdapterView.performItemClick(AdapterView.java:284)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.widget.AbsListView$PerformClick.run(AbsListView.java:1696)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.os.Handler.handleCallback(Handler.java:587)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.os.Handler.dispatchMessage(Handler.java:92)
12-14 11:43:36.973: E/AndroidRuntime(279): at android.os.Looper.loop(Looper.java:123)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.app.ActivityThread.main(ActivityThread.java:4627)
12-14 11:43:36.973: E/AndroidRuntime(279): at
java.lang.reflect.Method.invokeNative(Native Method)
12-14 11:43:36.973: E/AndroidRuntime(279): at
java.lang.reflect.Method.invoke(Method.java:521)
12-14 11:43:36.973: E/AndroidRuntime(279): at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
12-14 11:43:36.973: E/AndroidRuntime(279): at
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
12-14 11:43:36.973: E/AndroidRuntime(279): at dalvik.system.NativeStart.main(Native
Method)
12-14 11:43:36.973: E/AndroidRuntime(279): Caused by:
java.lang.reflect.InvocationTargetException
12-14 11:43:36.973: E/AndroidRuntime(279): at android.widget.ImageView.<init>
(ImageView.java:108)
12-14 11:43:36.973: E/AndroidRuntime(279): at
java.lang.reflect.Constructor.constructNative(Native Method)
12-14 11:43:36.973: E/AndroidRuntime(279): at
java.lang.reflect.Constructor.newInstance(Constructor.java:446)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.createView(LayoutInflater.java:500)
12-14 11:43:36.973: E/AndroidRuntime(279): ... 18 more
12-14 11:43:36.973: E/AndroidRuntime(279): Caused by: java.lang.OutOfMemoryError:
bitmap size exceeds VM budget
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.Bitmap.nativeCreate(Native Method)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.Bitmap.createBitmap(Bitmap.java:468)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.Bitmap.createBitmap(Bitmap.java:435)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.Bitmap.createScaledBitmap(Bitmap.java:340)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:488)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:462)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.content.res.Resources.loadDrawable(Resources.java:1709)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.content.res.TypedArray.getDrawable(TypedArray.java:601)
12-14 11:43:36.973: E/AndroidRuntime(279): at android.widget.ImageView.<init>
(ImageView.java:118)
12-14 11:43:36.973: E/AndroidRuntime(279): ... 22 more
12-14 11:43:38.763: I/Process(279): Sending signal. PID: 279 SIG: 9
正如你所看到的那样,在3 MB位图加载期间我特意遇到了一个内存错误...这对我来说没有意义,因为GC最近运行并且没有任何分配,因为它应该将VM带到3MB以内容量(256 MB)。是否只有一小部分256 MB的系统RAM在VM崩溃之前实际给予它?可能是Bitmap加载过程有自己的内存分配上限吗? 我知道对象池是在游戏循环期间尝试避免GC的一种好方法,但是如果不知道是什么触发了Dalvik GC,我们仍然对操作系统和Google对性能最佳实践的模糊讨论抱有极大的信心。
是否可以从代码中跟踪GC状态(例如“即将运行”,“正在运行”,“已完成运行”),以便可以围绕可用内存战略性地规划大量资源分配?我已经阅读过关于此事的帖子:Determine when the Android GC runs提供了一个有趣的潜在解决方案,但仍然依赖于“技巧”。我想知道在某个地方是否存在支持的API调用,可以依赖于生产代码(而不仅仅是调试)来跟踪垃圾收集器的精确状态。 System.gc()在某些情况下可能有用,如果可以检查GC状态;否则,因为它不能保证立即运行GC,其实用性会下降很多。
GC是否总是在系统范围内,或者可以分离线程(例如游戏的专用渲染线程)来避免由GC引起的潜在性能滞后问题?
鉴于以下假设情景: '我有一个成本(VM RAM预算)/ 2个字节来实例化的对象,我用一个引用立即实例化它。然后我将该引用置空,使该对象符合GC的条件,但当然还没有实际释放它的内存。然后我再次立即实例化该对象。 这会使VM崩溃还是某种方式操作系统会自动处理这种极端情况以避免崩溃VM?如果操作系统没有处理它,我会举几个例子来解释为什么我的问题#2有效;如果可以跟踪GC状态,则可以通过检查在加载之前是否已释放来自GC符合条件的对象的内存来处理源中的逻辑以处理大量对象分配问题(实际上更可能是比设计错误的类更大的资源)新的巨大对象实例,并在后台轮询GC时显示一个小的加载动画。这应该避免应用程序没有响应错误以及合法的内存不足错误...某种onGC()监听器将是理想的;可以在本机代码中实现GC侦听器而无需重新构建操作系统内核吗?
5.最后,一些源代码......对于性能高效的Android编程,我有正确的想法吗?
活动类:
package com.ai.ultimap;
//imports omitted...
public class UltiMapActivity extends Activity {
//Housekeeping
private String viewDriverID = "";
private static final int TUTORIAL = 7;
//visuals
private HomeView hv; //home view
private ConfigView cv; //config view
private MapView mv; //map view
private Manual man; //manual view
private int manCount = 0; //tracks the number of times the manual has been called
//with menu button, ignoring button presses unless value is zero
private PathCreator pcv; //path creator view
private MasterGL mgl; //the gl center
private String pending = "Coming soon...";
private PathCreator draw;
private Surfacer morlock;
// Used to handle pause and resume...
private static UltiMapActivity master;
//XML I/O considerations
private String fXML = "mypaths.xml";
private String sXML = "data was not saved properly...?";
private FileOutputStream fos;
private FileInputStream fis;
private FileWriter fw;
private FileReader fr;
private Date theDate = new Date();
private char[] buf = new char[1];
//Feedback stuffs
private FeedbackController feed;
//tracking you... :)
private WifiStalk stalk;
private long lat;
private long longitude;
//Testing
private DrawView dv;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("me","ultimap created!");
master = null;
mgl = new MasterGL(this); //revisit this later for versatility
man = new Manual(this);
feed = new FeedbackController(this);
stalk = new WifiStalk(this);
draw = new PathCreator(this);
hv = new HomeView(this,draw);
try {
BeanCounter bean = new BeanCounter(this);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (XmlPullParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
showDialog(TUTORIAL);
}
@Override
public boolean onKeyDown(int keyCode,KeyEvent e){
if (keyCode == 82){
if (viewDriverID.equals("hv")){
hv.removeHV();
}
else if (viewDriverID.equals("cv")){
cv.removeCV();
}
else if (viewDriverID.equals("mv")){
return true;
}
else if (viewDriverID.equals("pcv")){
return true;
}
if(man.getAddedState() == 0){
//Show the manual code...
System.out.println("View we're coming from: " + this.getVDID());
Log.e("me", "man.getaddedstate does equal 0, should be about to makeMan");
man.makeMan();
}
else if(man.getAddedState() == 2){
man.removeMan();
man.removeMan2();
man.setAddedState(1);
}
else if(man.getAddedState() == 1){
System.out.println("View we're coming from: " + this.getVDID());
man.addMan();
}
}
return true;
}
@Override
protected Dialog onCreateDialog(int id) {
//alerts ommitted for space
}
//Used to track the semantic context of what the Activity is displaying
//Getters/setters for external access ommitted
@Override
protected void onStart(){
super.onStart();
Log.d("me","ultimap started!");
}
@Override
protected void onPause() {
super.onPause();
Log.d("me","ultimap paused!");
if (mgl.getGLview() != null){
mgl.getGLview().onPause();
}
if (draw.getGLV() != null){
draw.getGLV().onPause();
}
}
@Override
protected void onResume() {
super.onResume();
Log.d("me","ultimap resumed!");
stalk.killListener();
if (mgl.getGLview() != null){
mgl.getGLview().onResume();
Log.d("me", "mgl.getGLview is NOT null on resume");
}
else if (mgl.getGLview() == null){
mgl.initGL();
mgl.getGLview().onResume();
Log.d("me", "mgl.getGLview is null on resume");
}
if (draw.getGLV() != null){
draw.getGLV().onResume();
Log.d("me", "draw.getGLV is NOT null on resume");
}
else if (draw.getGLV() == null && draw.getHGL() != null){
draw.pcvInit();
Log.d("me", "draw.getGLV is null on resume");
}
if (hv.getMV() != null && hv.getMV().getGLV() != null){
hv.getMV().getGLV().onResume();
Log.d("me", "map.getGLV is NOT null on resume");
}
else if (hv.getMV() != null && hv.getMV().getGLV() == null &&
hv.getMV().getHGL() != null){
hv.getMV().mvInit();
Log.d("me", "map.getGLV is null on resume");
}
}
@Override
protected void onStop() {
super.onStop();
//feed.getSP().release();
Log.d("me","ultimap stopped!");
}
@Override
protected void onRestart(){
super.onRestart();
Log.d("me","ultimap restarted!");
if (mgl != null){
mgl.initGL();
}
}
@Override
protected void onDestroy(){
super.onDestroy();
Log.d("me","ultimap destroyed!");
mgl.disposeTextures();
if (feed.getSP() != null && feed.getSID() != 0 && feed.getLoaded() ==
true){
feed.getSP().unload(feed.getSID());
feed.getSP().release();
}
}
}
教程视图管理器类:
/*
* This class defines an in-app manual which is callable/dismissable
* in a non-invasive way...
*
* http://www.codeproject.com/KB/android/ViewFlipper_Animation.aspx
*http://developer.android.com/reference/android/widget/
*ViewFlipper.html#ViewFlipper%28android.content.Context%29
* http://developer.android.com/resources/articles/avoiding-memory-leaks.html
*/
package com.ai.ultimap.views;
//imports ommitted
public class Manual extends View implements OnItemClickListener{
private UltiMapActivity hUMA;
private ListView lv1;
private ListAdapter la;
private LayoutInflater mInflater;
private Vector<RowData> data;
private TextView tv;
private RelativeLayout holderRL;
private View v;
private View v2;
private int addedState = 0; //tracks whether or not a view has been instantiated,
//and if so whether or not it is the currently visible view
private int addedState2 = 0;
//Grid View stuff
private GridView helpGrid;
//ViewFlipper stuff
private ViewFlipper vf;
private TextView tutTV;
private String mapTutString = "Map View Tutorial Part: ";
private String pcTutString = "Path Creator Tutorial Part: ";
private String tutType;
private TextView counterTV;
private int partCounter = 1;
private float oldTouchValue = 0.0f;
private boolean searchOk = true;
private ImageView floatingImage;
public Manual(UltiMapActivity hAct){
super(hAct);
hUMA = hAct;
holderRL = new RelativeLayout(hUMA);
v = new View(hUMA);
floatingImage = new ImageView(hUMA);
}
//Here we summon and populate the grid view
public void makeMan(){
if (addedState == 0){
Log.e("me", "in makeMan");
mInflater = (LayoutInflater)
hUMA.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
hUMA.addContentView(holderRL, new
LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT));
v = mInflater.inflate(R.layout.helpgrid, holderRL, false);
helpGrid = (GridView) v.findViewById(R.id.manGV);
helpGrid.setAdapter(new ImageAdapter(hUMA));
hUMA.addContentView(v, new
LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT));
helpGrid.setOnItemClickListener(this);
addedState = 2;
}
}
public void addMan(){
if (v != null && addedState == 1){
v.setVisibility(VISIBLE);
v.bringToFront();
addedState = 2;
}
}
public void addMan2(){
if (v2 != null && addedState2 == 1){
v2.setVisibility(VISIBLE);
v2.bringToFront();
addedState2 = 2;
}
}
public void removeMan(){
if (v != null && addedState == 2){
v.setVisibility(GONE);
addedState = 1;
String s = hUMA.getVDID();
if (s.equals("hv")){
hUMA.getHome().addHV();
Log.d("me", "add hjomeview called from anual");
Log.d("me", "hv addedstate : " +
hUMA.getHome().getAddedState());
}
else if (s.equals("cv")){
hUMA.getConfig().addCV();
}
else if (s.equals("mv")){
hUMA.getHome().getMV().mvInit();
}
else if (s.equals("pcv")){
hUMA.getDraw().pcvInit();
}
}
}
public void removeMan2(){
if (v2 != null && addedState2 == 2){
v2.setVisibility(GONE);
addedState2 = 1;
String s = hUMA.getVDID();
if (s.equals("hv")){
hUMA.getHome().addHV();
Log.d("me", "add hjomeview called from manual");
Log.d("me", "hv addedstate : " +
hUMA.getHome().getAddedState());
}
else if (s.equals("cv")){
hUMA.getConfig().addCV();
}
else if (s.equals("mv")){
hUMA.getHome().getMV().mvInit();
}
else if (s.equals("pcv")){
hUMA.getDraw().pcvInit();
}
}
}
//addedstate getters and setters ommitted for space
@Override
public boolean onTouchEvent(MotionEvent touchevent) {
switch (touchevent.getAction())
{
case MotionEvent.ACTION_DOWN:
{
System.out.println("received a touch down at " + touchevent.getX()
+ "," + touchevent.getY());
oldTouchValue = touchevent.getX();
if(this.searchOk==false) return false;
float currentX = touchevent.getX();
if (currentX > (vf.getWidth()/2))
{
vf.setInAnimation(AnimationHelper.inFromRightAnimation());
vf.setOutAnimation(AnimationHelper.outToLeftAnimation());
vf.showNext();
if (partCounter <= 3 && partCounter >= 1){
partCounter++;
}
else if (partCounter == 4){
partCounter = 1;
}
else{
Log.e("me", "partCounter got past 4...");
}
if(tutType.equals("map")){
counterTV.setText(mapTutString + partCounter);
}
else if(tutType.equals("pc")){
counterTV.setText(pcTutString + partCounter);
}
else{
Log.e("me","not getting valid tutType string");
}
}
if (currentX <= (vf.getWidth()/2))
{
vf.setInAnimation(AnimationHelper.inFromLeftAnimation());
vf.setOutAnimation(AnimationHelper.outToRightAnimation());
vf.showPrevious();
if (partCounter >= 2 && partCounter <= 4){
partCounter--;
}
else if (partCounter == 1){
partCounter = 4;
}
else{
Log.e("me", "partCounter got below 1...");
}
if(tutType.equals("map")){
counterTV.setText(mapTutString + partCounter);
}
else if(tutType.equals("pc")){
counterTV.setText(pcTutString + partCounter);
}
else{
Log.e("me","not getting valid tutType string");
}
}
break;
}
case MotionEvent.ACTION_UP:
{
//nothing to do here
}
}
return false;
}
public void setUserText(String str){
tv.setText(str);
}
private class CustomTV extends TextView{
private String content = "";
public CustomTV(Context c, String str){
super(c);
content = str;
this.setText(content);
}
}
/**
* Data type used for custom adapter. Single item of the adapter.
*/
private class RowData {
protected String mItem;
protected String mDescription;
RowData(String item, String description){
mItem = item;
mDescription = description;
}
@Override
public String toString() {
return mItem + " " + mDescription;
}
}
private class CustomAdapter extends ArrayAdapter<RowData> {
public CustomAdapter(Context context, int resource,
int textViewResourceId, List<RowData> objects) {
super(context, resource, textViewResourceId, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
//widgets displayed by each item in your list
TextView item = null;
TextView description = null;
//data from your adapter
RowData rowData= getItem(position);
//we want to reuse already constructed row views...
if(null == convertView){
convertView = mInflater.inflate(R.layout.custom_row, null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
holder = (ViewHolder) convertView.getTag();
item = holder.getItem();
item.setText(rowData.mItem);
description = holder.getDescription();
description.setText(rowData.mDescription);
return convertView;
}
}
/**
* Wrapper for row data.
*
*/
private class ViewHolder {
private View mRow;
private TextView description = null;
private TextView item = null;
public ViewHolder(View row) {
mRow = row;
}
public TextView getDescription() {
if(null == description){
description = (TextView) mRow.findViewById(R.id.cbox);
}
return description;
}
public TextView getItem() {
if(null == item){
item = (TextView) mRow.findViewById(R.id.cbox2);
}
return item;
}
}
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long id) {
v.setVisibility(GONE);
if (addedState2 == 0){
hUMA.addContentView(this,DefineLayoutParams.getParams(DefineLayoutParams.getMM()));
//this is why the onTouch only starts lsitening at this point
if (position == 0){
v2 = mInflater.inflate(R.layout.flipper, holderRL, false);
vf = (ViewFlipper) v2.findViewById(R.id.manFlipperVF);
tutTV = (TextView) v2.findViewById(R.id.manDescriptionTV);
counterTV = (TextView) v2.findViewById(R.id.mapviewtutCounterTV);
tutTV.setText("Map View Instructions: ...");
counterTV.setText(mapTutString + partCounter);
tutType = "map";
}
else if (position == 1){
v2 = mInflater.inflate(R.layout.flipperpc, holderRL, false);
vf = (ViewFlipper) v2.findViewById(R.id.manFlipperpcVF);
tutTV = (TextView) v2.findViewById(R.id.manDescriptionpcTV);
counterTV = (TextView) v2.findViewById(R.id.manFlipperCounterpcTV);
tutTV.setText("Path Creator Tutorial:...");
counterTV.setText(pcTutString + partCounter);
tutType = "pc";
}
addedState2 = 2;
hUMA.addContentView(v2, DefineLayoutParams.getParams(DefineLayoutParams.getWW()));
}
else if(addedState2 == 1){
v2.setVisibility(VISIBLE);
addedState2 = 2;
}
}
public String getTutType(){
return tutType;
}
}
教程查看Flipper XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ScrollView
android:id="@+id/manDerscriptionSV"
android:layout_width="match_parent"
android:layout_height="200px"
>
<TextView
android:id="@+id/manDescriptionTV"
android:text="Coming Soon..."
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</ScrollView>
<TextView
android:id="@+id/mapviewtutCounterTV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Map View Tutorial Part: "
android:gravity="center"
android:layout_below="@id/manDerscriptionSV"
/>
<ViewFlipper
android:id="@+id/manFlipperVF"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/mapviewtutCounterTV"
>
<ImageView
android:id="@+id/mapviewtut1"
android:src="@drawable/mapviewtutflipper1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<ImageView
android:id="@+id/mapviewtut2"
android:src="@drawable/mapviewtutflipper2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<ImageView
android:id="@+id/mapviewtut3"
android:src="@drawable/mapviewtutflipper3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<ImageView
android:id="@+id/mapviewtut4"
android:src="@drawable/mapviewtutflipper4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</ViewFlipper>
</RelativeLayout>
感谢, CCJ
答案 0 :(得分:23)
- 究竟是什么触发了Android中的GC?
醇>
这是SDK开发人员不应该担心的内部实现细节。
我见过的其他VM实现通常允许在GC收到要运行的信号之前将一定比例的系统内存分配给应用程序。
我会接受你的话。 Java不会这样做。 JVM并不关心存在多少系统内存 - 它只关心它自己的VM的潜在堆大小(例如,-Xmx
)。
然而,扫描以下LogCat似乎显示Dalvik GC至少部分运行
正确。特别是在较新版本的Android上,GC在其自己的线程中并发运行,而不是之前采用的世界各地的方法。
这对我来说没有意义,因为GC最近运行并且没有任何分配,因为应该将VM带入3MB容量(256 MB)。
您的VM不可能拥有256MB的堆空间。根据您的设备,它可能低至16MB。
此外,Android没有压缩GC算法,因此即使你可能有超过3MB的空间,你也可能没有连续的3MB块。
这就是recycle()
Bitmap
个inBitmap
个对象或尝试重用它们的重要原因(例如,BitmapOptions
的{{1}},在API级别11中添加。
此外,您可以使用DDMS创建堆转储和MAT来检查它,以更准确地确定您的内存在哪里以及谁在抓住什么。这在Android 3.0+上效果更好,因为MAT可以在这些版本中更准确地报告Bitmap
内存。
只有一小部分256 MB的系统RAM在VM崩溃之前实际上给了它吗?
是。它被称为堆。 Android设备具有堆大小限制。通常,它在16-48MB范围内,具体取决于Android操作系统版本和屏幕分辨率。
可能是Bitmap加载过程有自己的内存分配上限吗?
不,它使用相同的堆大小预算。从Android 3.0开始,它确实将内存从与Dalvik对象的其余部分相同的堆中加载 - 以前,它使用堆外部的系统RAM块,但是空间是根据堆的大小预算计算的。 / p>
但我们不知道是什么触发了Dalvik GC,我们仍然对操作系统和Google对性能最佳实践的模糊讨论抱有极大的信心
正如他们所说,生活还在继续。
是否可以从代码中跟踪GC状态(例如“即将运行”,“正在运行”,“已完成运行”),以便可以围绕可用内存战略性地规划大量资源分配? ...我想知道某个地方是否存在支持的API调用,可以依赖于生产代码(而不仅仅是调试)来跟踪垃圾收集器的精确状态。
没有
GC是否总是在系统范围内,或者可以分离线程(例如游戏的专用渲染线程)来避免由GC引起的潜在性能滞后问题?
对于任何VM,GC都不是“系统范围的”。 GC始终位于VM中。
在较新版本的Android上,GC是并发的,因此在正常情况下不会严重阻止任何线程。在旧版Android上,GC是世界各地的,会影响所有线程。对于Android 3.0而言,这种变化肯定已经到位 - 我的内存对于并发GC是否已经适用于Android 2.3而言仍然很模糊。您可能希望观看2011年关于Android内存管理的Google I | O演示文稿。
这会导致虚拟机崩溃,还是某种方式操作系统会自动处理这种极端情况以避免崩溃虚拟机?
Android应该在提升OutOfMemoryException
之前立即强制执行GC。根据我之前的paragaraph,这种情况不符合“正常情况”。