本文原创http://blog.youkuaiyun.com/yanbin1079415046,转载请注明出处。
最近做项目中遇到不少的问题,每每总是在解决之后才恍然大悟。相信每位朋友在实际开发中总会遇到这样或者那样的问题,但是其实有些问题总要我们稍微留意就可以避免。这篇文章用来记录在开发过程中遇到的一些小问题,或是粗心,或是技术不够引起,只为在以后的开发中多多注意。希望有兴趣的朋友也把自己的小问题贴在回复或者私信发给我,作为程序员,这些小问题都是我们宝贵财富,希望有意愿的朋友不吝分享自己的经验,以帮助更多的人。
一:小问题篇
1、动态loading框不会转动,只显示一张静态的图片。 (ps:这个问题比较傻,当初是因为电视项目,无法连上电视,因此一时半会没看到错误信息,如果可以联机调试,这个问题一debug就可以出来。但是这个知识点还是值得注意的。)
问题描述:这个问题本来可以用两张图很形象的说明,但是暂时还不会弄个动态的图,因此就把代码简单的贴一下,配合代码说一下需要实现的功能从而引出出现的问题。这个问题要实现的功能是在请求网络数据的时候,在activity上出现一个一直在转的loading框(一张静态的图片,开一个动画让其转动),当数据加载完成后loading框消失,数据显示。但是本人在实际操作的过程中,出现了如下的问题:loading框一直就是静态的图片,也就是说图片不会转。。。
功能性代码如下:
主页面xml布局文件:main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:layout_gravity="center"
android:background="@android:color/white"
tools:context=".TestActivity" >
<include
android:layout_marginTop="50dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
layout="@layout/loading"
/>
<TextView
android:layout_marginTop="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="测试loading框有没有动"
android:textColor="@android:color/black"
/>
</LinearLayout>
include中的loading.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/use_imagedown_loading"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal"
>
<ImageView android:id="@+id/loading_pb"
android:layout_height="90dp" android:layout_width="90dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:enabled="false"
android:background="@drawable/loading"
/>
</RelativeLayout>
测试用TestActivity.java:
import android.os.Bundle;
public class TestActivity extends BaseActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
BaseActivity.java: BaseActivity很简单,就是初始化名为loading_pb的loading框,让其转动。动画文件在BaseActivity的下面。
import android.app.Activity;
import android.os.Bundle;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
public class BaseActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
initView();
super.onCreate(savedInstanceState);
}
/**
* 初始化loading框
*/
private void initView() {
if (findViewById(R.id.loading_pb) != null)
{
ImageView view = (ImageView) findViewById(R.id.loading_pb);
Animation animation = AnimationUtils.loadAnimation(this,R.anim.loading_anim);//开始一个动画文件
animation.setRepeatCount(Animation.INFINITE);//立即重复动画
view.startAnimation(animation);
}
}
}
动画文件loading_anim.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/linear_interpolator"
android:fromDegrees="0" android:toDegrees="+720" android:duration="2000"
android:pivotX="50%" android:pivotY="50%" android:repeatCount="infinite" />
</set>
但是我按照上面的代码来跑程序,loading框根本不会转动。看了一下代码,貌似没什么问题。想偷懒的同学可以直接下这个源代码观察一下效果即可。
工程下载地址:
http://download.youkuaiyun.com/detail/yanbin1079415046/4548638
下面整理一下上面我们做的事。在TestActivity的mainactivity中include进去了一个 loading框,它本来只是一张静态的图片。但是由于我们在BaseActivity的initView方法中获得了子类的布局文件中的这个loading_pb,并且给它加了一个动画,所以在TestActivity中它应该是可以转动的啊。逻辑是没错的。但是我们的loading框确实没出来。前面说过是电视项目,当初没有联机调,所以出了这个问题。后来仔细看看了,打了几个toast(当初的机顶盒啊,害死人了,只有靠U盘拷来拷去的调),发现问题是这样产生的。
问题产生原因:在TestActivity中我们把setContentView放在了调用父类onCreate方法之前,也就是说,BaseActivity 的initView中那个if条件过不去。所以loading框就不会动了。这个问题的迷惑在于,loading图片出来了,但是不会动。所以。。。
问题解决方法:将setContentView(R.layout.main);放在super.onCreate(savedInstanceState);之前即可。
2、 工程可以正常编译,但是运行时报:java.lang.NoClassDefFoundError。
解决办法: 这里说的是能正常编译。如果不能正常编译的话,请检查一下相应的类是否正确导入。我们这里的情况,检查报错的信息,在jar文件中这个类是可以找到的。假设我们放在了src的libs目录底下,并且已经将其Add To Build Path了。此时我们只要将libs文件夹Use as Source Folder即可。具体操作步骤是:libs(具体与你放jar包的文件夹名字相同)-->右键-->build-->Use as Source Folder。
3、关于OnActivityResult()方法不会调用的问题。
①:startActivityForResult中的request必须大于0。
②:跳转到的那个Activity必须有setResult方法,否则不会被调用。
③:跳转到的Activity的lanuchmode设置成了SingleTask,也就是这两个Activity不在一个任务栈中了,所以无法调用。
曾经在一个项目中调用了360相机来拍照,结果拍完照以后,onActivityResult()根本不会被调用。后来使用如下的代码解决了这个问题,即:在start照相机程序的时候给其附带一个Extra,从而将相片输出临时存放在一个Uri中,然后从该Uri中拿出数据即可。(image可以,video也当然就可以这样做)。具体代码如下:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//打开照相程序
/**
* MediaStore.EXTRA_OUTPUT文档说明:
* The name of the Intent-extra used to indicate a content resolver Uri
* to be used to store the requested image or video.
*/
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(imgpath)));//将图片或者视频文件存在该uri下。
startActivityForResult(intent, TAKE_PICTURE);//TAKE_PICTURE为自定的常量
4、每次更新drawable下的图片文件都会增加一个文件:thumbs.db。
解决方法:该文件时缓存的缩略图文件。只需在电脑的 文件夹选项--查看中设置不缓存缩略图即可。
5、关于Bitmap.recycle()方法的作用
一个图片加载到内存里,其实是有两部分数据组成,一部分是图片的相关描述信息,另一部分就是最重要的像素信息(这部分是有byte数组组成的),android系统为了提高对图片的处理效率,对于图片的处理都是调用了底层的功能(由C语言实现的),也就是说一个图片加载到内存里后是使用两部分的内存区域,简单的说:一部分是java可用的内存区,一部分是c可用的内存区,这两个内存区域是不能相互直接使用的,
这个bitmap对象是有java分配的,当然不用的时候系统会自动回收了,可是那个对应的C可用的内存区域jvm是不能直接回收的,这个只能调用底层的功能释放。所以你要调用recycle方法来释放那一部分内存。
原文地址:http://blog.sina.com.cn/s/blog_7d35fa1a01016thb.html
6、接入人人网时,一开始用的很好,突然某一天出现如下的问题:
无效的测试用户,测试用户的好友总数必须小于或等于10
这个问题一般是在人人发布了专门的移动开发者平台之后出现的。咨询了一下人人的工程师,出现问题的原因如下:
开放平台和移动开发者平台申请app的账号串用了。据人人工程师的说法,人人网开放平台和人人移动开发者平台之间账号是独立的。因为他们提供了两套api,但是可以实现同一功能。假如你在人人移动开发者平台上申请了一个app,并且该app已经通过审核上线了。在这个app里你使用的是移动开发者平台的那个api来实现相应的功能,假如某一天你的app改版了,并且使用人人开放平台的api来实现相应的功能,但是你的app的key和secret还是用的移动开发者平台上的那个app的key和secret,此时就会出现这样的问题了。解决办法是使用一致的api或者重新申请一下app吧。
6、客户端接入开心网的一些问题
客户端接入开心网,在开心网加载授权框的dialog还未消失的时候,点击取消,不让dialog继续加载,退出开心网授权的页面。一段时间之后,我们的应用程序莫名其妙的崩掉。检查异常日志,发现时progressDialog未关闭所致。因为在开心网的授权页面中,授权dialog里面嵌套一个ProgressDialog。而在外面的dialog执行dismiss的时候,未对ProgressDialog做相应的处理,于是出现了异常。
解决办法是在KaixinDialog.java中重写dismiss方法,代码如下:
@Override
public void dismiss(){
if(mProgress != null){
mProgress.dismiss();
}
super.dismiss();
}
二:小技巧篇
1、自定义的RadioButton,使图片和文字是紧挨着的。
当我们自定义了RadioButton之后,如果不进行设置,RadioButton的图片和文字会自由的排列,我们只需要进行如下的设置即可让图片和文字挨在一起,设置单选框的background为null,然后图片使用button属性来设置。xml布局文件示例如下:
<RadioButton android:id="@+id/rb1" android:text="自定义单选框" android:textColor="#000000"
android:layout_weight="1" android:layout_width="wrap_content"
android:background="@null"
android:gravity="center"
android:button="@drawable/radio_button" android:paddingLeft="23dp"
/>
2、去除ExpandableListView分组Group左边的指示图标:
ExpandableListView..setGroupIndicator(null);
3、第三方客户端(新浪微博,腾讯微博,人人网,开心网)取消授权:
第三方客户端的授权比较简单,参看各大开放平台给出的demo就可以。但如若要取消授权应该如何做。下面就给出相应的解决办法,一旦可以取消授权,登出操作自然就不难了吧。
新浪微博:在Weibo类中有一个dialog(...)方法,加一个强制登陆的参数就可以实现取消授权的效果了。
public void dialog(Context context, WeiboParameters parameters,
final WeiboDialogListener listener) {
parameters.add("forcelogin", "true");//强制登陆
parameters.add("client_id", APP_KEY);
parameters.add("response_type", "token");
parameters.add("redirect_uri", mRedirectUrl);
parameters.add("display", "mobile");
//....
}
腾讯微博:不需要做任何的处理。
人人网:如果你使用的移动开发者平台,调用RMConnectCenter.logout()即可。如果你使用的第三方平台,调用Renren.logout()即可。
开心网:Kaixin类的authorize(...)方法下有一个判断session是否有效的方法,将这部分代码注释并且调用Kaixin.clearStorage(Context context).即可,需要注释部分的如下:
public void authorize(final Context context, String[] permissions,
final KaixinAuthListener listener) {
// if (this.isSessionValid()) {
// listener.onAuthComplete(new Bundle());
// return;
// }
this.authorize(context, permissions, listener, KX_AUTHORIZE_CALLBACK_URL, "token");
}
三:代码片段篇
1、返回Manifest文件中的版本号和版本名:
Manifest文件中的manifest标签下的内容一般如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="yanbin.insertWeibo"
android:versionCode="1"
android:versionName="1.0" >
得到app版本号:
public static String getLocalVerCode(Context mContext){
int versionCode = 0;
PackageManager manager = mContext.getPackageManager();
try {
PackageInfo info = manager.getPackageInfo(mContext.getPackageName(), 0);
versionCode = info.versionCode; //版本号
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return String.valueOf(versionCode);
}
得到app版本名:
public static String getVersionName(Context mContext){
String str = "";
PackageManager manager = mContext.getPackageManager();
try {
PackageInfo info = manager.getPackageInfo(mContext.getPackageName(), 0);
str = info.versionName; //版本名
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return str;
}
2、addView(View child, int index, LayoutParams params)中index的含义
ViewGroup.java中对于addView(...)有很多的重载方法,其中有一个如下:
public void addView(View child, int index, LayoutParams params) {
if (DBG) {
System.out.println(this + " addView");
}
// addViewInner() will call child.requestLayout() when setting the new LayoutParams
// therefore, we call requestLayout() on ourselves before, so that the child's request
// will be blocked at our level
requestLayout();
invalidate();
addViewInner(child, index, params, false);
}
说一下其中index的含义,它表示要将当前的这个child子View添加到子View数组中的位置。如果你的值大于0,将插入到index位置,如果小于0,将插入到子view数组的末尾。其实就是调用了System.java类的System.arraycopy(...)这个静态方法来实现的。看一个System.arraycopy(...)的小例子:
public class ArrayCopyTest {
public static void main(String[] args) {
int index = 0;
Integer arr[] = new Integer[]{1,2,3,4,5,6};
Integer arrCopy[] = new Integer[arr.length + 1];
System.arraycopy(arr, index, arrCopy, index + 1, arr.length);
}
}
在Launcher程序中使用了这个addView(...)方法来实现往CellLayout中添加桌面元素。且元素默认是添加在末尾的。