一个安卓开发过程工具下载过程遇到的问题?

1579人阅读
Android(25)
长期补充,我只是大自然的搬运工=。=
1.获取手机的宽度和高度
WindowManager wm = this.getWindowManager();
int width = wm.getDefaultDisplay().getWidth();
int height = wm.getDefaultDisplay().getHeight();
2.popupWindow布局居中显示
popup.setHeight(LayoutParams.WRAP_CONTENT);
popup.setWidth(width*3/4); //为手机的宽度(上面的width)
popup.showAtLocation(listView, Gravity.CENTER,0,0);//s
3.判断手机是否安装某第三方app
& &项目中遇到,需要判断支付宝是否安装,弹出提示。像判断微信是否安装,自己的jar包中会有isWXAppInstalled的方法。但是支付宝貌似没找到
& 想要判断是否安装第三方app,就必须知道包名,包名的话,只能去反编译了。从AndroidManifest 去找到对应的package属性。
& &对了,突然想到,开发中,如果突然想改项目的包名,在Android Tools 中 rename Application Package就可以了
& 好了,言归正传,有了包名,只要遍历这个手机中所有的应用的包名就可以了,网上找了一个可用的方法,我觉得写的还是挺详细的
* 检查手机上是否安装了指定的软件
* @param context
* @param packageName:应用包名 如 com.example.test
private boolean isAvilible(Context context, String packageName){
//获取packagemanager
final PackageManager packageManager = context.getPackageManager();
//获取所有已安装程序的包信息
List&PackageInfo& packageInfos = packageManager.getInstalledPackages(0);
//用于存储所有已安装程序的包名
List&String& packageNames = new ArrayList&String&();
//从pinfo中将包名字逐一取出,压入pName list中
if(packageInfos != null){
for(int i = 0; i & packageInfos.size(); i++){
String packName = packageInfos.get(i).packageN
packageNames.add(packName);
//判断packageNames中是否有目标程序的包名,有TRUE,没有FALSE
return packageNames.contains(packageName);
& &4.关于拍照的回调返回的数据,在onActivityResult中,从返回的Intent中,获得的Uri uri = data.getData();uri在4.3----&4.4系统的时候产生了不同
& 在4.4一下,可以直接使用,但是在4.4以后,返回的是content://。。。。,会出现无法加载此图片。
private void handleData(Intent data) {
Uri uri = data.getData();
// 4.4以后新特征
if (android.os.Build.VERSION.SDK_INT &= android.os.Build.VERSION_CODES.KITKAT) {
String url = getPath(PersonalDataActivity.this, uri);
uri = Uri.fromFile(new File(url));
handleData(uri);
// 4.4以下
handleData(uri);
}通过判断当然系统的版本进行不同的处理,主要是getPath这个方法。网上也有这个方法,测试可用
// 4.4新特征
public String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT &= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(&:&);
final String type = split[0];
if (&primary&.equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + &/&
+ split[1];
// TODO handle non-primary volumes
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse(&content://downloads/public_downloads&),
Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(&:&);
final String type = split[0];
Uri contentUri =
if (&image&.equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if (&video&.equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if (&audio&.equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
final String selection = &_id=?&;
final String[] selectionArgs = new String[] { split[1] };
return getDataColumn(context, contentUri, selection,
selectionArgs);
// MediaStore (and general)
else if (&content&.equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
else if (&file&.equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
* @param context
The context.
* @param uri
The Uri to query.
* @param selection
(Optional) Filter used in the query.
* @param selectionArgs
(Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
public static String getDataColumn(Context context, Uri uri,
String selection, String[] selectionArgs) {
Cursor cursor =
final String column = &_data&;
final String[] projection = { column };
cursor = context.getContentResolver().query(uri, projection,
selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
} finally {
if (cursor != null)
cursor.close();
* @param uri
The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
public static boolean isExternalStorageDocument(Uri uri) {
return &com.android.externalstorage.documents&.equals(uri
.getAuthority());
* @param uri
The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
public static boolean isDownloadsDocument(Uri uri) {
return &com.android.providers.downloads.documents&.equals(uri
.getAuthority());
* @param uri
The Uri to check.
* @return Whether the Uri authority is MediaProvider.
public static boolean isMediaDocument(Uri uri) {
return &com.android.providers.media.documents&.equals(uri
.getAuthority());
* @param uri
The Uri to check.
* @return Whether the Uri authority is Google Photos.
public static boolean isGooglePhotosUri(Uri uri) {
return &com.google.android.apps.photos.content&.equals(uri
.getAuthority());
开发的时候需要把自己开发时候的包改为4.4.因为DocumentsContract 是在4.4包中独有的类。
4.3的开发包是找不到这个类的,会报红。
5.把网络图片转化为存在本地
&先将流转化为bitmap对象
map = BitmapFactory.decodeStream(inStream);
然后再讲bitmap存在本地。
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(file));
map .pressFormat.JPEG, 80, bos);
其中的file可以调用这个File(String dirPath, String name) ,目录加名字来构造
6.本地保存图片之后需要刷新相册
//相册扫描图片
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri uri = Uri.fromFile(new File(mFilePath));
intent.setData(uri);
sendBroadcast(intent);
7.需要判断第三方应用是否安装(代码在上)
如果未安装,跳转下载,调到浏览器下载
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(&下载网址&));
mContext.startActivity(intent);
如果安装了,直接打开app
PackageManager packageManager = mContext.getPackageManager();
Intent intent1 = packageManager.getLaunchIntentForPackage(&包名&);
mContext.startActivity(intent1);
Intent &intent = new Intent(Intent.ACTION_CALL, Uri.parse(&tel:&+ &电话号码&));
& & & &&startActivity(intent);
跳转到打电话的页面
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse(&tel:& + telephone));
context.startActivity(intent);
复制到剪切板
public static void copy2Clipboard(Context context,String msg){
ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
// 将文本内容放到系统剪贴板里。
ClipData clip = ClipData.newPlainText(&simple text&, msg);
// Set the clipboard's primary clip.
cm.setPrimaryClip(clip);
Toast.makeText(context, &复制成功&, Toast.LENGTH_LONG).show();
防止有EditText时候进入界面直接弹出输入框,不友好
可以在EditText前面放置一个看不到的LinearLayout
&LinearLayout
android:focusable=&true&
android:focusableInTouchMode=&true&
android:layout_width=&0px&
android:layout_height=&0px&/&或者在他的父布局中加入
android:focusable=&true&
android:focusableInTouchMode=&true&
关于WebView的使用用到的问题
1.加载完显示空白页:
@Override public boolean shouldOverrideUrlLoading(WebView view, String url) {
CommUtil.logD(TAG, &shouldOverrideUrlLoading, url:& + url);
返回false 就好了
2.webview 中加载的页面,如果需要弹出网页的对话框之类的,需要设置如下
mWebView.setWebChromeClient(new WebChromeClient(){
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
android 6.0的手机就算manifest文件中设置了存储权限,app运行时候还是需要手动申请,否则在app里面创建文件夹会失败。
需要 调用&showPremissDialogIfNeccessary 就好了
private static final int REQUEST_CODE_FOR_WRITE_PERMISSON = 1;
public void showPremissDialogIfNeccessary(){
//6.0 权限的手机需要打开权限
if(Build.VERSION.SDK_INT &= Build.VERSION_CODES.M){
int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
if(hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED){
requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE }, REQUEST_CODE_FOR_WRITE_PERMISSON);
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == REQUEST_CODE_FOR_WRITE_PERMISSON){
if(permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE) && grantResults[0] == PackageManager.PERMISSION_GRANTED){
//同意就好了
Toast.makeText(mContext, R.string.request_write_pression_failed, Toast.LENGTH_SHORT).show();
关于Listview 的divider的设置问题,如果数据item最后一个不需要现实divider的样式(分割线),需要将Listview 设置成wrap_content就好了(recyclerview不知道,有兴趣的朋友可以试一下)
由于使用了markDown编辑器,该博客不维护,请移步
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:39223次
排名:千里之外
原创:20篇
转载:15篇
评论:25条
(1)(1)(4)(1)(1)(1)(1)(1)(1)(1)(1)(3)(5)(2)(3)(2)(4)(1)(2)作为Android开发者,最头疼是什么?相信大家会异口同声的说Bug!的确,这些是避免不了的同时也是最烦人,调Bug,改Bug,一个项目做完,剩下的几乎全是Bug的调试,有的公司很可能在项目上线前还有改不完的Bug,针对这些困扰Android开发者的种种难题,整理出了这些难题或是Bug的解决方案,希望能给大家带来帮助。
Android其它相关难题
Nielsen最近的研究数据表明,Android系统的设备已经在数量上超过iOS系统了…Android 如何实现垃圾清理、缓存清理?要删除什么哪些文件或者要怎么做?这…作为Android开发程序员,我们经常会遇到项目中R文件的丢失问题,大部分情况…默认EditText在获得焦点时,会弹出软键盘,如果想实现当EditText获得焦点时…上次鄙人做了一个简单的利用webView实现的一个浏览器!其中遇到了两个问题…可能对于Android开发者来说,软引用这个词有的会不是很熟悉,软引用在Java…在adroid 应用程序中,我们经常会用到异步加载。 所以我们也要知道如何取消…想学习Android编程应该有一年时间了...一直没有真的去做,应该就在几天前吧…
你在Android开发过程中,遇到哪类问题最多?
多线程方面
屏幕的分辨率
网络通信相关
Socket管理
UI和IO方面
作为Android开发者,最头疼是什么?相信大家会异口同声的说Bug!的确,这些是避...
谷歌在新品发布会上正式发布了传闻已久的Android 4.3 Jelly Bean更新。从命名可...
作为Android开发者,我们都离不开Android模拟器来运行我们项目,当然,你可能会...
作为Android开发人员,在开发项目的过程中,我们往往会遇到各种各样的难题,针...Android开发经验谈-很少有人会告诉你的Android开发基本常识 - 简书
Android开发经验谈-很少有人会告诉你的Android开发基本常识
本文介绍Android开发过程中的一些基本常识,大多是一些流程、专业术语和解决问题的方法等。
软件开发流程
一个完整的软件开发流程离不开策划、交互、视觉、软件、测试、维护和运营这七个环节,这七个环节并不是孤立的,它们是开发一款成功产品的前提,但每一项也都可以形成一个学科,是一个独立的岗位,随着敏捷开发的流行,以及来到了体验为王的时代,现代软件开发更多的是注重效率和敏捷,而不是循规蹈矩的遵循这些开发流程,比如软件开发的岗位不再仅仅是个技术岗位,它需要去参与前期的设计和评审、可以在视觉和交互方面提出自己的见解,在开发的过程中需要自测程序尽快解决现存问题,运营和维护的过程中也需要软件的帮助。可见现代软件开发对开发者的综合素质(这并不是facebook所讲的全栈工程师)越来越高,自称为码农或者程序猿显然是不合理的,因为这个过程是脑力劳动和体力脑动并存,称呼自己为工程师显得更为合理。
策划:需求收集(通过用户调研、灰度发布、大数据分析、竞品分析、领导拍脑袋等方式获取需求)、需求整理(将需求归类、划分优先级等)、将需求转换成解决方案(输出设计文档);
交互:从心理学(利用人性的弱点)、人性化(心智)、个性化的角度将解决方案转换成可交互的功能和界面(需要输出交互文档),比如加载等待、消息提示、页面布局、页面内和页面间的交互逻辑、页面切换动画等等,这个过程中一般会使用或者来制作交互文档;
视觉:根据交互图,使用来做视觉效果,在Android上的图片格式大多是png和jpg,对于需要屏幕适配,程序又适合做屏幕适配的地方可以使用九图,格式为。
软件:根据视觉和交互效果将需求转化为具体的实现,在实现的过程中可能会因为需求、交互或者视觉的变动导致软件实现的变动,因为策划、交互、视觉这每一个环节都可能会有信息失真的现象,或者是由于市场环境的变化、获取信息不够准确、领导拍脑袋等等情况导致软件始终处于被动状态,所以现在会提倡、、、、来提高程序的灵活性和稳定性;
测试:软件达到可交互的标准后,需要将可交互的程序提供测试,其中灰度发布(用户测试)、自测(开发自测)、SQA(品质保证)都算是测试;
维护和运营:通过测试程序达到稳定标准后,软件就可以上线了,软件上线后,需要去维护,用户反馈的问题要及时解决、用户有疑问要及时解答;根据后台统计信息、抓住可运营的节日、民族文化需要做运营来提高用户使用产品的粘度,让更多的用户知道、使用产品都是运营应该做的。
可以查看这个答案了解一个APP从创意到上线的具体流程,
可以查看笔戈科技的这篇文章了解一个手机(平板或其它电子产品也差不多)的诞生需要哪些环节,
提问的智慧
大多数工作都是以结果为导向的,特别是软件开发这个职业,绩效考核、KPI这些都是在考核你工作的成果,所以工作更多地是需要你解决问题的能力,至于学习这个事情,还是在工作之外的时间去做吧。对于提高解决问题能力我有两个建议:
学会学习和思考:学习的过程中要广度和深度并存,Android应用开发本身对技术功底的要求不高(因为很多底层的东西都被google、框架、开源代码给封装起来了,多数时候你只需要看ReadMe或者API知道怎么用就可以了),更多地是在你遇到问题的时候知道这个问题能够通过什么方法和方式来解决。书要看,但多逛逛论坛、QQ群、Github、StackOverflow、CSDN博客专栏对自己都是有益的。
学会提问:你身边有很多资源,比如同事、StackOverflow、QQ技术交流群、搜索引擎,当你遇到问题的时候完全可以利用身边的资源来解决遇到的问题,如果一个问题在一个小时之内自己都不能够解决它,我就会通过搜索引擎、Github、QQ技术交流群、同事、StackOverflow(以上排序是按优先级排列的)来解决它。如果你需要好的答案你就需要有好的提问,特别是在QQ群或者论坛,在提问的过程中需要体现出你的思考,能够通过搜索引擎解决的问题坚决不问他人,这是对别人的尊重,在这里推荐几个链接,认真看会对你有莫大的帮助:
解决bug的方法
为了写这一项我专门在知乎上提过一个问题:
在知道如何快速解决bug之前,你需要知道什么是bug。没有完成策划、交互、视觉要求的功能,这不叫bug,这叫功能缺陷;一个功能完成后不能正常使用也不叫bug,因为它根本还没达到可测试的标准。我认为当你的程序达到可测试标准之后发现的问题才叫bug。综合我自己解决bug的经验和知乎上的回答,总结常见的解决bug的方法有(你想要高效解决bug的前提是你能够快速定位到缺陷所在的位置,所以以下方法多数讲的是如何快速定位问题,至于真正解决bug,需要你自己修改程序才行):
断点调试:
以Eclipse为例:
1、打断点:
(1)打断点:
(2)清除断点:
2、启动调试模式的两种方式:
(1)通过debug as启动调试程序:右键工程名--&Debug AS --&Android Application --&模拟器或者真机会弹出......watching for the debugger......的提示框,不要点击等待其自动消失 --& 此时已经进入调试模式,操作程序到达打断点的地方。
(2)在程序运行过程中,在DDMS视图下选中要调试的程序,启动调试模式:
DDMS视图进入调试模式
3、调试:请自行尝试F5、F6、F7、F8这几个调试的快捷键;
4、watch成员变量:在调试的过程中,比如在执行for、while、do while循环、递归、系统回调等程序时可以通过watch来观察成员变量或者方法返回值的变化情况,watch的方法:
注:更多关于在Eclipse IDE中调试Android程序的知识请参见:
打印调试的方法对于循环、异步加载、递归、JNI等代码段非常有用,特别是在循环中,在循环次数非常大时,通过打断点调试显然是一件费力的事情,这时候打印就显得更“智能”了,我通常会通过下面封装的打印调试类来输出打印信息,这个类可以打印print、log、行号、文件名、StrictMode等信息,当不需要打印信息时,只需要将DEBUG_MODE改为false就可以了:
import android.content.C
import android.os.StrictM
import android.util.L
import android.widget.T
* 调试打印类
public class DebugUtils{
private DebugUtils( ){
public static void println( String printInfo ){
if( Debug.DEBUG_MODE && null != printInfo ){
System.out.println( printInfo );
public static void print( String printInfo ){
if( Debug.DEBUG_MODE && null != printInfo ){
System.out.print( printInfo );
public static void printLogI( String logInfo ){
printLogI( TAG, logInfo );
public static void printLogI( String tag, String logInfo ){
if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
Log.i( tag, logInfo );
public static void printLogE( String logInfo ){
printLogE( TAG, logInfo );
public static void printLogE( String tag, String logInfo ){
if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
Log.e( tag, logInfo );
public static void printLogW( String logInfo ){
printLogW( TAG, logInfo );
public static void printLogW( String tag, String logInfo ){
if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
Log.w( tag, logInfo );
public static void printLogD( String logInfo ){
printLogD( TAG, logInfo );
public static void printLogD( String tag, String logInfo ){
if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
Log.d( tag, logInfo );
public static void printLogV( String logInfo ){
printLogV( TAG, logInfo );
public static void printLogV( String tag, String logInfo ){
if( Debug.DEBUG_MODE && null != tag || null != logInfo ){
Log.v( tag, logInfo );
public static void printLogWtf( String logInfo ){
printLogWtf( TAG, logInfo );
public static void printLogWtf( String tag, String logInfo ){
if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
Log.wtf( tag, logInfo );
public static void showToast( Context context, String toastInfo ){
if( null != context && null != toastInfo ){
Toast.makeText( context, toastInfo, Toast.LENGTH_LONG ).show( );
public static void showToast( Context context, String toastInfo, int timeLen ){
if( null != context && null != toastInfo && ( timeLen & 0 ) ){
Toast.makeText( context, toastInfo, timeLen ).show( );
public static void printBaseInfo( ){
if( Debug.DEBUG_MODE ){
StringBuffer strBuffer = new StringBuffer( );
StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
strBuffer.append( "; class:" ).append( stackTrace[ 1 ].getClassName( ) )
.append( "; method:" ).append( stackTrace[ 1 ].getMethodName( ) )
.append( "; number:" ).append( stackTrace[ 1 ].getLineNumber( ) )
.append( "; fileName:" ).append( stackTrace[ 1 ].getFileName( ) );
println( strBuffer.toString( ) );
public static void printFileNameAndLinerNumber( ){
if( Debug.DEBUG_MODE ){
StringBuffer strBuffer = new StringBuffer( );
StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
strBuffer.append( "; fileName:" ).append( stackTrace[ 1 ].getFileName( ) )
.append( "; number:" ).append( stackTrace[ 1 ].getLineNumber( ) );
println( strBuffer.toString( ) );
public static int printLineNumber( ){
if( Debug.DEBUG_MODE ){
StringBuffer strBuffer = new StringBuffer( );
StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
strBuffer.append( "; number:" ).append( stackTrace[ 1 ].getLineNumber( ) );
println( strBuffer.toString( ) );
return stackTrace[ 1 ].getLineNumber( );
public static void printMethod( ){
if( Debug.DEBUG_MODE ){
StringBuffer strBuffer = new StringBuffer( );
StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
strBuffer.append( "; number:" ).append( stackTrace[ 1 ].getMethodName( ) );
println( strBuffer.toString( ) );
public static void printFileNameAndLinerNumber( String printInfo ){
if( null == printInfo || !Debug.DEBUG_MODE ){
StringBuffer strBuffer = new StringBuffer( );
StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
strBuffer.append( "; fileName:" ).append( stackTrace[ 1 ].getFileName( ) )
.append( "; number:" ).append( stackTrace[ 1 ].getLineNumber( ) ).append( "\n" )
.append( ( null != printInfo ) ? printInfo : "" );
println( strBuffer.toString( ) );
public static void showStrictMode( ) {
if (DebugUtils.Debug.DEBUG_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects().detectLeakedClosableObjects().penaltyLog().penaltyDeath().build());
public static void d(String tag, String msg){
if(DebugUtils.Debug.DEBUG_MODE){
Log.d(tag, msg);
public class Debug{
public static final boolean DEBUG_MODE =
public static final String TAG = "Debug";
这适合于code review,但是不太靠谱,因为人的精力毕竟有限,有时候你多敲一个分号,缩进不对都有可能导致程序出现问题,但在代码量较少时是一个高效率的方法。
自动化测试:
Android的自动化测试(分白盒测试和黑盒测试)工具有:monkey、Robotium、Appium、云端测试(比如testin),具体用法可参见:
调试、打印、目视这三种方法适合于可以复现的问题,对于随机问题(实际上不存在随机问题,只是问题不那么容易复现而已),比如在线程、音频播放、AnsynTask、Timer切换或者结束时刚好做了相应地人为操作导致出现灵异现象。这时候可以通过排除法来排查问题,具体的方法是首先大概定位到出现问题的位置,然后将代码一段一段地注释,观察程序现象,逐步缩小出现问题的范围。
版本管理介绍
在较大的软件开发过程中,可能有多个软件工程师同时开发一个项目的情况,比如有负责读取数据、获取网络数据等API封装的,有负责程序架构的,有负责上层界面实现的,为了能够最终编译一个完成的程序出来,需要将代码整合,这个时候最方便的方法就是使用版本管理工具,固定时间上传(比如每天、没改动一个功能等等),这样能够实时保证服务器上的代码是最完整、最新的,也可以避免由于自然灾害、电脑异常导致本地电脑挂掉损失掉代码的问题。
常见的版本管理工具有SVN和Git,我也使用过CVS,关于版本管理工具的介绍参见:
注:对于windows用户来说,建议使用乌龟壳系列的版本控制客户端,使用github的朋友可以使用github for windows客户端:
通常我们用Eclipse或者Android Studio开发android程序时,只需要运行程序就可以在模拟器或者机器上运行程序了,但为了保证代码的完整性、能够在服务器上编译,需要通过编译工具将代码编译成apk,常见的编译工具有:、,但这两种编译工具都是需要通过手动敲命令来完成编译功能(当然你也可以自己写脚本来实现编译自动化),是一个持续集成的工具,通过它可以代码克隆、编译以及程序加密自动化,其实它也是通过批处理来实现的,ant、gradle和jenkins的具体用法自行谷歌,使用起来很简单,目前android studio和github上很多功能都是通过gradle来编译的。
专业术语介绍
以下解释完全是本人的理解,详细解释可自行谷歌。
版本迭代:按照需求优先级,在保证基本功能OK后持续开发和升级,这样能够降低软件开发的风险,并且能够及时解决用户反馈的问题,船小好掉头嘛;
敏捷开发:小步快跑,大概意思就是不要过于注重文档,要注重当面交流,能够在实现时高保真的还原用户的需求场景,并且能够快速地解决用户的需求。
单元测试:白盒测试的一种,对核心方法通过写程序来测试自己的程序,单元测试的目的是让你有意识地降低程序间的耦合,保证每一个方法都是最小单元,但这对于测试程序逻辑是没有帮助,这是我自己的理解。。。
灰度发布:先找一部分用户来使用即将发布的程序(这部分用户可以是随机抽取、制定年龄段、指定地区或者通过某种方式知道他是活跃用户),在测试的过程中给与用户一点好处让用户写用户体验报告、反馈问题等方式来发现程序存在的问题和缺陷;
DA统计:也叫后台统计,通过在程序中埋点的方式,在有网络的情况下将用户的操作行为和数据上传到后台,将每个用户的信息都上传回来就叫大数据,通过建模对这些数据分析就叫大数据分析。
开放平台:比如分享到QQ空间、分享到微信、讯飞语音、友盟的后台统计、天气、地图等等都叫做开放平台,它提供了一些开放的接口给开发者,方便开发者使用它的服务,开放平台多数服务都是免费的,但有时候也可能不稳定,比如用的人少它自然就活不下去了,然后就没有然后了。
同行评审:你的同行和你一起看看你的代码,发现是否有问题;
结对编程:在写代码的过程中,有个人坐在你旁边或者你坐在别人旁边,编写边讨论,降低程序出现逻辑和低级错误的概率。
Android开发资源
参见我的另一篇文章:
尽量阅读,这才是原汁原味、不失真的开发指导;
即使你认为设计程序是浪费时间,你只是喜欢写程序,至少你也得用思维导图理清思路,思维导图对于帮助你理解设计文档、理清思路有很大的帮助;
不要用Intent传递大量的数据,这有可能导致ANR或者报异常;
在退出页面后,系统不一定会及时执行onDestory方法,如果你在onDestory方法里做关闭文件、释放内存的操作可能出现退出程序又立即进入时,由于需要重新初始化这些信息导致代码重入的异常;
在改动JNI后,运行程序之前记得卸载掉已经安装在模拟器或者真机上的该程序,如果直接运行,android不会load最新编译的so,也就不能立即看到修改后的效果;
代码至少每天备份一次,或者是完善一个功能就备份一次,不要堆积之后一次性备份,因为在你的代码出问题需要回溯代码时你需要从服务器上重新取代码,同时也可以避免代码不是最新导致最后和其他人合并时不知道改了哪些地方;
将打印信息封装成一个方法,用一个标志位控制这个这个方法的方法体是否需要执行,这样在由debug版释放到release版本时,不需要傻傻地一行一行地去掉代码,你只需要改变标志位的值就可以了;
对于有返回值的JNI函数,即使你不返回任何值,用NDK编译JNI的时候也不会报错,所以在写JNI代码的时候,一定要仔细检查代码;
JNI频繁读写文件操作会影响程序的运行性能,可以考虑一次性在内存中申请一块大内存作为缓存空间,用这种空间换时间的方式可以大大提高程序的运行效率;
不要指望类的finalize方法去处理需要回收和销毁的工作,因为finalize是系统回调的方法,调用时机不可预见,切记;
使用文件流、Cursor时,使用结束后记得一定要关闭,否则可能导致内存泄漏,严重的情况可能引发程序崩溃;
优先使用Google搜索引擎(少用百度),如果不能正常使用Google搜索引擎建议通过代理、VPN、修改文件等方式搭建梯子。这里提供一个免费的
对于不需要使用硬件加速的activity(没有动画效果、视频播放以及各种多媒体文件的操作都可以关掉硬件加速),在AndroidManifest.xml文件中通过“android:hardwareAccelerated="false"”关掉硬件加速可节省应用内存;
对于需要横竖屏转换的应用,又不想在横竖屏切换的时候重新跑onCreate方法,可以在AndroidManifest.xml文件中对应的Activity标签下调用“android:configChanges="screenSize|orientation"”;
为了减轻应用程序主进程的内存压力,对于耗内存比较多的界面(比如视频播放界面、flash播放界面等),可以在AndroidManifest.xml文件中对应的Activity标签下调用“android:process=".processname"”单开一个进程,但在退出这个界面的时候一定要在该界面的onDestory方法中调用System的kill方法来杀掉该进程;
在res/values/arrays.xml文件中定义的单个数组的元素个数不宜过大,过大会导致加载数据时非常慢,有时候你需要使用数组资源时数据有可能还没加载完成;
一个Activity中最耗费内存的是activity的背景(多数情况如此,特别是对于分辨率很大的机器,一个界面的背景算下来都需要好几兆内存),所以在程序界面较多时,可以考虑将图片转换成静态的drawable,然后多个activity共用这一张背景图;
可以通过为application、activity自定义主题的方式来关掉多点触摸功能,只需要在自定义的主题下添加这两个标签:
&item name="android:windowEnableSplitTouch"&false&/item&
&item name="android:splitMotionEvents"&false&/item&
很多游戏进入时,播放的片头动画多数是一个视频文件;
Android单个dex文件的方法数不能超过65536个,
使用模拟器代替android自带模拟器(它需要虚拟机vituralbox的支持,不过官网已经提供了一个集成虚拟机的安装包了,直接下载下来安装即可),可以大大提高使用模拟器的体验(流畅、快),它也可以以插件的形式集成在Eclipse中,
给Application或者activity设置自定义主题时,最好不要设置为全透明,否则在activity按Home键回退到桌面的时候效果很渣;
如果你需要取消toast显示的功能,在一个类中你只需要实例化该类一次(也就是说将Toast定义成一个全局的成员变量),这样你就可以调用mToast.cancel()了,我把它写成了一个静态类:
public class ToastUtils {
private ToastUtils( ){
public static void showToast( Context context, String toast ){
if( null == mToast ){
mToast = Toast.makeText( context, toast, Toast.LENGTH_LONG );
mToast.setText( toast );
mToast.show( );
public static void cancel( ){
if( null != mToast ){
mToast.cancel( );
public static Toast mToast =
你可以定义一个静态类来实现防止按钮被重复点击导致重复执行一段代码的问题:
* 按钮重复点击
public class BtnClickUtils {
private BtnClickUtils( ){
public static boolean isFastDoubleClick() {
long time = System.currentTimeMillis();
long timeD = time - mLastClickT
if ( 0 & timeD && timeD & 1000) {
mLastClickTime =
private static long mLastClickTime = 0;
放在apk的assets或者raw目录下的数据文件最好做加密处理,在需要使用的时候才解密,这样可以避免在apk被他人破解时数据也被破解的问题;
最好不要再activity的onCreate方法里面调用popupwindow的show方法,有可能由于activity没有完全初始化导致程序异常(android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid),如果非要在一进activity就显示popupwindow,建议用handler.post、View.postDelay来处理;
对于自定义View,在构造方法里面是获取不到视图的宽高的(此时获取长宽都为0),需要在onMeasure方法中或者跑了onMeasure方法后才能够获取到视图的宽高,不过你可以通过在构造方法里面强制测量视图的宽高来实现在构造方法里获取视图的宽高信息,具体见
如果你觉得在安装Eclipse后还需要配置android开发环境很麻烦,你可以直接使用ADT Bundle,它是一个懒人套餐,下载下来就可以用了,可以在下载。
有时间看看、、,可以学习到一些解决问题、做大项目的经验。
当应用中动画比较多,并且动画都是通过图片来切换的时候,可以考虑借用Cocos的思想,这样就可以避免图片命名的烦恼。
代码对比:
屏幕取色:
思维导图:
在线工具:
Android应用开发第三方解决方案
下图为Android应用开发第三方解决方案汇总,有些可以借助第三方平台搞定的就尽量不要自己搞,一是可以节省成本,二是你没人家专业,原文链接:
第三方解决方案
博客:http://zmywly8866.github.io/
Github:/zmywly8866
知乎:/people/zhang-ming-yun-97}

我要回帖

更多关于 安卓开发过程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信