Android—开发过程中的相关注意事项
查看测试机显示的内容所对应的Activity
先在环境变量Path中添加sdk所在的路径。
adb shell dumpsys activity 包名 | findstr ACTIVITY
App打包apk安装后重复启动根界面的问题
设我们应用启动页面会有一个SplashActivity启动界面,经过3s跳转到主界面MainActivity。
当我们点击apk文件安装app,安装完成界面点击打开按钮,点击Home键,进入系统桌面,此时app退到后台,再点击桌面上启动图标时。
在bug情况下启动app时,SplashActivity(app的根Activity)再次创建并叠加到Task任务栈上了。
解决方法:
在app的SplashActivity(app的根Activity)的onCreate方法中通过isTaskRoot()方法来判断是否是任务栈中的根Activity,如果是就不做任何处理,如果不是则直接finish掉;
PackageManager类
说明: 获得已安装的应用程序信息 。可以通过getPackageManager()方法获得。
常用方法:
public abstract PackageManager getPackageManager()
功能:获得一个PackageManger对象
public abstract Drawable getApplicationIcon(String packageName)
参数: packageName 包名
功能:返回给定包名的图标,否则返回null
public abstract ApplicationInfo getApplicationInfo(String packageName, int flags)
参数:
packagename 包名
flags 该ApplicationInfo是此flags标记,通常可以直接赋予常数0即可
功能:返回该ApplicationInfo对象
public abstract List<ApplicationInfo> getInstalledApplications(int flags)
参数:
flag为一般为GET_UNINSTALLED_PACKAGES,那么此时会返回所有ApplicationInfo。我们可以对ApplicationInfo
的flags过滤,得到我们需要的。
功能:返回给定条件的所有PackageInfo
public abstract List<PackageInfo> getInstalledPackages(int flags)
参数如上
功能:返回给定条件的所有PackageInfo
public abstract ResolveInfo resolveActivity(Intent intent, int flags)
参数:
intent 查寻条件,Activity所配置的action和category
flags: MATCH_DEFAULT_ONLY :Category必须带有CATEGORY_DEFAULT的Activity,才匹配
GET_INTENT_FILTERS :匹配Intent条件即可
GET_RESOLVED_FILTER :匹配Intent条件即可
功能 :返回给定条件的ResolveInfo对象(本质上是Activity)
public abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags)
参数同上
功能 :返回给定条件的所有ResolveInfo对象(本质上是Activity),集合对象
public abstract ResolveInfo resolveService(Intent intent, int flags)
参数同上
功能 :返回给定条件的ResolveInfo对象(本质上是Service)
public abstract List<ResolveInfo> queryIntentServices(Intent intent, int flags)
参数同上
功能 :返回给定条件的所有ResolveInfo对象(本质上是Service),集合对象
利用应用程序打开目标类型文件
播放视频
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file:///sdcard/media.mp4");
intent.setDataAndType(uri, "video/*");
startActivity(intent);
预防App调用第三方应用程序出现Crash处理方式
开发中我们经常会用到第三方应用程序的Activity和Service,但是你无法保证用户设备上安装了特定的某个应用软件,或者设备上有能够处理你的Intent请求的程序。
所以我们一般在调用前确定下是否可以将三方启动的界面解析为一个Activity,并判断它能否启动该intent
public void viewUrl(String url, String mimeType) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(url), mimeType);
if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
startActivity(intent);
}else {
// 找不到指定的Activity
}
}
广播安全
对于只用于应用内的广播,优先使用LocalBroadcastManager 来进行注册和发送,LocalBroadcastManager 安全性更好,同时拥有更高的运行效率。
对于使用Context#sendBroadcast()等方法发送全局广播的代码进行提示。如果该广播仅用于应用内,则可以使用LocalBroadcastManager 来避免广播泄漏以及广播被拦截等安全问题,同时相对全局广播本地广播的更高效。
registerReceiver()和unregisterReceiver()要成对出现,onResume内注册广播则应该在onPause注销,onResume和onPause是成对的,如果在onDestory会出现多次注册一次注销的情况。
OnPause
当前Activity 的onPause 方法执行结束后才会创建(onCreate)或恢复(onRestart)别的Activity,所以在onPause 方法中不适合做耗时较长的工作,这会影响到页面之间的跳转效率。
Toast
使用 Toast 时,建议定义一个全局的 Toast 对象,这样可以避免连续显示 Toast 时不能取消上一次 Toast 消息的情况(如果你有连续弹出 Toast 的情况,避免使用 Toast.makeText)。
ViewHolder
使用 Adapter 的时候,如果你使用了 ViewHolder 做缓存,在 getView() 的方法中无论这项 convertView 的每个子控件是否需要设置属性(比如某个 TextView 设置的文本可能为 null,某个按钮的背景色为透明,某控件的颜色为透明等),都需要为其显式设置属性(TextView 的文本为空也需要设置 setText(“”),背景透明也需要设置),否则在滑动的过程中,因为 adapter item 复用的原因,会出现内容的显示错乱。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder myViews;
if (convertView == null) {
myViews = new ViewHolder();
convertView = mInflater.inflate(R.layout.list_item, null);
myViews.mUsername = (TextView) convertView.findViewById(R.id.username);
convertView.setTag(myViews);
} else {
myViews = (ViewHolder) convertView.getTag();
}
Info p = infoList.get(position);
String dn = p.getDisplayName;
myViews.mUsername.setText(StringUtils.isEmpty(dn) ? "" : dn);
return convertView;
}
static class ViewHolder {
private TextView mUsername;
}
Dialog
在 Activity 中显示对话框或者弹出浮层时,尽量使用 DialogFragment,而非 Dialog/AlertDialog,这样便于随 Activity 生命周期管理对话框/弹出浮层的生命周期。
public void showPromptDialog(String text) {
DialogFragment promptDialog = new DialogFragment() {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
View view = inflater.inflate(R.layout.fragment_prompt, container);
return view;
}
};
promptDialog.show(getFragmentManager(), text);
}
SQLite
多线程操作写入数据库时,需要使用事务,以避免出现同步问题。
public void insertUserPhoto(SQLiteDatabase db, String userId, String content) {
ContentValues cv = new ContentValues();
cv.put("userId", userId);
cv.put("content", content);
db.beginTransaction();
try {
db.insert(TUserPhoto, null, cv); // 其他操作
db.setTransactionSuccessful();
} catch (Exception e) {
// TODO
} finally {
db.endTransaction();
}
}
执行 SQL 语句时,应使用 SQLiteDatabase#insert()、update()、delete(),不要使用 SQLiteDatabase#execSQL(),以避免 SQL 注入风险。
public int updateUserPhoto(SQLiteDatabase db, String userId, String content) {
ContentValues cv = new ContentValues();
cv.put("content", content);
String[] args = {String.valueOf(userId)};
return db.update(TUserPhoto, cv, "userId=?", args);
}
如果 ContentProvider 管理的数据存储在 SQL 数据库中,应该避免将不受信任的外部数据直接拼接在原始 SQL 语句中,可使用一个用于将 ?作为可替换参数的选择子句以及一个单独的选择参数数组,会避免 SQL 注入。
String mSelectionClause = "var = ?";
String[] selectionArgs = {""};
selectionArgs[0] = mUserInput;
Bitmap
加载大图片或者一次性加载多张图片,应该在异步线程中进行。图片的加载,涉及到 IO 操作,以及 CPU 密集操作,很可能引起卡顿。
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
// 在后台进行图片解码
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap = BitmapFactory.decodeFile("some path");
return bitmap;
}
...
}
在 ListView,ViewPager,RecyclerView,GridView 等组件中使用图片时,应做好图片的缓存,避免始终持有图片导致内存泄漏,也避免重复创建图片,引起性能问题。建议使用 Fresco、Glide等图片库。
png 图片使用 tinypng 或者类似工具压缩处理,减少包体积。
Animation
在 Activity.onPause() 或 Activity.onStop() 回调中,关闭当前 activity 正在执行的动画。
public class MyActivity extends Activity {
ImageView mImageView;
Animation mAnimation;
Button mBtn;
/**
* 首次创建 activity 时调用
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mImageView = (ImageView) findViewById(R.id.ImageView01);
mAnimation = AnimationUtils.loadAnimation(this, R.anim.anim);
mBtn = (Button) findViewById(R.id.Button01);
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mImageView.startAnimation(mAnimation);
}
});
}
public void onPause() {
//页面退出,及时清理动画资源
mImageView.clearAnimation();
}
}
在动画或者其他异步任务结束时,应该考虑回调时刻的环境是否还支持业务处理。例如 Activity 的 onStop() 函数已经执行,且在该函数中制动释放了资源,此时回调中如果不做判断就会空指针崩溃。
package com.niles.ndkdemo;
public class MyActivity extends Activity {
private ImageView mImageView;
private Animation mAnimation;
/**
* 首次创建 activity 时调用
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mImageView = (ImageView) findViewById(R.id.ImageView01);
mAnimation = AnimationUtils.loadAnimation(this, R.anim.anim);
mAnimation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationEnd(Animation arg0) {
//判断一下资源是否被释放了
if (mImageView != null) {
mImageView.clearAnimation();
}
}
});
mImageView.startAnimation(mAnimation);
}
}
大图片资源不要直接打包到 apk,可以参考通过文件仓库远程下载,减小包体积。
PendingIntent
使用 PendingIntent 时,禁止使用空 Intent,同时禁止使用隐式 Intent。
本文地址:https://blog.csdn.net/weixin_41939525/article/details/110447297
上一篇: system函数
推荐阅读
-
请问 IOS 或 Android 开发中有向网页那样的前端框架,后端框架吗?
-
Android NDK 开发Jni 遇到Fatal 崩溃错误后,怎么定位crash的位置
-
php使用Smarty的相关注意事项及访问变量的几种方式_php模板
-
基于“玩android”开放API开发的开源微信小程序项目
-
Android开发之使用ExifInterface获取拍照后的图片属性
-
Android开发教程之shape和selector的结合使用
-
Android版的股票行情K线图开发
-
Android开发新手必须知道的10大严重错误
-
Android开发之将两张图片合并为一张图片的方法
-
Android开发笔记之简单基站定位程序的实现